Files
baya-monorepo/archive/docs/integration/domains/bnpl.md
T
2026-08-02 20:01:31 +03:30

5.5 KiB
Raw Blame History

bnpl — provider-financed installments

Client seam client/src/services/bnpl/ · USE_BNPL_MOCK = true (mock is primary) · 9 server ops Last verified: 2026-07-30 against commit d3ec723 and swagger.v1.json (2026-07-29).

The second checkout rail. Balinyaar does not finance anything — a provider (SnappPay, Digipay, …) pays the platform net of its commission and carries the customer's installments itself. Card checkout is payment.md.

Endpoints

Method Path Auth Verdict
POST /api/v1/checkout_bnpl/eligibility [Authorize] · sensitive wired
POST /api/v1/checkout_bnpl/initiate [Authorize] · sensitive wired · Idempotency-Key
GET /api/v1/checkout_bnpl/{id} [Authorize] · sensitive wired
GET /api/v1/checkout_bnpl/by_request/{bookingRequestId} [Authorize] · sensitive wired
POST /api/v1/webhooks_bnpl/{provider} anonymous · webhook 120/min server-only — the provider calls it
GET /api/v1/admin_bnpl/{id} admin · sensitive unwired — no console screen
POST /api/v1/admin_bnpl/{id}/verify admin · sensitive unwired
POST /api/v1/admin_bnpl/{id}/settle admin · sensitive unwired
POST /api/v1/admin_bnpl/{id}/revert admin · sensitive unwired — the reversal is driven from refunds.md instead

Phantom — 3

All three are REQ-022's deferred half. Written real-shaped so the swap is one line.

Client call REQ Note
GET /api/v1/checkout_bnpl/options/{bookingRequestId} REQ-022 The D1/D2 provider + plan list, per-plan monthly / down-payment / total
GET /api/v1/checkout_bnpl/schedule/{id} REQ-022 The D4 repayment schedule
GET /api/v1/checkout_bnpl/wallet_installments REQ-024 The D5 wallet installment list

REQ-022 was partially delivered: balinyaar was added to the provider_code enum; options and schedule were deferred. BnplEligibilityDto carries a single planSummary + installmentCount, not a list of plans — which is exactly why the client needs options.

The money shape

Two facts that make BNPL different from card, and both are easy to get wrong:

  1. The card payment is recorded net of the provider's fee. The provider deducts its commission before remitting, so the platform receives orderAmount bnplCommission. settledAmountIrr and bnplCommissionIrr are both on BnplOrderStatusDto, and the handler reads the actual deducted amount from the settlement response — never a rate from config. Seams:Bnpl:CommissionRate tunes the mock only.
  2. Settlement is not necessarily instant. settledAt is nullable, modelling the deferred / T+13 / weekly reality. A null settledAt on a settled order is normal, not an inconsistency.

BnplStatus is forward-only. A reversal is reverted, with revertTransactionId, revertedAmountIrr, revertedAt and — when the provider returns it — providerCommissionReversedAmount, which the reconciliation needs and which most providers do not send. See refunds.md for the bnpl_revert refund channel.

Shape rules the JSON does not express

  • Eligibility accepts the credit-check inputs { nationalId, mobile, consent } (REQ-023, delivered). Consent is required when the KYC inquiry runs — it is a legal precondition, not a checkbox.
  • eligibilityStatus distinguishes three outcomes, and the third is not a failure: not_eligible (provider declined) vs ceiling_exceeded (order above creditCeilingIrr — offer card instead) vs eligible. The UI must fall back to card, not show an error, on either negative.
  • bookingId is on the settled order (REQ-024, confirmed) so the wallet can link an installment plan to its booking.
  • D5 installment status is provider-reported, not ledger-derived. The platform does not track the customer's repayment; whatever the provider says is the truth. Never compute an installment state from Balinyaar's own ledger.
  • currency is on the wire and matters. Seams:Bnpl:WireCurrency is IRR by default; SnappPay and Digipay speak Rial. Conversion happens only inside the adapter via ICurrencyNormalizer.
  • Provider credentials proper live in the encrypted payment_gateways.config_json; only non-secret connection facts (base URL, sandbox flag, merchant handle) come from Seams:Bnpl:Providers.
  • Seams:Bnpl:NotEligibleMobile (09120000099) is the designated test mobile that returns not_eligible, so the fall-back-to-card path is testable.

Enums

Vocabulary Values
BnplStatus eligible token_issued verified settled reverted cancelled failed
BnplEligibilityStatus eligible not_eligible ceiling_exceeded
ProviderCode snapppay digipay tara torobpay balinyaar
BnplInstallmentStatus (D5, provider-reported) paid due_soon upcoming overdue
BnplHandoffOutcome (client, from the return URL) success failure

The first three are verified identical to Entities/Bnpl/BnplStatus.cs, BnplEligibilityStatus.cs and BnplProviderCodes.cs. Note snapppay has three ps.

Open REQs

REQ Status Effect
REQ-022 partially delivered options and schedule deferred → 3 phantom routes. D1/D2/D4 are mock-only
REQ-024 partially delivered bookingId confirmed present; the wallet installment list is deferred