Refund to Wallet
Refund to Wallet credits a captured payment back into the buyer's in-program wallet balance — store credit the customer spends inside your ecosystem — instead of reversing it to the card network. It is a separate endpoint with its own permission; your existing card refund (POST /payments/:id/refund) is unchanged. Both are money-critical: this endpoint uses the same Step-Up (Bearer plus Ed25519 signature) and runs under one durable idempotency key.
All amounts are minor-unit integers (kuruş): "amount": 500 credits ₺5,00 to the wallet. Never send decimals or floats.
This endpoint is POST /payments/:id/refund-to-wallet — not the card-refund endpoint. Use card refund to send money back to the card. Marketplace / split payments cannot be refunded to a wallet and must use card refund.
How it works
Call the refund-to-wallet endpoint
POST /payments/:id/refund-to-wallet with your Step-Up credentials and an Idempotency-Key. Include amount for a partial credit; omit it to credit everything still refundable. Include programId only if you run more than one wallet program.
The platform resolves the customer server-side
The buyer's phone and external id are read from the payment's recorded buyer — never from the request. The customer is deduped by phone within the program; if none exists, a wallet is auto-provisioned. Marketplace/split payments are rejected here.
The credit is applied to the wallet
The wallet is credited and the ledger is posted first, then the destination=WALLET refund line and the payment's state transition are finalized. The whole operation is idempotent on the durable refundId, so retries never double-credit.
The webhook confirms
wallet.refund.credited is delivered to your webhook endpoint. Reconcile your order state from the webhook, not just the synchronous response.
Request
Authentication is Step-Up: your at_live_… Bearer token plus an Ed25519 request signature, required on every money-movement endpoint — see Authentication. The permission is payment:refund-to-wallet, distinct from the card-refund permission.
| Field | Type | Meaning |
|---|---|---|
amount | integer (minor units), optional | Amount to credit to the wallet. Omit to credit the full remaining refundable balance. |
reason | string, optional | Free-text reason, stored on the refund for your reporting. |
programId | string, optional | Target wallet program. Omit when you run a single program (the wallet resolves it). Required only when you run multiple programs. |
The buyer is not taken from the request. buyerPhone and externalCustomerId are resolved on the server from the payment's recorded buyer, so the DTO has no buyer field. A distinct target customer cannot be forced from the client.
# Partial credit of ₺5,00 to the buyer's wallet
curl https://api-gateway.lunixi.com/api/v1/payments/pay_01J9ZK3W8Q/refund-to-wallet \
-H "Authorization: Bearer at_live_5f3e2b1a..." \
-H "Idempotency-Key: wallet-refund-84721-1" \
-H "Content-Type: application/json" \
# ...plus the Ed25519 request-signature headers — see Authentication
-d '{
"amount": 500,
"reason": "Store credit"
}'
# Full remaining balance to wallet — omit amount
curl https://api-gateway.lunixi.com/api/v1/payments/pay_01J9ZK3W8Q/refund-to-wallet \
-H "Authorization: Bearer at_live_5f3e2b1a..." \
-H "Idempotency-Key: wallet-refund-84721-final" \
-H "Content-Type: application/json" \
-d '{ "reason": "Order cancelled" }'Use a distinct Idempotency-Key per refund intent. The key becomes the durable refundId; retrying it safely replays the same wallet credit (network timeouts and reconcile sweeps won't double-credit), while a new key on the same payment issues an additional credit against the remaining balance.
Response
200 OK means the money was credited to the wallet or, when the buyer has no wallet, it was refunded to the card instead (see the fallback rules below). Reconcile from the webhook to know which path was taken.
Behavior
Program resolution — your own wallet program
programId is optional. Omit it and the wallet resolves the merchant's single program; if you run multiple programs, programId is required.
Phone dedup and auto-provision
For the program, end-user and wallet/account model, see Wallet — Core Concepts.
The wallet user is deduped by phone (primary) within the program: if a customer with that phone hash exists, the credit is written to them; otherwise a wallet is auto-provisioned from the phone plus externalCustomerId. Concurrent provisioning races resolve against the phone/external uniqueness constraint.
No-wallet fallback to card refund
If the buyer has no wallet and cannot be provisioned — no phone on the payment (NO_PHONE) or CANNOT_PROVISION — the flow automatically falls back to the standard card refund. The customer is still refunded and you still get 200.
Feature-disabled is an explicit error
When the feature is off, the wallet returns an inert DISABLED and the endpoint responds with an explicit error — it does not silently fall back to the card. That deliberate distinction separates "the buyer has no wallet" (fallback) from "the feature is turned off" (error).
Which statuses can be refunded to wallet
| Payment status | Refundable to wallet? |
|---|---|
SETTLED | Yes — the supported status in v1 |
AUTHORIZED | No — no captured money to credit yet |
INITIATED, PROCESSING, AWAITING_3D | No — no captured money yet |
REFUNDED | No — nothing left to refund |
| Marketplace / split payment (any status) | No — use card refund |
The refundable ceiling shares the same math as card refund: cumulative captured_amount minus prior refunds.
Errors
| Situation | What you see | What to do |
|---|---|---|
| Marketplace / split payment | Rejected | Use the card refund endpoint instead |
| Buyer has no phone / cannot be provisioned | 200 — refunded to the card instead | None — the customer is refunded; reconcile from the webhook |
| Feature disabled | Explicit DISABLED error | Contact your account manager to enable Refund to Wallet |
amount exceeds the remaining refundable balance | 422 | Read the payment, credit at most the remaining balance |
| Unknown payment id | 404 | Verify the paymentId |
| Wallet credit could not complete, or its outcome is not final yet | 502 payment.refund_to_wallet.credit_failed | Do not start a card refund — the credit may have happened. Retry with the same Idempotency-Key or wait for the wallet.refund.credited webhook; the same key never creates a double credit |
Duplicate request, same Idempotency-Key | Same wallet credit replayed | Safe — no double credit |
Webhooks
| Event | Fired when |
|---|---|
wallet.refund.credited | The refund was successfully credited to the wallet |
wallet.refund.reversed | An operator reversed this wallet refund (the paired inverse of wallet.refund.credited) |
Configure endpoints and delivery semantics in Webhooks.
Next steps
- Refunds — returning captured money to the card, and the fallback path when the buyer has no wallet
- Webhooks — receiving
wallet.refund.creditedandwallet.refund.reversed - Listing Payments — reading a payment's current status and history