Hosted Checkout
Redirect customers to BSync hosted checkout pages.
BSync provides a fully hosted checkout page. After creating a Payment Intent, redirect the customer to checkoutUrl — BSync handles provider selection, payment instructions, and status tracking.
OpenAPI: Hosted Checkout paths under
/checkout/{token}— /docs/api-reference
Checkout URL
When you create a Payment Intent, the response includes:
{
"checkoutUrl": "https://bsyncapp.com/checkout/token_xyz"
}Redirect the customer:
res.redirect(payment.checkoutUrl);The checkout page is hosted by BSync. You do not need to build payment UI, provider selection, or SMS matching logic.
Redirect Flow
sequenceDiagram
participant Merchant
participant Customer
participant BSyncCheckout as BSync_Checkout
participant BSyncAPI as BSync_API
Merchant->>BSyncAPI: POST /v1/payment-intents
BSyncAPI-->>Merchant: checkoutUrl
Merchant->>Customer: Redirect to checkoutUrl
Customer->>BSyncCheckout: Load checkout page
BSyncCheckout->>Customer: Select provider + enter phone
Customer->>BSyncCheckout: Complete payment
BSyncCheckout->>Merchant: Redirect to successUrl
BSyncAPI->>Merchant: PAYMENT_PAID webhookCustomer Experience
- Customer lands on the hosted checkout page.
- Available payment providers are displayed (e.g. Vodafone Cash).
- Customer selects a provider.
- Customer enters their sender phone number.
- Payment instructions are shown (amount, receiver, QR/deep link).
- Customer completes payment via their mobile wallet.
- BSync matches the SMS transaction automatically.
- On success, customer is redirected to
successUrlafterredirectDelayseconds.
Checkout Endpoints
These endpoints power the hosted page. You typically do not call them directly — they are documented in OpenAPI for reference and custom integrations.
| Method | Path | Description |
|---|---|---|
| GET | /checkout/{token} | Load checkout session |
| GET | /checkout/{token}/status | Poll checkout status |
| GET | /checkout/{token}/instructions | Payment instructions |
| POST | /checkout/{token}/provider | Select payment provider |
| POST | /checkout/{token}/sender | Submit sender phone |
| GET | /checkout/{token}/events | SSE real-time events |
No API key required for checkout endpoints — the session token authenticates the request.
Real-Time Events (SSE)
Subscribe to GET /checkout/{token}/events for live payment updates:
event: checkout_connected
data: {"paymentId":"pay_abc123","checkoutStatus":"waiting_payment",...}
event: payment_confirmed
data: {"paymentId":"pay_abc123","checkoutStatus":"paid","paymentStatus":"paid",...}
| SSE Event | Trigger |
|---|---|
checkout_connected | Initial connection |
payment_waiting | Payment created |
payment_detected | SMS matched |
payment_confirmed | Payment paid (terminal) |
payment_review_required | Manual review needed |
payment_failed | Review rejected (terminal) |
payment_expired | Session expired (terminal) |
payment_cancelled | Cancelled (terminal) |
heartbeat | Keep-alive |
server_shutdown | Server restart — reconnect after retryAfter seconds |
Terminal events: payment_confirmed, payment_expired, payment_cancelled, payment_failed.
SSE is useful for custom checkout pages. For most integrations, webhooks are the recommended approach.
Expiry
Checkout sessions expire at the Payment Intent's expiresAt timestamp (default: 30 minutes). Expired sessions return HTTP 410:
{
"success": false,
"error": {
"code": "CHECKOUT_EXPIRED",
"type": "conflict_error",
"message": "Checkout session has expired.",
"requestId": "req_abc123",
"details": {}
}
}Success
When payment is confirmed:
- Checkout page shows success state.
- Customer is redirected to
successUrlafterredirectDelayseconds. PAYMENT_PAIDwebhook is delivered to your server.- Payment Intent status becomes
paid.
Always verify payment server-side — do not rely on the redirect alone.
Cancellation
Customer can cancel from the checkout page. If cancelUrl is set, they are redirected there. A PAYMENT_CANCELLED webhook is delivered.
You can also cancel server-side via POST /v1/payment-intents/{paymentId}/cancel.
Review Required
Some payments require manual review (e.g. amount mismatch, duplicate detection). When this happens:
- SSE event:
payment_review_required - Webhook:
PAYMENT_REVIEW_REQUIRED matchStatusbecomesreview_requiredverificationStatusbecomesmanual_review
Your integration should handle this state gracefully — inform the customer that payment is being verified.
Subsequent webhooks: PAYMENT_REVIEW_APPROVED or PAYMENT_REVIEW_REJECTED.
Failure
Payment can fail when manual review is rejected:
- SSE event:
payment_failed(terminal) - Payment Intent status becomes
failed verificationStatusbecomesfailed
Production Tips
- Set both
successUrlandcancelUrlfor a complete customer experience. - Use webhooks as the primary notification mechanism, not SSE or polling.
- Handle
review_requiredstate in your order management system. - Never fulfill orders based on the redirect alone — always confirm via webhook +
getStatus().
Related
Was this page helpful?
hosted-checkout