Developer docs

Hosted Checkout

After you create a deposit or withdrawal, redirect the customer to checkout_url. PeerPay renders a merchant-branded page with "Powered by PeerPay" secondary branding. You do not need to build first-version payment UI.

Checkout URLs

  • Deposits: https://checkout.peerpayment.org/c/{public_token}
  • Withdrawals: https://checkout.peerpayment.org/w/{public_token}

Checkout links expire with the order (60 minutes). Expired links reject reference submission / confirmation. A withdrawal stays created until the customer confirms the destination on /w/{public_token}; that confirm is what starts agent assignment. Workspaces with Auto-Confirm Withdrawals on skip this page: the create call confirms the destination and returns no checkout_url.

An expired deposit link remains invalid. A workspace owner or administrator may reopen the expired deposit from the dashboard, which issues a new URL and deadline; it never reactivates the old token.

Return Navigation

  • Deposit checkout redirects to the supplied URL after successful checkout without adding PeerPay status parameters.
  • Withdrawal checkout redirects after destination confirmation—not payout success—and adds peerpay_withdrawal_id, merchant_withdrawal_id, and checkout_status=customer_confirmed.
  • Both redirects are browser navigation only. Use authenticated resources and signed webhooks for wallet decisions.

What customers see

For a deposit, the checkout is the customer's guided payment step: PeerPay shows the available payment option, the assigned receiving account and instructions, then accepts the external transfer reference. The customer never sends money through a PeerPay form; they transfer in their bank or wallet app and return to submit the reference.

  • Available banks and wallets for the order
  • Receiving account with copy controls (deposit)
  • Min/max amount limits and method-specific instructions
  • Reference submission and pending / retryable error states
  • Success, failure, review-required, and expiry outcomes plus a support reference

The checkout status is a customer-facing view, not your settlement ledger. Reconcile server-side with a signed webhook or GET /v1/deposits/:id before crediting a wallet.

Public-token API (checkout only)

These routes live on the API host under /checkout-api. They are scoped to the public token — no merchant API key.

Endpoints
GET  /checkout-api/c/{public_token}/state
POST /checkout-api/c/{public_token}/references
GET  /checkout-api/c/{public_token}/events
GET  /checkout-api/c/{public_token}/logo

GET  /checkout-api/w/{public_token}/state
POST /checkout-api/w/{public_token}/confirm
GET  /checkout-api/w/{public_token}/events
GET  /checkout-api/w/{public_token}/logo

Deposit reference submission body:

POST .../c/{token}/references
{
  "payment_method": "telebirr",
  "reference": "CHK7M2P9QX",
  "supplementary_fields": {
    "phone": "0911223344"
  }
}

Withdrawal destination confirmation (when the customer confirms or edits the destination on checkout):

POST .../w/{token}/confirm
{
  "destination": {
    "bank": "telebirr",
    "account_number": "0933445566"
  }
}

Checkout events default to Server-Sent Events when Accept: text/event-stream. Use ?format=json for a JSON timeline. Resume with Last-Event-ID or ?after=.

Security boundaries

Never expose to checkout clients:

  • Merchant API keys
  • Webhook endpoint secrets
  • Bank-verification secrets
  • Provider raw responses
  • Other merchants' receiving accounts or admin data

After checkout, reconcile server-side via Webhooks or deposit polling.