> For the complete documentation index, see [llms.txt](https://docs.commercelayer.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.commercelayer.io/core-api-reference/2026-05/payment_transactions.md).

# Payment transactions

The payment transaction object and the allowed CRUD operations on the related resource endpoint

Payment transactions are the entry point for any payment action in Commerce Layer. Every operation — [authorization](/core-api-reference/2026-05/payment_authorizations.md), [capture](/core-api-reference/2026-05/payment_captures.md), [void](/core-api-reference/2026-05/payment_voids.md), or [refund](/core-api-reference/2026-05/payment_refunds.md) — is modeled as a transaction against a [payment session](/core-api-reference/2026-05/payment_sessions.md), whether initiated explicitly via the API or created automatically when a gateway webhook event arrives. Each transaction tracks its [full lifecycle](#lifecycle) from pending through to a terminal state.

{% hint style="info" %}
This is an **immutable API**, meaning that create, update, and delete operations are not allowed on this endpoint. You can only fetch a list of payment transactions or a single payment transaction object. For the full set of available CRUD actions, refer to the specific payment transaction type endpoints.
{% endhint %}

Payment transactions are polymorphic. The concrete type can be one of:

<table><thead><tr><th width="240">Type</th><th>Description</th></tr></thead><tbody><tr><td><strong><code>payment_authorizations</code></strong></td><td>Reserves funds on the customer's payment instrument.</td></tr><tr><td><strong><code>payment_captures</code></strong></td><td>Collects previously authorized funds.</td></tr><tr><td><strong><code>payment_voids</code></strong></td><td>Cancels an authorization before capture.</td></tr><tr><td><strong><code>payment_refunds</code></strong></td><td>Returns previously captured funds to the customer.</td></tr></tbody></table>

## Lifecycle

All transaction types share the same state machine:

<table><thead><tr><th width="240">Status</th><th>Description</th></tr></thead><tbody><tr><td><strong><code>pending</code></strong></td><td>The transaction has been created and is queued for processing.</td></tr><tr><td><strong><code>requires_action</code></strong></td><td>The transaction requires additional customer action (e.g., a 3DS challenge).</td></tr><tr><td><strong><code>processing</code></strong></td><td>The transaction is being processed by the payment provider.</td></tr><tr><td><strong><code>succeeded</code></strong></td><td>The transaction completed successfully.</td></tr><tr><td><strong><code>declined</code></strong></td><td>The transaction was declined by the payment provider.</td></tr><tr><td><strong><code>failed</code></strong></td><td>The transaction failed due to a processing error.</td></tr><tr><td><strong><code>canceled</code></strong></td><td>The transaction was canceled before completion.</td></tr><tr><td><strong><code>expired</code></strong></td><td>The transaction timed out.</td></tr></tbody></table>

{% hint style="warning" %}
A [payment session](/core-api-reference/2026-05/payment_sessions.md#lifecycle)'s `voided` or `invalidated` status is terminal — once either is reached, no further transaction (authorization, capture, void, or refund) can be created against it, whether initiated explicitly via the API or automatically from a gateway webhook event. The attempt is rejected with a `422 Unprocessable Entity` error.
{% endhint %}

{% hint style="info" %}
The `succeeded` status isn't always final for a payment authorization: a gateway can send a corrective event reversing its own earlier success (e.g. a delayed bank decline), moving the authorization to `declined`, `failed`, `canceled`, or `expired` and invalidating its session. A reversed authorization is the only corrective event that invalidates the session this way — a corrective event on a capture, void, or refund moves that transaction's own status, but leaves the session's status untouched. A session can also end up `invalidated` without any transaction ever reaching `succeeded` — e.g. when a [gift card](/core-api-reference/2026-05/payment_setting_gift_cards.md#invalidation-at-authorization) authorization fails outright.
{% endhint %}

## Idempotency

Every payment transaction is created with an idempotency key, and Commerce Layer derives the value it sends to the payment provider from that key. Two transactions sharing a key present the gateway with the same operation, so a resubmitted capture or refund — whether from a retried API call or a double-clicked button — is recognized as the one already taken instead of moving the money twice.

A new transaction inherits the key of an earlier one when all of the following hold:

* It belongs to the same payment session.
* It is the same type — an authorization, capture, void, or refund.
* It points at the same reference transaction: the payment authorization being captured or voided, the payment capture being refunded.
* Its `amount_cents` is identical.
* Its `reference` and `reference_origin` are identical — two transactions that both omit them count as a match.
* The earlier transaction is still open (`pending`, `requires_action`, or `processing`) and was created within the [idempotency window](/core-api-reference/2026-05/payment_settings.md#idempotency-window).

Otherwise the new transaction gets a key of its own and reaches the gateway as a distinct operation.

That last condition is what keeps a legitimate repeat from being swallowed. An earlier attempt that already reached `succeeded`, `declined`, `failed`, `canceled`, or `expired` never lends its key — a retry after a decline, and a genuine follow-up after a success, both go through immediately, with no need to wait for the window to lapse. The window bounds the open case only: an attempt stuck in `processing` because a gateway event never arrived stops absorbing new attempts once it expires.

{% hint style="warning" %}
Inside the window, two open transactions matching on all of the above are indistinguishable to Commerce Layer — two separate partial refunds of the same amount against the same capture, submitted back to back, are treated as one. Give each its own `reference` (and `reference_origin`) when they are genuinely distinct operations.
{% endhint %}

Where the key is sent to the provider depends on the payment setting type (more details on external endpoints [here](/core-api-reference/2026-05/payment_setting_externals.md#idempotency)):

<table><thead><tr><th width="260">Type</th><th>Idempotency parameter</th></tr></thead><tbody><tr><td><strong><code>payment_setting_adyens</code></strong></td><td><code>Idempotency-Key</code> header.</td></tr><tr><td><strong><code>payment_setting_braintrees</code></strong></td><td>Not sent — Braintree's API takes no idempotency parameter.</td></tr><tr><td><strong><code>payment_setting_checkout_coms</code></strong></td><td><code>Cko-Idempotency-Key</code> header.</td></tr><tr><td><strong><code>payment_setting_paypals</code></strong></td><td><code>PayPal-Request-Id</code> header.</td></tr><tr><td><strong><code>payment_setting_stripes</code></strong></td><td><code>Idempotency-Key</code> header.</td></tr><tr><td><strong><code>payment_setting_gift_cards</code></strong></td><td>Not applicable — no external provider is called.</td></tr><tr><td><strong><code>payment_setting_manuals</code></strong></td><td>Not applicable — no external provider is called.</td></tr><tr><td><strong><code>payment_setting_externals</code></strong></td><td><code>X-CommerceLayer-Idempotency-Key</code> header.</td></tr></tbody></table>

{% hint style="info" %}
[Payment links](/core-api-reference/2026-05/payment_links.md) and [payment wallets](/core-api-reference/2026-05/payment_wallets.md) are not transactions and don't take part in this inheritance — each derives its key from its own token and request payload, so an identical repeat on the same link or wallet is deduplicated by that key instead, where the provider accepts one.
{% endhint %}

## Gateway options

Like [payment sessions](/core-api-reference/2026-05/payment_sessions.md#gateway-options), some transaction types accept an `options` object passed straight through to the payment provider when the transaction is created — useful for gateway parameters not otherwise modeled by Commerce Layer (e.g. a Braintree `descriptor` or `orderId` on a capture or refund).

{% hint style="warning" %}
Whether options are supported, and which keys are accepted, vary by payment setting type and transaction type — not every gateway honors it on every action (for example, Braintree does not support options on voids). The reconciliation reference and the transaction amount can never be overridden via options, regardless of what's passed.
{% endhint %}

{% hint style="info" %}
The `options` attribute can only be set using [integration](/core/api-credentials.md#integration) API credentials — sales channels cannot supply it. Options and `response_data` are also hidden from sales channel API credentials when reading a transaction — only integration API credentials can see them in the response. Instead, `request_data` is unaffected and remains visible to every credential type.
{% endhint %}

## Internal version

Like payment sessions, wallets, and links, an individual transaction can force a specific supported [internal payload version](/core-api-reference/2026-05/payment_settings.md#per-request-overrides) for its own creation request via the `_internal_version` trigger attribute — for example, pinning the exact payload variant used to build a capture or refund, independent of what the payment setting is configured with by default.

{% hint style="info" %}
Unlike `options`, it's available to sales channel API credentials too.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.commercelayer.io/core-api-reference/2026-05/payment_transactions.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
