Skip to content

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.
POST /v1/payments/{paymentCode}/refunds
Authorization: Bearer ldg_live_…
Content-Type: application/json

Request 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.

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).

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.

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:

  1. Single-flight: you sent a new refund request while a pending refund still exists for the same payment (regardless of which endpoint you used). Wait until the previous refund is finalized.
  2. Duplicate refund code (idempotency): you resent a partnerRefundCode you had already used for a non-declined refund. The request is not processed; query the status of that refund via GET /v1/payments/{paymentCode}/refunds/{refundId} (the code is already recorded).
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.

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.