Refunds & Void
You can refund all or part of a successful payment. Three endpoints are available: if you want the platform to choose the method (void vs. credit), use the default endpoint; if you want to choose it yourself, send the request to the void or credit endpoint. All three use the same request and response body.
| Endpoint | Method | Use it when |
|---|---|---|
POST /v1/payments/{paymentCode}/refunds |
Platform chooses | Default; existing integrations can keep using this unchanged. |
POST /v1/payments/{paymentCode}/refunds/void |
Always cancel | Full amount only; there is no partial void. |
POST /v1/payments/{paymentCode}/refunds/credit |
Always refund | Full or partial amount, freely. |
Start a refund
Section titled “Start a refund”POST /v1/payments/{paymentCode}/refundsAuthorization: Bearer ldg_live_…Content-Type: application/jsonRequest body (identical schema on all three endpoints):
{ "amount": 40000, "partnerRefundCode": "REF-2026-8841-1"}| Field | Required | Description |
|---|---|---|
amount |
No | Amount to refund (kuruş). If omitted, the entire remaining balance is refunded. On the void endpoint, if you supply an amount it must equal the entire remaining balance. |
partnerRefundCode |
Yes | A unique refund code generated on your side (idempotency). |
Response (201 Created, identical schema on all three endpoints):
{ "refundId": "ref_5Tg8p", "method": "void", "status": "approved"}| Field | Description |
|---|---|
refundId |
The refund’s identifier; used in the status query. |
method |
The method used: void (cancel) or credit (refund). On the default endpoint this is the method the platform chose; on the forced endpoints it matches the endpoint you called. Returned transparently. |
status |
approved, pending (result not yet final), or declined (the bank rejected it synchronously). |
Default endpoint: the platform chooses the method
Section titled “Default endpoint: the platform chooses the method”Whether a POST /v1/payments/{paymentCode}/refunds request becomes a void (cancel) or a credit (refund) is decided by Lydia Gate based on the timing of the sale. It checks whether the sale and the refund request fall on the same processing day (based on the payment provider’s day-end cutoff, not the calendar day). The rule is:
| Sale and request | Requested amount | Method | Result |
|---|---|---|---|
| Same processing day | Full (entire remaining balance) | void |
The transaction is cancelled before it reaches the bank’s books. |
| Same processing day | Partial | (none) | Not yet possible → refund_not_yet_available. Retry after day-end, processed as credit. |
| A different processing day | Full or partial | credit |
The refund is processed. |
In practice: a same-processing-day partial refund returns refund_not_yet_available; retrying after day-end processes it as a credit. A same-processing-day full refund becomes a void immediately.
Forced endpoints: void and credit
Section titled “Forced endpoints: void and credit”If you do not want to leave the method to the platform’s estimate, you can call /refunds/void or /refunds/credit directly. These two endpoints are not subject to the same-processing-day gate; each follows its own structural rules instead.
POST /v1/payments/{paymentCode}/refunds/void
Section titled “POST /v1/payments/{paymentCode}/refunds/void”Always attempts a void (cancel). Structural rules:
- Always a full, untouched amount: if you supply
amount, it must equal the entire remaining balance; a partial amount is not accepted. - If the payment already has an approved credit refund (it is
partially_refunded), it can no longer be voided.
If either rule is not satisfied, the request is rejected with 422 refund_method_not_available before it ever reaches the bank.
Even when the structural rules are satisfied, timing is up to the bank: a void request sent after the transaction has entered the end-of-day cut can be declined by the bank (status: "declined"). As with credit, this is a deliberate-use outcome, not an error.
POST /v1/payments/{paymentCode}/refunds/credit
Section titled “POST /v1/payments/{paymentCode}/refunds/credit”Always attempts a credit (refund); full or partial amounts are both allowed. You can call this on the same processing day as the sale — but if the transaction has not yet passed the day-end cutoff, the bank may decline the request (status: "declined"). This is a deliberate choice on your part, not an error.
If the payment was made on a direct-collection (direct) terminal, this endpoint always returns 422 direct_refund_not_allowed (see the note above).
How a refund affects the payment
Section titled “How a refund affects the payment”An approved refund updates the payment’s status:
| Method and scope | New payment status |
|---|---|
void (full cancel) |
voided |
credit partial (cumulative refunds < sale) |
partially_refunded |
credit full, or cumulative refunds = sale |
refunded |
The status changes only when the refund is approved (status: "approved"); a pending refund does not change the payment status yet.
pending: inconclusive refund
Section titled “pending: inconclusive refund”A refund returning status: "pending" is not yet final; its status is updated once the result is resolved (approved or declined). Do not treat an inconclusive refund as failed on your own. The uncertainty usually comes from a delayed bank response; the refund stays pending until the result is finalized, and this resolution can be delayed. Track the final status via GET /v1/payments/{paymentCode}/refunds/{refundId}. If a refund stays pending for a long time, contact support.
The refund_in_progress (409) error has two distinct triggers:
- Single-flight: you sent a new refund request while a
pendingrefund still exists for the same payment (regardless of which endpoint you used). Wait until the previous refund is finalized. - Duplicate refund code (idempotency): you resent a
partnerRefundCodeyou had already used for a non-declined refund. The request is not processed; query the status of that refund viaGET /v1/payments/{paymentCode}/refunds/{refundId}(the code is already recorded).
Query refund status
Section titled “Query refund status”GET /v1/payments/{paymentCode}/refunds/{refundId}Authorization: Bearer ldg_live_…Response (200 OK):
{ "refundId": "ref_5Tg8p", "method": "void", "status": "approved", "amount": 40000, "procReturnCode": "00", "authCode": "123456", "errMsg": null, "bankReference": "240615091203", "createdAt": "2026-06-15T09:12:00Z", "updatedAt": "2026-06-15T09:12:03Z"}| Field | Description |
|---|---|
refundId |
The refund’s identifier. |
method |
The method used (void / credit). |
status |
Refund status (pending / approved / declined). |
amount |
Refund amount (kuruş). |
procReturnCode |
Bank result code for this refund (if any; otherwise null). |
authCode |
Bank authorization code for this refund (if any; otherwise null). |
errMsg |
Bank error message for this refund (if any; otherwise null). |
bankReference |
Bank reference for this refund (if any; otherwise null). |
createdAt / updatedAt |
Timestamps (UTC). |
Use this endpoint to learn a refund’s final outcome; track the resolution of a pending refund here. You can also see the payment’s current refund breakdown (total/remaining) via GET /v1/payments.
Related error codes
Section titled “Related error codes”| Code | Reason |
|---|---|
payment_not_refundable |
The payment is not in a refundable state. |
refund_amount_exceeds_remaining |
The requested amount exceeds the remaining refundable balance. |
refund_not_yet_available |
On the default endpoint, a same-processing-day partial refund; available after day-end. |
refund_method_not_available |
On the /refunds/void endpoint, the forced method cannot be applied to this payment: the requested amount does not equal the entire remaining balance, or the payment already has an approved credit refund. |
refund_in_progress |
A pending refund already exists for this payment (single-flight), or this partnerRefundCode was already used (duplicate code). |
direct_refund_not_allowed |
The payment was made on a direct-collection (direct) terminal; a credit refund is not possible, only a same-day full void is available. |
Full list: Error Codes.