Skip to main content

Wallet

Wallet lets you refund customer payments to a stored balance keyed by merchant, customer, and currency, then let the customer spend that credit at any future Ottu checkout in the same currency. No PII is stored on the wallet service.

Authentication for merchant-facing endpoints follows the standard Ottu API key flow — the same key you use for the Checkout API. Service-to-service auth between Ottu Connect and the wallet service uses OAuth 2.0 internally and is invisible to merchants.

Base URL

Every API call in this guide targets https://sandbox.ottu.net. Swap in your own merchant domain when you integrate.

Boost Your Integration

Ottu offers SDKs and tools to speed up your integration. See Getting Started for all available options.

When to Use

  • Refunding without returning funds to the original card or gateway — loyalty, goodwill, or voucher use cases.
  • Letting customers carry over balance between sessions.
  • Reducing payment-gateway fees by spending wallet credit before charging a card.
  • Multi-currency merchants — each currency maintains its own wallet account.

Guide

Workflow

Ottu Wallet Flow The merchant backend refunds a payment to wallet through Ottu Connect, which credits the wallet service. On the next checkout, the SDK discovers the wallet balance, reserves it on submit, and any remainder is charged through the payment gateway. Ottu Connect commits the reservation on success. OTTU PLATFORM Customer Merchant Frontend Embeds Checkout SDK Merchant Backend Refund · Session create Checkout SDK Reserves · Commits · Releases Ottu Connect REST API · Operation routing Wallet Service Ledger · Reservations · Balance Refund destination "pg" (default) · "wallet" Payment Gateway Charges remainder when wallet doesn't cover Refund reverses here when destination = "pg" pays at checkout embeds refund · session reserve / commit credit / debit remainder balance shown

Wallet credit is created when a merchant refunds with destination: "wallet" — see Refund to Wallet. Once a customer has positive balance in the session currency, the developer journey to spend it is:

  1. Filter the payment methods — call the Payment Methods API with payment_services: ["wallet"] to get the wallet-capable pg_codes.
  2. Create a Checkout session — call the Checkout API with those pg_codes and the customer_id. The customer_id is what binds the session to that customer's wallet account.
  3. Initialize the Checkout SDK — the SDK fetches the wallet balance from Ottu Connect and renders "Wallet (X.XXX CCC)" alongside the other payment methods.
  4. Customer applies wallet on submit — Ottu Connect reserves the required amount (or the full balance, whichever is lower). Any shortfall is charged through the selected gateway.
  5. Commit or release — on success the reservation commits to a debit entry; on abandon, cancel, or failure it auto-releases after about four hours.

Live Demo

Try the wallet flow below. The demo discovers a wallet-capable gateway, seeds a fresh customer's wallet through the docs backend, creates a Checkout session, and mounts the SDK so you can pay with wallet. Screenshots of the merchant-side flow are on the business wallet pages.

Try Wallet at Checkout

Loading…

Step-by-Step

1. List the wallet-capable payment gateways

Call the Payment Methods API (POST /b/pbl/v2/payment-methods/) with payment_services: ["wallet"] to filter the gateway list to those that support wallet payments. Use these pg_codes when creating the Checkout session.

Filter payment methods to wallet-capable PGs
curl --location 'https://sandbox.ottu.net/b/pbl/v2/payment-methods/' \
--header 'Authorization: Api-Key <YOUR_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"plugin": "e_commerce",
"currencies": ["KWD"],
"type": "sandbox",
"payment_services": ["wallet"],
"tags": ["demo"]
}'
Example response (wallet-enabled PG)
{
"customer_payment_methods": [],
"payment_methods": [
{
"code": "ottu-sandbox",
"name": "Ottu Sandbox",
"is_sandbox": true,
"default_currency": "KWD",
"accepted_currencies": ["KWD", "SAR", "AED", "USD", "BHD"],
"operation": "purchase",
"operations": ["refund"],
"is_tokenizable": true,
"auto_debit_enabled": true,
"payment_services": ["wallet"],
"tags": ["e_commerce", "demo"]
}
]
}
customer_id filter

You don't need customer_id to discover wallet-capable gateways generically. If you do pass customer_id and that customer has a wallet enabled in the session currency, the wallet entry will be returned alongside the standard payment methods. To narrow the gateway list to wallet-capable PGs for later refunds, use payment_services: ["wallet"] as shown above.

For the full request/response reference, headers, error codes, and supported filters, see the Payment Methods API page.

2. Create a Checkout session with the wallet-capable PGs

Call the Checkout API with the pg_codes returned in step 1, plus the customer_id to enable the customer's wallet on this session. The response includes a session_id that you pass to the SDK in the next step.

Create Checkout session with wallet enabled
curl --location 'https://sandbox.ottu.net/b/checkout/v1/pymt-txn/' \
--header 'Authorization: Api-Key <YOUR_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"type": "e_commerce",
"pg_codes": ["ottu-sandbox"],
"amount": "12.500",
"currency_code": "KWD",
"customer_id": "cust_abc123",
"customer_email": "[email protected]",
"customer_phone": "+96550000000"
}'
Example response
{
"session_id": "sess_9f8e7d6c5b4a",
"checkout_url": "https://sandbox.ottu.net/checkout/sess_9f8e7d6c5b4a",
"checkout_short_url": "https://sandbox.ottu.net/c/sess_9f8e7d6c5b4a",
"expiration_time": "00:30:00"
}

The session_id returned here is what step 3 passes into Checkout.init. The customer_id is what links the session to that customer's wallet account — wallet appears in the SDK only when (a) the session has a customer_id and (b) the wallet account for (merchant, customer_id, currency) has positive balance.

Only one wallet per provider may be available per checkout

When you list several payment gateways in pg_codes, Ottu checks the wallets behind them. If two or more of the selected gateways each carry a wallet from the same wallet provider that can be used for the transaction's currency, the checkout creation request is rejected with HTTP 400 — the checkout page could not unambiguously show which wallet balance to apply. Choose your pg_codes so that at most one wallet per provider is available for the transaction.

For example, the request below lists two gateways — gateway-a and gateway-b — that each have a wallet from the same provider available in KWD:

Request that triggers the error — POST /checkout/v1/pymt-txn
curl --location 'https://sandbox.ottu.net/b/checkout/v1/pymt-txn/' \
--header 'Authorization: Api-Key <YOUR_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"type": "payment_request",
"amount": "10.000",
"currency_code": "KWD",
"pg_codes": ["gateway-a", "gateway-b"]
}'

Because both gateways expose a same-provider wallet for KWD, the request fails:

Response — 400 Bad Request
{
"pg_codes": [
"Multiple wallets of the same provider are available for this currency across the selected payment gateways. Only one wallet per provider may be available for a transaction."
]
}

3. Render the Checkout SDK with the session

Add a mount point to your page — the SDK looks for an element matching selector:

Mount point in your page
<div id="checkout"></div>

Then initialize the SDK with the session_id returned by step 2:

Checkout SDK init with wallet enabled
Checkout.init({
selector: "checkout",
merchant_id: "sandbox.ottu.net", // use your merchant id
apiKey: "YOUR_API_PUBLIC_KEY",
session_id: "sess_9f8e7d6c5b4a",
formsOfPayment: ["wallet", "ottu-sandbox"], // or omit to show all
});

4. Customer applies wallet credit at checkout

The SDK reserves the required amount (or the full balance, whichever is lower). If the session amount exceeds the wallet balance, the customer is prompted to pick a second method for the remainder. On submit, all reservations confirm together. On success the wallet entry commits; on cancel, error, or four-hour timeout it auto-releases.

Step 1 of 5
Checkout SDK showing Wallet as a payment method with balance
Step 1 of 5

Wallet appears as a method

When the customer has positive balance in the session currency, Wallet (100.00 USD) renders alongside cards and other gateways. No SDK config is needed beyond passing customer_id on the Checkout API session.

5. (Optional) Read wallet state from your backend

Reading wallet state from your backend is optional — the same balance, ledger, and operation views are available in the Ottu dashboard. Use the read APIs only if you want to surface wallet operations to your end customers in your own UI. See the API Reference section below.

Use Cases

Partial payments (wallet plus another method)

Three balance-versus-amount cases:

Wallet balance vs amountBehavior
Balance ≥ amountOnly amount is deducted. Surplus stays in the wallet.
Balance == amountWallet fully covers; balance becomes 0.
Balance < amountFull balance is consumed; customer pays the difference with another method.

Customers cannot choose how much wallet credit to apply — Ottu computes it automatically.

Reservation lifecycle

  • Reserved when the customer submits payment.
  • Committed on payment success.
  • Released automatically about four hours after an abandoned, cancelled, or failed payment. No human intervention.

Wallet hidden for authorize-only sessions

When the session is configured for authorization-only (no immediate capture), wallet is not offered as a payment method. Wallet supports immediate-capture flows only.

Refunding to wallet

See Refund to Wallet on the Operations page for the full API and behavior. The refund endpoint accepts destination: "wallet" to credit the wallet instead of reversing through the gateway.

Wallet-paid transactions cannot be refunded

A transaction settled with wallet credit cannot be refunded back to the wallet — or to any other destination. Once a customer spends their balance at checkout, that leg of the payment is final.

Cross-currency wallet payments are not supported

A KWD wallet cannot be used to pay a SAR order, and vice versa. Each currency maintains a separate wallet account.

Wallet Transactions

When a payment is settled with wallet credit, the payment details — returned by the Checkout API retrieve call, the PSQ inquiry, and payment webhooks — include a customer_wallet_transactions array. Each element is one wallet leg that contributed to the payment, so you can attribute every leg without a separate call to the wallet read APIs.

The key is omitted entirely when no wallet was involved. A single-wallet payment emits a one-element list; multi-wallet stacking (Ottu credit plus a loyalty wallet, for example) emits one element per provider.

FieldTypeDescription
provider_codestringStable identifier of the wallet provider that handled this leg — e.g. wallet_ottu. Empty for legacy native payments where the provider wasn't recorded.
amountstringAmount charged from this wallet leg.
currencystringISO 4217 currency code of the leg.
operation_idstringWallet service operation ID — the join key to the wallet ledger.
wallet_responseobjectFull raw response from the wallet service for this leg.
created_atstring (date-time)When the wallet transaction was created.
modified_atstring (date-time)When the wallet transaction was last modified.
customer_wallet_transactions in a payment details response
"customer_wallet_transactions": [
{
"provider_code": "wallet_ottu",
"amount": "5.000",
"currency": "KWD",
"operation_id": "op_7c2f9a1e4b",
"wallet_response": {
"amount": "5.000",
"currency": "KWD",
"status": "completed",
"operation_id": "op_7c2f9a1e4b"
},
"created_at": "2026-05-20T09:14:33Z",
"modified_at": "2026-05-20T09:14:35Z"
}
]

Use these fields to keep your own system in sync:

  • Reconciliation — sum amount across the array to know how much of the payment wallet credit covered. The remainder (paid_amount minus the wallet total) is what the payment gateway settled, so you can split a single payment across the two funding sources in your books.
  • Audit trail — store operation_id against your order. Pass it to Get Operation by ID to pull every ledger entry the operation produced for a full audit record.
  • System updates — use provider_code to attribute the credit to the correct wallet program, and created_at / modified_at to timestamp the movement in your own ledger.

Stacked-wallet setups (e.g., Ottu wallet + Qitaf) emit one record per provider in the same payload; single-wallet payments emit a one-element list — so you can reconcile multi-wallet payments without polling the wallet read APIs.

API Reference

Three read APIs let you query wallet state from your backend. All three accept the standard Ottu API key authentication and are POST endpoints — the request body carries the customer_id (and, where required, the currency) that scopes the query.

List wallet accounts for a customer

List wallet accounts for a customer

POST 

/b/wallet/ottu/v1/accounts/

Returns the customer's wallet accounts — one per currency — with confirmed and available balances.

Accounts are created lazily: one exists only after the first credit lands in that currency (a refund to wallet, a manual adjustment, a promo, or a goodwill credit). A customer who has never received a wallet credit in a given currency has no account in that currency, and none is returned — count: 0 with an empty results array is a valid response.

balance reflects confirmed funds only; available_balance is balance minus the amounts held by in-flight checkout reservations, and is what the customer can actually spend right now. Results are cursor-paginated — pass the pagination.cursor value from the next object to fetch the following page.

curl --location 'https://sandbox.ottu.net/b/wallet/ottu/v1/accounts/' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Api-Key YOUR_API_KEY' \
--data '{
  "customer_id": "sandbox-usd",
  "currency": "USD"
}'

Request

Responses

Best Practices

  • Discover per session. Always call Payment Methods API per session — don't assume balance from a prior call.
  • Display balance fresh. Call List Accounts on page load if you show balance in your own UI. Cache only briefly; balance changes on every payment.
  • Append-only ledger. Never derive balance client-side from history — rely on the Accounts endpoint for authoritative balance.
  • Match the currency. For multi-currency merchants, present the wallet matching the session currency only.

FAQ

What's Next?