Payment Orchestrator
Pre-authorization, Capture & Void

Pre-authorization, Capture & Void

With a pre-authorization the payment is not charged immediately: the buyer's bank holds the amount and the payment waits in AUTHORIZED. When you are ready to fulfil the order you capture all or part of the amount; if you will not fulfil it you void the hold.

Typical uses: charging when the order ships, deposits and reservations, partially fulfilled orders, payments held until a review finishes. The same hold, capture and void model is also used for the card parts of a split payment.

You request a pre-authorization with the preauth field of the payment request. This page describes every pre-authorization rule in one place; the payment pages show the field in their own requests and link here.

All amounts are integers in minor units (kuruş): "amount": 10050 = ₺100.50. Do not send decimals or floats.

How it works

Send preauth: true in the payment request

Which request carries the field depends on your payment path; see Requesting a pre-authorization.

The amount is held

When the payment succeeds its status becomes AUTHORIZED instead of CAPTURED and payment.authorized is sent to your webhook. No money has moved yet; do not ship the order on this event.

Capture or void

When you fulfil the order, capture the amount (payment.captured or payment.partially_captured). If you will not fulfil it, void the hold (payment.voided).

Do not miss the window

A pre-authorization that is not captured is closed on the platform as EXPIRED 7 days after the payment was created; see Expiry.

Requesting a pre-authorization

There is no separate pre-authorization endpoint. You send the preauth field in the request that starts the payment:

Payment pathRequest that carries preauthDetails
Checkout Form (card and non-card methods)POST /payments/intentsThe decision is stored on the intent; whatever charge the form makes (2D, 3D Secure or a non-card method) follows it. You send nothing else from the form. See Checkout Form
Direct API — cardPOST /payments/direct/payment (paymentMethod: "CARD")Card — 2D, Card — 3D Secure
Direct API — non-card methodsPOST /payments/direct/paymentPayment

The field behaves the same in both requests:

ValueResult
truePre-authorization: the amount is held, the payment becomes AUTHORIZED
false or omittedImmediate charge: the payment becomes CAPTURED
Any non-boolean value ("true", "false", 1 …)400 VALIDATION_ERROR. A string value is not converted to a boolean

A pre-authorization instruction is never silently turned into an immediate charge. The payment is never sent to a provider that cannot honour it; it is either routed to another suitable provider or rejected with an explicit error (see Which methods support it).

{
  "amount": 10050,
  "currency": "TRY",
  "orderId": "ORD-84722",
  "callbackUrl": "https://shop.example.com/payment/result",
  "preauth": true
}

For every field of POST /payments/intents see Checkout Form.

Which methods support it

The provider that processes the payment must support pre-authorization.

Card

  • 2D: Providers that do not support pre-authorization are removed from the candidates and the payment is routed to one that does. If no suitable provider remains, the payment becomes FAILED with decline code PREAUTH_NOT_SUPPORTED.
  • 3D Secure: The provider must also support pre-authorization over 3D Secure. Providers that do not are removed from the candidates. If no suitable provider remains, the payment becomes FAILED with decline code PREAUTH_3DS_NOT_SUPPORTED. If no provider supports pre-authorization over 3D Secure at all, the request is rejected before the payment starts with 422 payment.preauth.three_ds_unsupported.
  • In the 3D Secure flow no money is charged when the buyer completes the bank verification; the payment becomes AUTHORIZED.

Non-card methods

  • Whether a method and a provider support pre-authorization is shown by the preauth field of the GET /payments/methods response (on the method row and on each provider).
  • If you send preauth: true with a method that does not support it, the request is rejected with 422 payment.preauth.unsupported_for_method. The payment is not started and nothing is charged.
  • For a pre-authorization intent the Checkout Form only shows methods and providers that support pre-authorization.

Capture

POST /payments/{id}/capture charges the amount of an AUTHORIZED payment. The endpoint requires Step-Up (Bearer token and Ed25519 signature; see Authentication).

HeaderValue
Idempotency-KeyRequired. Without it the request is rejected with 400 payment.operation.idempotency_key_required. A request repeated with the same key does not capture twice
FieldTypeRequiredDescription
paymentIntentIdstringYesThe same payment ID as {id} in the path
amountintegerNoAmount to capture, minor units, at least 1. When omitted, the whole remaining amount is captured
  • When amount is lower than the authorized amount a partial capture is made and the status becomes PARTIALLY_CAPTURED. You can capture the rest with another capture request; when the total reaches the authorized amount the status becomes CAPTURED.
  • The total captured can never exceed the authorized amount; a request that would exceed it is rejected with 400 payment.capture.amount_exceeded.
# See the Authentication page for how the Step-Up signature headers are computed.
curl -X POST https://api-gateway.lunixi.com/api/v1/payments/9c6f2b1a-4e8d-4a3f-b5e6-2d7c1a9b8e30/capture \
  -H "Authorization: Bearer at_test_5f3e2b1a..." \
  -H "Content-Type: application/json" \
  -H "X-Key-Id: mk_test_sk_..." \
  -H "X-Date: 2026-10-11T09:00:00.000Z" \
  -H "X-Nonce: 3c1d8e2a-7b4f-4e6a-9d0c-5a2b1e8f7c64" \
  -H "Digest: SHA-256=..." \
  -H "X-Signature: ..." \
  -H "Idempotency-Key: cap-ORD-84722-1" \
  -d '{
    "paymentIntentId": "9c6f2b1a-4e8d-4a3f-b5e6-2d7c1a9b8e30",
    "amount": 7500
  }'

Capture response

{
  "status": "success",
  "code": "PAYMENT_PARTIALLY_CAPTURED",
  "message": "…",
  "data": {
    "id": "9c6f2b1a-4e8d-4a3f-b5e6-2d7c1a9b8e30",
    "amount": 10050,
    "currency": "TRY",
    "status": "PARTIALLY_CAPTURED"
  },
  "details": {
    "capturedAmount": "7500",
    "refundedAmount": "0",
    "remainingCapturable": "2550",
    "remainingRefundable": "7500"
  },
  "statusCode": 200,
  "requestId": "…"
}

On a full capture code is PAYMENT_CAPTURED and data.status is CAPTURED. The amounts in details are minor-unit strings.

Void

POST /payments/{id}/void removes a hold that has not been captured; no money moves and the payment becomes VOIDED. The endpoint accepts an API-key signature or a panel session; when calling it from the API, send the signature headers described on Authentication.

# See the Authentication page for how the Step-Up signature headers are computed.
curl -X POST https://api-gateway.lunixi.com/api/v1/payments/9c6f2b1a-4e8d-4a3f-b5e6-2d7c1a9b8e30/void \
  -H "Authorization: Bearer at_test_5f3e2b1a..." \
  -H "Content-Type: application/json" \
  -H "X-Key-Id: mk_test_sk_..." \
  -H "X-Date: 2026-10-11T09:00:00.000Z" \
  -H "X-Nonce: 6e2a9c41-0d3b-4f7a-8b15-9c4e2d1f7a03" \
  -H "Digest: SHA-256=..." \
  -H "X-Signature: ..." \
  -H "Idempotency-Key: void-ORD-84722-1" \
  -d '{}'

Void response

{
  "status": "success",
  "code": "PAYMENT_VOIDED",
  "message": "…",
  "data": {
    "id": "9c6f2b1a-4e8d-4a3f-b5e6-2d7c1a9b8e30",
    "amount": 10050,
    "currency": "TRY",
    "status": "VOIDED"
  },
  "statusCode": 200,
  "requestId": "…"
}
⚠️

Void is not a refund. Void removes a hold that has not been charged. To give back money that was charged, use Refunds; a void on a CAPTURED payment is rejected.

Expiry

  • A pre-authorization that is neither captured nor voided becomes EXPIRED (final status) on the platform 7 days after the payment was created, and payment.expired is sent. The window cannot be changed per account.
  • An EXPIRED payment can no longer be captured.
  • The hold on the card is not released by this event. No cancellation is sent to the bank; the hold drops off according to the bank's own period. To release the hold early, void it before the window ends.

Statuses

StatusMeaning
AUTHORIZEDThe amount is held; nothing has been charged yet
PARTIALLY_CAPTUREDPart of the held amount was charged; the rest can still be captured
CAPTUREDThe whole held amount was charged
VOIDEDThe hold was removed without a capture; no money moved
EXPIREDThe pre-authorization was not captured within 7 days and was closed on the platform
FAILEDThe pre-authorization could not be obtained; nothing was held

For every status see Listing Payments.

Errors

SituationResponseWhat to do
preauth is not a boolean400 VALIDATION_ERRORSend true or false
The non-card method does not support pre-authorization422 payment.preauth.unsupported_for_methodUse the method without preauth, or choose a method that supports it
No provider supports pre-authorization over 3D Secure422 payment.preauth.three_ds_unsupportedRequest the pre-authorization with 2D, or take the payment as an immediate charge
The card provider does not support pre-authorizationPayment FAILED, decline code PREAUTH_NOT_SUPPORTED or PREAUTH_3DS_NOT_SUPPORTEDNothing was held; take the payment as an immediate charge
Capture or void without Idempotency-Key400 payment.operation.idempotency_key_requiredSend a key with every capture and void
Capture amount exceeds the remaining amount400 payment.capture.amount_exceededCapture at most details.remainingCapturable
The payment to capture is not AUTHORIZED or PARTIALLY_CAPTURED (e.g. EXPIRED, VOIDED)400Check the status with GET /payments/:id
The payment to void is not in a voidable status (e.g. CAPTURED, VOIDED, EXPIRED)409 payment.intent.invalid_transitionUse Refunds for charged money
Another operation is running on the same payment409 payment.operation.lockedRetry shortly with the same Idempotency-Key
The same Idempotency-Key was used with a different capture or void request409 payment.operation.idempotency_conflictUse a new key for every separate operation; when repeating the same operation send the same key and the same body
Missing or invalid signature, expired token401, 403Sign requests as described on Authentication; for the codes see Error Handling

Error codes

CodeHTTPMeaning
payment.preauth.unsupported_for_method422The selected non-card method or provider does not support pre-authorization. The payment was not started and nothing was charged. details.paymentMethod and details.providerCode say which selection was rejected
payment.preauth.three_ds_unsupported422A pre-authorization was requested for a 3D Secure payment but no provider supports pre-authorization over 3D Secure. The payment was not turned into an immediate charge
payment.operation.idempotency_key_required400The Idempotency-Key header is required on this endpoint. Send the same key when repeating an operation and a new key for a new operation

Webhooks

EventWhen
payment.authorizedThe amount is held (AUTHORIZED). No money has moved; do not ship on this event
payment.partially_capturedA partial capture completed (PARTIALLY_CAPTURED)
payment.capturedThe whole held amount was charged (CAPTURED). Ship the order on this event
payment.voidedThe hold was removed without a capture (VOIDED)
payment.expiredThe pre-authorization was not captured within 7 days (EXPIRED). The hold on the card is not released by this event
payment.failedThe pre-authorization could not be obtained (FAILED)

For delivery and signature verification see Webhooks.

Next steps