Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

BNPL

Who it's for: Shoppers who want to spread the cost of a purchase over time, and merchants who want to offer installment plans without managing credit risk themselves. Why it matters: BNPL brings structured installment credit to African markets where consumer finance is scarce — purchases are paid in predictable monthly steps drawn directly from a lending pool.

Buy Now Pay Later lets a borrower purchase goods from a merchant and repay in scheduled installments drawn from a lending pool.

POST /bnpl/create

Request
{
  "userAddressId": "wallet-uuid",
  "userAddress": "0xabc...",
  "poolId": "1",
  "merchant": "0xmerchant...",
  "totalAmount": "120000000",
  "numInstallments": 6,
  "installmentIntervalSeconds": 2592000
}
FieldConstraints
numInstallments1–36
installmentIntervalSeconds≥ 86400 (1 day minimum)

Transaction batch: [approve(lendingDiamond, totalAmount), createPlan(...)]

Response: { id, status: 'pending' }


POST /bnpl/:planId/pay-installment

Pays the next unpaid installment. The service reads the due amount on-chain (including any late fee) before submitting.

Request
{ "userAddressId": "...", "userAddress": "0x..." }

The service:

  1. Reads getInstallment(planId, nextIndex) to get due amount
  2. Checks if overdue → adds 2% late fee
  3. Transaction batch: [approve(lendingDiamond, amount + lateFee), payInstallment(planId)]

POST /bnpl/:planId/mark-late/:installmentIndex

Called by the keeper when an installment is overdue.

{ "callerAddressId": "...", "callerAddress": "0x..." }

Read Endpoints

GET /bnpl/:planId                          — plan summary + D1 tracking
GET /bnpl/:planId/installment/:index       — { dueDate, amount, status }
GET /bnpl/borrower/:address                — all plans for an address
Plan summary shape
{
  "planId": "3",
  "borrower": "0x...",
  "merchant": "0x...",
  "totalAmount": "120000000",
  "amountPaid": "40000000",
  "numInstallments": 6,
  "installmentsPaid": 2,
  "active": true
}