Create Payment
To start a payment, call POST /v1/payments. This call creates a payment request (amount, terminal, returnUrl); the request is cardless, so the card is not part of it. The response returns a handoffUrl you redirect the buyer’s browser to, and a paymentRef bound to it. The card is carried, in the next step, from the buyer’s browser to Lydia Gate 3DS Relay; see Card Handoff.
POST /v1/paymentsAuthorization: Bearer ldg_live_…Content-Type: application/jsonRequest body
Section titled “Request body”{ "amount": 125050, "partnerTxCode": "ORD-2026-8841", "terminalId": "trm_3Kd9x", "installment": 1, "description": "Order #8841", "returnUrl": "https://yourstore.example/payment/result"}| Field | Required | Description |
|---|---|---|
amount |
Yes | Amount to charge, integer in minor units (kuruş) (1,250.50 TRY → 125050). See Amounts and Currency. |
partnerTxCode |
Yes | A transaction code you generate on your side, unique per order. Acts as an idempotency key (see below): if the response is lost, retry with the same code instead of generating a new one. |
terminalId |
Yes | The terminal the payment is routed to. Must belong to you and be active; otherwise terminal_not_allowed is returned. Terminals are provisioned for you during onboarding; you use the terminalId you were given. |
installment |
No | Number of installments; 1 for a single charge (default). If your terminal has no permission for this count, the request is rejected with installment_not_allowed at create time. You can see ahead of time which installment counts the card qualifies for, and the commission rates, using Installment Options. |
description |
No | Transaction description. |
returnUrl |
Yes | The address the buyer is redirected to via 303 after the 3D return. Lydia Gate validates this address (open-redirect protection). |
Response
Section titled “Response”The first create returns 201 Created; an idempotent replay of the same request returns 200 OK — see Idempotency: partnerTxCode. Either way, the response contains no card or bank 3D form data; it returns a paymentRef and a handoffUrl:
{ "paymentCode": "pay_7Hq2bL", "status": "initiated", "paymentRef": "pr_3Kd9xQ1w2E", "handoffUrl": "https://pay.lydiagate.example/checkout", "idempotentReplay": false}| Field | Description |
|---|---|
paymentCode |
The payment’s permanent identifier; used in query and refund calls. |
status |
The initial status is always initiated. See Payment Lifecycle. |
paymentRef |
A single-use payment reference; carried with the card in the handoff POST. It is distinct from paymentCode and is not used in query/refund calls. |
handoffUrl |
The Lydia Gate 3DS Relay address the buyer’s browser/WebView is redirected to, with the card + paymentRef. |
idempotentReplay |
false means this request created a new payment (HTTP 201). true means the response is a replay of a previously created payment (HTTP 200): no new record was written, and the returned paymentCode and paymentRef are identical to the first call’s, while status reflects the payment’s current state (it may no longer be initiated if the 3D flow has progressed since). See Idempotency: partnerTxCode. |
Redirect the buyer to the handoff
Section titled “Redirect the buyer to the handoff”You redirect the buyer’s browser/WebView to the handoffUrl from the response, carrying the card you collected on your own checkout plus the paymentRef. You do not build the bank’s 3D form; Lydia Gate 3DS Relay produces the auto-submit form. Detail (web form-POST and mobile WebView): Card Handoff.
Window (TTL)
Section titled “Window (TTL)”The payment’s window to complete 3D is 30 minutes by default; on terminals routed to the HalkÖde infrastructure the window is 135 minutes. If the buyer does not complete it within this window, the payment moves to expired.
Rate limits
Section titled “Rate limits”Each API key has a budget of 60 requests per minute for POST /v1/payments. The endpoint also enforces
30 requests per minute and 1,800 requests per hour for the same source IP. Different API keys under the
same account have independent 60/minute budgets, while requests from the same source IP share the IP budgets.
Changing the source IP does not reset an API key’s own 60/minute budget.
For high-volume integrations both the API key budget and the IP limits can be raised together per account — contact us to arrange it.
Every create attempt, including an idempotent replay, consumes these budgets. When a limit is exceeded, Lydia
Gate returns 429 rate_limited before reading or creating a payment. The Retry-After response header is
always 60 (which limit was exhausted is not disclosed); you can retry after 60 seconds.
All API-key /v1 endpoints also pass through a coarse abuse-control gate before the key is looked up: a shared
240/minute budget for the same source IP and a 600/minute platform-wide budget. These are not this
endpoint’s business quota. Different /v1 endpoints share the upstream IP budget, so abnormal aggregate load
can produce an earlier 429 rate_limited response.
Idempotency: partnerTxCode
Section titled “Idempotency: partnerTxCode”partnerTxCode is an idempotency key: a request sent with the same code and the same content does not create a new payment — it replays the first call’s response.
| Scenario | Response |
|---|---|
| First create | 201 Created + idempotentReplay: false |
Same partnerTxCode + same request |
200 OK + idempotentReplay: true — paymentCode/paymentRef identical to the first call, status reflects the current state |
Same partnerTxCode + different request |
409 duplicate_partner_tx_code |
What counts as “the same request”: the request is considered identical only if all of the following match exactly: partnerId (comes from your API key, not part of the body), terminalId, amount, installment, and the currency (always TRY today; not a separate field in the request). returnUrl and description are excluded from this comparison — sending a different return address for the same order is legitimate and does not break the replay.
Three boundaries:
- A
failed/expiredpayment frees the key. Once a payment moves to one of these statuses, a request with the samepartnerTxCodeis not a replay; it creates a new payment normally (201). - Idempotent replay is scoped to the interface that created the payment. If a payment was created from the merchant panel, an API call with the same
partnerTxCodeis not a replay (it returns409) — and vice versa. Each interface only replays the payments it created. - Payments created through a payment link are not replayed. A payment completed via a payment link gets an auto-generated
partnerTxCode(PL-prefix, e.g.PL-42-1); sending this code toPOST /v1/paymentswhile the link payment is still active returns409 duplicate_partner_tx_code— the payment-link flow is outside the scope of idempotent replay. (If the link payment reachedfailed/expired, rule 1 applies: the key is released and a new payment is created.)
To check the current status of a payment you previously created with a given partnerTxCode (for example, to check before retrying after a lost response), you can use GET /v1/payments?partnerTxCode=… — this alias resolves only active payments; for a failed/expired payment, query definitively by paymentCode instead (see Get Payment).
Possible errors
Section titled “Possible errors”| Code | Reason |
|---|---|
rate_limited |
The API-key or source-IP rate limit was exceeded (429). Wait for the Retry-After duration, then retry with the same partnerTxCode. |
duplicate_partner_tx_code |
The same partnerTxCode was used with a different request (or belongs to a payment created on another interface, or to a payment link) — the code is in use for a different charge. Retrying the same request is not an error; it returns 200 as an idempotent replay. See Idempotency: partnerTxCode. |
terminal_not_allowed |
The terminal does not belong to you or is inactive. |
amount_limit_exceeded |
If a per-transaction min/max amount limit is configured for your account, an amount outside that range is rejected with 422. Without a configured limit (the default), the only amount check is positivity: a negative or 0 amount is rejected with 400 validation_error. |
pos_per_tx_limit_exceeded, pos_daily_limit_exceeded, pos_weekly_limit_exceeded, pos_monthly_limit_exceeded, pos_time_window_limit_exceeded |
A limit configured on the bank connection behind your terminal (per-transaction, daily, weekly, monthly, or a specific time window) is full (422). The payment is not created; partnerTxCode is not consumed, so you can retry the same request later. These limits can also apply at the start of 3D; see Payment Flow and Error Codes. |
installment_not_allowed |
If your terminal has no permission for this installment count, the request is rejected at create time (this call). Reasons that depend on the card’s brand (the card is not eligible for installments, or this brand × installment count is not offered at your terminal) are still evaluated only during the handoff/3D relay, since the card is not yet known at this call. Remedy: Error Codes. |
Full list: Error Codes.