Accept payments

Charge Localoy orders through your own payment gateway and report the outcome, so Localoy marks the order paid.

With Open Network payments, you collect the money with your own payment gateway and Localoy records the outcome against the order. Localoy never handles card or wallet details and never moves money.

How it works#

Payment flow
Payment flowCustomerLocaloyCheckout pageYour serverYour gateway1. Pay for an order2. Open a payment session3. Redirect ?session_id=…4. session_id5. GET /payments/sessions/{id}6. amountMinor, customer, returnUrl7. Charge amountMinor8. Transaction ID9. POST …/result SUCCEEDEDThe order is marked paid10. 200 settlement record11. Webhook: payment.updated12. Redirect to returnUrl
  1. Customer → Localoy: Pay for an order
  2. Localoy → Localoy: Open a payment session
  3. Localoy → Checkout page: Redirect ?session_id=…
  4. Checkout page → Your server: session_id
  5. Your server → Localoy: GET /payments/sessions/{id}
  6. Localoy → Your server: amountMinor, customer, returnUrl
  7. Your server → Your gateway: Charge amountMinor
  8. Your gateway → Your server: Transaction ID
  9. Your server → Localoy: POST …/result SUCCEEDED
  10. Note: The order is marked paid
  11. Localoy → Your server: 200 settlement record
  12. Localoy → Your server: Webhook: payment.updated
  13. Checkout page → Localoy: Redirect to returnUrl

The only call that marks an order paid is step 9, made by your server with your API key. Nothing the browser sends can change a payment.

1. Configure your checkout#

In the Partner Portal, open Open Network → Payments and set:

SettingDetail
Checkout URLThe page that starts your checkout, one per environment. It must be https. Any query string you add is kept, and Localoy appends session_id.
Session lifetimeHow long a customer has to pay: 5 to 1,440 minutes, 30 by default.
EnabledA production checkout is switched off until you enable it. Enabling it requires Partner+.

Bookings wait for payment

While a production checkout is enabled, new activity bookings made through the Open Network are left pending until they are paid. A payment that succeeds confirms the booking if your confirmation policy is automatic.

2. Read the session#

Your checkout page receives only session_id. Always read the amount from the API — never from the browser's URL.

cURL
curl "$LOCALOY_BASE_URL/payments/sessions/cm5s3ss10n00000000000001" \
  -H "Authorization: Bearer $LOCALOY_API_KEY"
Response · 200
{
  "success": true,
  "data": {
    "id": "cm5s3ss10n00000000000001",
    "status": "PENDING",
    "environment": "PRODUCTION",
    "amountMinor": 90000,
    "currency": "BDT",
    "description": "Kayak (1 hour) × 2",
    "subject": { "type": "ACTIVITY_BOOKING", "id": "cm5b00k1ng0000000000001", "experienceId": "cm5act1v1ty000000000001" },
    "customer": { "name": "Nusrat Jahan", "phone": "01712345678", "email": null },
    "returnUrl": "https://partner.localoy.app/payments/return?session_id=cm5s3ss10n00000000000001&token=…",
    "expiresAt": "2026-09-26T09:00:00.000Z",
    "isExpired": false,
    "isTest": false,
    "providerReference": null,
    "settledAt": null,
    "createdAt": "2026-09-26T08:30:00.000Z"
  }
}

Refuse to charge if status is not PENDING or PROCESSING, or if isExpired is true.

3. Charge the customer#

Charge exactly amountMinor in currency with your gateway. Keep your gateway's transaction ID — you will report it as providerReference.

If your gateway confirms asynchronously, you can report PROCESSING first to record that the customer has started paying.

4. Report the result#

cURL
curl -X POST "$LOCALOY_BASE_URL/payments/sessions/cm5s3ss10n00000000000001/result" \
  -H "Authorization: Bearer $LOCALOY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7d1f0b3e-2c4a-4b9e-8f51-0a6c2d9e4b17" \
  -d '{
    "status": "SUCCEEDED",
    "amountMinor": 90000,
    "currency": "BDT",
    "providerReference": "TXN-88213094"
  }'
You reportSessionOrder
SUCCEEDEDSettledMarked paid. Localoy sends payment.updated, and booking.updated if the booking is confirmed.
FAILEDFailed; send failureCode and failureReasonMarked failed; failureReason is shown to the customer. Localoy sends payment.updated. The customer can try again.
PROCESSINGProcessingUnchanged.
CANCELLEDCancelled — finalUnchanged.

Localoy checks a SUCCEEDED result against the session. If amountMinor or currency differs, the result is refused with 409 and the order is not marked paid. Refunds and reversals of a mismatched charge are up to you.

See Report a result for every rule and error.

5. Send the customer back#

Redirect the browser to the session's returnUrl. Localoy's return page reads the session's status from Localoy itself, so adding parameters such as ?status=success changes nothing.

Refunds#

When you refund a charge through your gateway, record it so the order shows as refunded:

cURL
curl -X POST "$LOCALOY_BASE_URL/payments/sessions/cm5s3ss10n00000000000001/refund" \
  -H "Authorization: Bearer $LOCALOY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 0b9e4c2a-6f3d-4e1b-9a7c-5d8f2e1b3c40" \
  -d '{ "providerReference": "RFD-55120", "reason": "Customer cancelled" }'

A refund is always for the full session amount, and only a SUCCEEDED session can be refunded. See Record a refund.

Session statuses#

Allowed transitions
PENDING    → PROCESSING, SUCCEEDED, FAILED, CANCELLED, EXPIRED
PROCESSING → SUCCEEDED, FAILED, CANCELLED, EXPIRED
FAILED     → PROCESSING, SUCCEEDED
EXPIRED    → SUCCEEDED
SUCCEEDED  → REFUNDED            (refund endpoint only)
CANCELLED, REFUNDED              final
  • EXPIRED is set by Localoy when the session's lifetime runs out. You cannot report it.
  • A charge that completes after expiry can still be reported as SUCCEEDED; Localoy accepts it and flags the session as settled after expiry.

Rehearse in the sandbox#

  1. Save a sandbox checkout URL in the portal.
  2. On the Payments page, create a test session. It is for 10000 minor units (৳100.00), with a test customer and no real order behind it.
  3. Open the redirect URL the portal gives you, and drive the session with a sandbox key: read it, report a result, try a refund.

Test sessions change no order and fire no webhook. On them isTest is true, and the three subject fields — type, id and experienceId — are null. A sandbox key cannot see production sessions, and a production key cannot see sandbox ones.