Payment Solution — Client API (1.0.0)

Download OpenAPI specification:

Payin, payout, refund and transaction lookup over a brand API key.

The client API is how a merchant backend moves money through the Payment Solution platform. Five endpoints, all under /v1, all authenticated by a brand API key.

Transaction types

Money coming in is a payin (POST /v1/payins); money going out is a payout (POST /v1/payouts). A refund is client-initiated and draws against a completed payin's refundable remainder. A reversal is provider-initiated compensation — a returned payout, a chargeback — which can only ever be observed, never requested. Refunds and reversals draw against the same remainder, so do not model them as one thing.

Shape of every call

  • Authorization: Bearer psk_… (or X-Api-Key: psk_…) on every request.
  • Idempotency-Key is required on all three POSTs.
  • A fresh operation answers 201; an idempotent replay of the same key with the same payload answers 200 with the stored result. Both are success and both carry the same body shape.
  • Errors are always {status, code, message, details, timestamp} with a stable machine-readable code.

Statuses

CREATED → PENDING | PROCESSING → COMPLETED | FAILED | CANCELLED | EXPIRED, with IN_DOUBT as the ambiguity state. COMPLETED, FAILED, CANCELLED and EXPIRED are terminal — see integration-guide.md for the two deliberate exceptions and what each status means for a client.

Callbacks

Status changes a client could not have learned from its own API call are delivered as signed HTTP POSTs to the brand's callback URL. They are described under webhooks at the bottom of this document; the signature recipe and a worked example are in integration-guide.md.

Support

Where this document says contact support, use the support channel you were given during onboarding.

Operations

Moving money — payins, payouts and refunds.

Start a payin

Creates a payin and calls the routed PSP. What comes back depends on the provider and method:

  • a hosted-page provider answers PENDING with a redirectUrl — send the end user there;
  • a pay-to-address provider answers PENDING with paymentInstructions and no redirectUrl;
  • a synchronous refusal answers FAILED with error;
  • an ambiguous provider failure (timeout, crash mid-call) answers IN_DOUBT — the platform reconciles it and sends a callback. It is never terminal-failed, because the PSP may be executing it.

Validation order matters, because everything below happens before a transaction row exists, so a refusal costs nothing: bean validation → idempotency → routing → required customer fields → required request fields. Only then is a row persisted and the PSP called.

Authorizations:
bearerApiKeyheaderApiKey
header Parameters
Idempotency-Key
required
string [ 1 .. 64 ] characters ^[A-Za-z0-9_-]{1,64}$
Examples: order-8891-payin-01H9

Required on every POST. Generate it once per business intent — before the call, and durably — and reuse it on every retry of that intent. A key minted after a successful response can never help you.

Same key + same payload = replay: 200 with the stored transaction. Same key + different payload = 409 IDEMPOTENCY_CONFLICT.

Unique per brand; keys are compared exactly after trimming surrounding whitespace.

Request Body schema: application/json
required
amount
required
number (Money) decimal places <= 2 > 0

A decimal money amount. Sent as a JSON number (a JSON string parses too); returned as a JSON number that preserves the stored scale (25.00, not 25). At most 17 integer digits and 2 fraction digits.

A client that does arithmetic on these should read them as decimal strings from the raw body rather than through a double — and must never re-serialize a callback body it intends to verify.

currency
required
string^[A-Z]{3}$

ISO-4217, uppercase.

method
required
string [ 1 .. 32 ] characters ^[A-Z0-9_]+$

A payment method token from GET /v1/payment-methods. The set is not fixed by the platform — it is whatever the brand's configured adapters drive (CARD, BANK_WIRE, BANK_TRANSFER, CRYPTO, …). Never hard-code it.

provider
string or null <= 64 characters

Pin the operation to one provider code. Omitted = the platform routes by the brand's method rules, which is the normal case.

userRef
required
string [ 1 .. 64 ] characters

Your end-user reference. The platform holds no user accounts, so this is the only identity a transaction carries. Echoed on every view and callback.

clientTxRef
string or null <= 64 characters

Your own reference for this operation, echoed back everywhere. Note that this is not the idempotency mechanism — it is not checked for uniqueness. Use Idempotency-Key for that.

country
string or null^[A-Z]{2}$

ISO-3166-1 alpha-2. Required when requiredRequestFields.payin for the routed option contains country; otherwise it only narrows routing.

returnUrl
string or null <= 1024 characters

Where the PSP sends the end user back after the hosted page. Required when requiredRequestFields.payin contains returnUrl — several adapters cannot open a hosted page without one, and omitting it is refused with 400 MISSING_REQUEST_FIELDS rather than costing you a FAILED transaction.

object (CustomerDetails)

End-user data forwarded to the PSP, in-flight only — the platform keeps no end-user PII at rest and holds no end-user accounts.

Which of these are required depends on the routed provider, the method and the currency. Read requiredCustomerFields from GET /v1/payment-methods and build the form from it; a missing one is refused with 400 MISSING_CUSTOMER_FIELDS, listing the field names in details.

Blank strings count as absent.

Responses

Request samples

Content type
application/json
Example
{
  • "amount": 25,
  • "currency": "EUR",
  • "method": "CARD",
  • "userRef": "user-10427",
  • "clientTxRef": "order-8891",
  • "country": "DE",
  • "customer": {
    }
}

Response samples

Content type
application/json
{
  • "id": "tx_4bQ9xR2mKpLvN7sTfWgHdYcZ",
  • "type": "PAYIN",
  • "status": "CREATED",
  • "method": "CARD",
  • "provider": "EXAMPLEPSP",
  • "amount": 25,
  • "currency": "EUR",
  • "settledAmount": 0,
  • "settledCurrency": "string",
  • "userRef": "user-10427",
  • "clientTxRef": "order-8891",
  • "country": "DE",
  • "redirectUrl": "string",
  • "paymentInstructions": {
    },
  • "destinationMasked": "DE00…0000",
  • "parentTransactionId": "string",
  • "error": {
    },
  • "createdAt": "2026-09-04T09:12:04.771200Z",
  • "completedAt": "2019-08-24T14:15:22Z"
}

Send a payout

Executes a payout immediately — the platform performs no approval step of its own, because approval already happened on the client's side before the call.

The destination rule is the whole endpoint. For a normal method, destination is required; it is stored encrypted under the brand key and only a head+tail mask (destinationMasked) is ever readable afterwards. For a method flagged hostedPayout on GET /v1/payment-methods, the PSP collects the destination on its own page: send no destination and no bankCode, and send the end user to the redirectUrl on the answer. Sending one anyway is refused (DESTINATION_NOT_ACCEPTED) rather than quietly stored and ignored — this is what makes a card payout possible without a PAN ever reaching the platform.

Where the account number does not route itself — a UK sort code on Faster Payments — send bankCode beside destination. Whether that applies depends on method and currency: read requiresDestinationBankCode per (method, currency) from GET /v1/payment-methods.

Authorizations:
bearerApiKeyheaderApiKey
header Parameters
Idempotency-Key
required
string [ 1 .. 64 ] characters ^[A-Za-z0-9_-]{1,64}$
Examples: order-8891-payin-01H9

Required on every POST. Generate it once per business intent — before the call, and durably — and reuse it on every retry of that intent. A key minted after a successful response can never help you.

Same key + same payload = replay: 200 with the stored transaction. Same key + different payload = 409 IDEMPOTENCY_CONFLICT.

Unique per brand; keys are compared exactly after trimming surrounding whitespace.

Request Body schema: application/json
required
amount
required
number (Money) decimal places <= 2 > 0

A decimal money amount. Sent as a JSON number (a JSON string parses too); returned as a JSON number that preserves the stored scale (25.00, not 25). At most 17 integer digits and 2 fraction digits.

A client that does arithmetic on these should read them as decimal strings from the raw body rather than through a double — and must never re-serialize a callback body it intends to verify.

currency
required
string^[A-Z]{3}$
method
required
string [ 1 .. 32 ] characters ^[A-Z0-9_]+$

A method token from GET /v1/payment-methods whose row has supportsPayouts: true. A PSP's payin and payout method sets are often different.

provider
string or null <= 64 characters
userRef
required
string [ 1 .. 64 ] characters
clientTxRef
string or null <= 64 characters
country
string or null^[A-Z]{2}$
destination
string or null <= 256 characters

The account being paid — an IBAN, a domestic account number, a wallet account, a crypto address. What it is for a given (method, currency) is published as destinationKind on GET /v1/payment-methods; label your form from that rather than assuming a shape.

Stored encrypted under the brand key; only destinationMasked (head + tail) is readable afterwards.

Omit it only for a method flagged hostedPayout, where the PSP collects the destination on its own page and answers with a redirectUrl. Any other method without one is refused (400 DESTINATION_REQUIRED); a hosted method with one is also refused (400 DESTINATION_NOT_ACCEPTED), because the adapter would drop it while the platform had already encrypted and stored whatever you sent.

bankCode
string or null <= 16 characters ^[0-9][0-9 -]*$

The routing code that completes destination where the account number does not route itself — a UK sort code on Faster Payments. Digits, optionally separated by spaces or dashes as a human would type them (12-34-56); the adapter normalizes the separators away.

Send it exactly when requiresDestinationBankCode is true for this (method, currency). Missing where required is 400 DESTINATION_BANK_CODE_REQUIRED; present on a hosted method is 400 DESTINATION_NOT_ACCEPTED, since half a destination is still a destination.

Stored encrypted and never echoed back — a sort code names a branch, not an account, so there is nothing in it worth displaying.

returnUrl
string or null <= 1024 characters

Where the end user returns from a hosted payout page.

object (CustomerDetails)

End-user data forwarded to the PSP, in-flight only — the platform keeps no end-user PII at rest and holds no end-user accounts.

Which of these are required depends on the routed provider, the method and the currency. Read requiredCustomerFields from GET /v1/payment-methods and build the form from it; a missing one is refused with 400 MISSING_CUSTOMER_FIELDS, listing the field names in details.

Blank strings count as absent.

Responses

Request samples

Content type
application/json
Example
{
  • "amount": 120,
  • "currency": "EUR",
  • "method": "BANK_TRANSFER",
  • "userRef": "user-10427",
  • "clientTxRef": "withdrawal-4412",
  • "country": "DE",
  • "destination": "DE00000000000000000000",
  • "customer": {
    }
}

Response samples

Content type
application/json
{
  • "id": "tx_4bQ9xR2mKpLvN7sTfWgHdYcZ",
  • "type": "PAYIN",
  • "status": "CREATED",
  • "method": "CARD",
  • "provider": "EXAMPLEPSP",
  • "amount": 25,
  • "currency": "EUR",
  • "settledAmount": 0,
  • "settledCurrency": "string",
  • "userRef": "user-10427",
  • "clientTxRef": "order-8891",
  • "country": "DE",
  • "redirectUrl": "string",
  • "paymentInstructions": {
    },
  • "destinationMasked": "DE00…0000",
  • "parentTransactionId": "string",
  • "error": {
    },
  • "createdAt": "2026-09-04T09:12:04.771200Z",
  • "completedAt": "2019-08-24T14:15:22Z"
}

Refund a completed payin

Refunds a COMPLETED payin, fully or partially. Omit amount to refund the full remaining refundable balance.

The refund is not routed — it follows its parent payin to the same PSP product that took the money, using the route settings snapshotted on the parent row.

The refundable remainder is enforced under a lock on the parent, so concurrent refunds cannot overdraw it (REFUND_EXCEEDS_REMAINING). A payin under an active dispute is refused outright (DISPUTED) — this check runs first, because refunding a disputed payin is the classic double-credit loss and every other refusal message would be a lie about the real reason.

Whether a refund settles inside this response or later over a callback is a per-provider property. Do not assume either; read status and wait for a callback if it is not terminal.

Authorizations:
bearerApiKeyheaderApiKey
header Parameters
Idempotency-Key
required
string [ 1 .. 64 ] characters ^[A-Za-z0-9_-]{1,64}$
Examples: order-8891-payin-01H9

Required on every POST. Generate it once per business intent — before the call, and durably — and reuse it on every retry of that intent. A key minted after a successful response can never help you.

Same key + same payload = replay: 200 with the stored transaction. Same key + different payload = 409 IDEMPOTENCY_CONFLICT.

Unique per brand; keys are compared exactly after trimming surrounding whitespace.

Request Body schema: application/json
required
transactionId
required
string [ 1 .. 40 ] characters

Public id of the COMPLETED payin to refund. Anything else — another type, another status, another brand — is refused.

Money (number) or null

Omitted = the full remaining refundable amount (the payin's settled amount, less every refund and reversal already claimed against it).

Responses

Request samples

Content type
application/json
Example
{
  • "transactionId": "tx_4bQ9xR2mKpLvN7sTfWgHdYcZ"
}

Response samples

Content type
application/json
{
  • "id": "tx_4bQ9xR2mKpLvN7sTfWgHdYcZ",
  • "type": "PAYIN",
  • "status": "CREATED",
  • "method": "CARD",
  • "provider": "EXAMPLEPSP",
  • "amount": 25,
  • "currency": "EUR",
  • "settledAmount": 0,
  • "settledCurrency": "string",
  • "userRef": "user-10427",
  • "clientTxRef": "order-8891",
  • "country": "DE",
  • "redirectUrl": "string",
  • "paymentInstructions": {
    },
  • "destinationMasked": "DE00…0000",
  • "parentTransactionId": "string",
  • "error": {
    },
  • "createdAt": "2026-09-04T09:12:04.771200Z",
  • "completedAt": "2019-08-24T14:15:22Z"
}

Transactions

Reading back what happened.

Read a transaction

The reconciliation route. Use it when a callback never arrived, when a POST failed in transport and you never learned its outcome, or after a restart mid-flight — ask the platform rather than guessing.

Scoped to the brand of the presented API key: a transaction belonging to another brand answers 404 NOT_FOUND, exactly as an unknown id does.

Works for every transaction the brand owns, including REVERSAL rows the platform created on its own from a PSP notification.

Authorizations:
bearerApiKeyheaderApiKey
path Parameters
publicId
required
string
Examples: tx_4bQ9xR2mKpLvN7sTfWgHdYcZ

The transaction's public id, as returned in id (tx_…).

Responses

Response samples

Content type
application/json
{
  • "id": "tx_4bQ9xR2mKpLvN7sTfWgHdYcZ",
  • "type": "PAYIN",
  • "status": "CREATED",
  • "method": "CARD",
  • "provider": "EXAMPLEPSP",
  • "amount": 25,
  • "currency": "EUR",
  • "settledAmount": 0,
  • "settledCurrency": "string",
  • "userRef": "user-10427",
  • "clientTxRef": "order-8891",
  • "country": "DE",
  • "redirectUrl": "string",
  • "paymentInstructions": {
    },
  • "destinationMasked": "DE00…0000",
  • "parentTransactionId": "string",
  • "error": {
    },
  • "createdAt": "2026-09-04T09:12:04.771200Z",
  • "completedAt": "2019-08-24T14:15:22Z"
}

Configuration

What this brand can currently accept.

What this brand currently accepts

One row per enabled (provider config, method, currency, country) route the brand has. Build your payment form entirely from this list — limits, required customer fields, which directions each option supports, and what a payout destination for it actually is. Nothing about any specific PSP belongs in client code.

Rows come from your brand's current routing configuration, so the list changes without a release. Re-read it rather than caching it indefinitely.

An empty list means the brand currently has nothing matching: no enabled provider configuration, no enabled routes on one, or no route matching the filters you passed. That is a 200 with [], not an error.

Authorizations:
bearerApiKeyheaderApiKey
query Parameters
currency
string
Examples: currency=EUR

Exact ISO-4217 match. Omitted = every currency.

country
string
Examples: country=DE

ISO-3166-1 alpha-2. A row whose own country is null is country-agnostic and matches every filter value. Omitted = no country filtering at all.

Responses

Response samples

Content type
application/json

Illustrative values — provider codes, labels and limits are whatever your own routing is configured with. Note the shape rather than the contents: CARD here takes payins only and needs no customer data, while CRYPTO works in both directions, demands an e-mail, and is addressed to a chain address rather than a bank account.

[
  • {
    },
  • {
    }
]

A transaction changed status Webhook

Delivered to the brand's configured callback URL for every status transition the client did not itself cause synchronously — a notification from the provider, the platform's own reconciliation of an unresolved transaction, or a manual intervention. A status the client already learned from its own API response produces no callback.

Answer 2xx to anything whose signature verified, including duplicates, stale deliveries and transactions you do not recognise. Anything else buys retries (and eventually a DEAD delivery) for something a retry cannot fix. Refuse only what fails verification, with 401 and no explanation.

See integration-guide.md § "Verifying a callback signature".

Authorizations:
bearerApiKeyheaderApiKey
header Parameters
X-PS-Signature
required
string
Examples: t=1788528751,v1=2629194c7ff61de00be9dfcf45b480a44b758ff2ae3efa89466382f943b5f20f

t=<unix seconds>,v1=<hex HMAC-SHA256> — see the guide.

X-PS-Event
required
string

The event name, identical to event in the body.

Value: "transaction.status_changed"
X-PS-Delivery
required
string
Examples: 84213

Delivery id — the same value on every retry of this delivery. Deduplicate on it.

Request Body schema: application/json
required
event
required
string
Enum: "transaction.status_changed" "transaction.reversed"

Identical to the X-PS-Event header. Ignore events you do not know rather than failing — but do not silently funnel transaction.reversed into a status_changed handler.

transactionId
required
string
parentTransactionId
required
string or null

The transaction this one derives from — the refunded payin, or the movement a reversal compensates. Present on every payload that has a parent, not only on reversals.

type
required
string (TransactionType)
Enum: "PAYIN" "PAYOUT" "REFUND" "REVERSAL"
  • PAYIN — money in, created by POST /v1/payins.
  • PAYOUT — money out, created by POST /v1/payouts.
  • REFUND — client-initiated return of a completed payin.
  • REVERSAL — PSP-initiated compensation (returned payout, chargeback, recall, fraud claim, or a won chargeback moving the money back). The platform creates these on its own; a client can only observe them.
status
required
string (TransactionStatus)
Enum: "CREATED" "PENDING" "PROCESSING" "IN_DOUBT" "COMPLETED" "FAILED" "CANCELLED" "EXPIRED"
  • CREATED — the row exists, the PSP has not answered yet. Almost never observed by a client, since a POST returns only after the PSP call; it survives only across a crash between persisting the row and applying the result.
  • PENDING — waiting on the end user (a hosted page, or funds sent to a quoted address).
  • PROCESSING — the PSP is executing it. Nothing for the client to do but wait for a callback.
  • IN_DOUBT — an ambiguous provider failure: the PSP may or may not have accepted the operation. Never terminal. The platform reconciles it by querying the PSP, or a late webhook resolves it. Do not re-send the operation; retry the same Idempotency-Key or poll GET /v1/transactions/{publicId}.
  • COMPLETED — settled. Terminal and final.
  • FAILED — the PSP refused. Terminal.
  • CANCELLED — cancelled before completion. Terminal and final.
  • EXPIRED — the platform's housekeeping guess that an abandoned PENDING payin will never be paid. Terminal, but it is our guess rather than the PSP's answer, so a late PSP truth outranks it: EXPIRED may still become COMPLETED or FAILED.

The other exception to terminality: for some methods a PAYIN that FAILED on a PSP answer carries a provider-declared provisional window during which the PSP may still revise it to COMPLETED (slow open-banking rails do this). It is narrow on three axes at once — which adapter, which method, and for how long — and a client learns of it only by receiving the callback.

method
required
string
amount
required
number (Money) decimal places <= 2 > 0

A decimal money amount. Sent as a JSON number (a JSON string parses too); returned as a JSON number that preserves the stored scale (25.00, not 25). At most 17 integer digits and 2 fraction digits.

A client that does arithmetic on these should read them as decimal strings from the raw body rather than through a double — and must never re-serialize a callback body it intends to verify.

currency
required
string
settledAmount
required
number or null
settledCurrency
required
string or null
provider
required
string or null
userRef
required
string
clientTxRef
required
string or null
errorCode
required
string or null
errorMessage
required
string or null
occurredAt
required
string <date-time>

When the transition happened. The ordering key — arrival order is not.

Responses

Request samples

Content type
application/json
{
  • "event": "transaction.status_changed",
  • "transactionId": "tx_4bQ9xR2mKpLvN7sTfWgHdYcZ",
  • "parentTransactionId": null,
  • "type": "PAYIN",
  • "status": "COMPLETED",
  • "method": "CARD",
  • "amount": 25,
  • "currency": "EUR",
  • "settledAmount": 25,
  • "settledCurrency": "EUR",
  • "provider": "EXAMPLEPSP",
  • "userRef": "user-10427",
  • "clientTxRef": "order-8891",
  • "errorCode": null,
  • "errorMessage": null,
  • "occurredAt": "2026-09-04T09:12:31.482913Z"
}

Money moved backwards Webhook

A deliberately distinct event name for a REVERSAL transaction: money that moved and then moved back — a payout the bank returned, or a chargeback / recall / fraud claim on a settled payin.

It is not a REFUND: a refund is client-initiated and draws against a refundable budget; a reversal is inbound and nobody asked us for it. The event name differs precisely so that an unprepared client fails loudly at integration time instead of dropping the most financially significant message the platform sends into an else branch.

Every transition of the reversal row — its creation and its later resolution alike — arrives under this same name. parentTransactionId points at the movement being compensated; its direction tells you which kind of reversal this is (a compensated PAYIN is a chargeback, a compensated PAYOUT is a returned payout).

Authorizations:
bearerApiKeyheaderApiKey
header Parameters
X-PS-Signature
required
string
X-PS-Event
required
string
Value: "transaction.reversed"
X-PS-Delivery
required
string
Request Body schema: application/json
required
event
required
string
Enum: "transaction.status_changed" "transaction.reversed"

Identical to the X-PS-Event header. Ignore events you do not know rather than failing — but do not silently funnel transaction.reversed into a status_changed handler.

transactionId
required
string
parentTransactionId
required
string or null

The transaction this one derives from — the refunded payin, or the movement a reversal compensates. Present on every payload that has a parent, not only on reversals.

type
required
string (TransactionType)
Enum: "PAYIN" "PAYOUT" "REFUND" "REVERSAL"
  • PAYIN — money in, created by POST /v1/payins.
  • PAYOUT — money out, created by POST /v1/payouts.
  • REFUND — client-initiated return of a completed payin.
  • REVERSAL — PSP-initiated compensation (returned payout, chargeback, recall, fraud claim, or a won chargeback moving the money back). The platform creates these on its own; a client can only observe them.
status
required
string (TransactionStatus)
Enum: "CREATED" "PENDING" "PROCESSING" "IN_DOUBT" "COMPLETED" "FAILED" "CANCELLED" "EXPIRED"
  • CREATED — the row exists, the PSP has not answered yet. Almost never observed by a client, since a POST returns only after the PSP call; it survives only across a crash between persisting the row and applying the result.
  • PENDING — waiting on the end user (a hosted page, or funds sent to a quoted address).
  • PROCESSING — the PSP is executing it. Nothing for the client to do but wait for a callback.
  • IN_DOUBT — an ambiguous provider failure: the PSP may or may not have accepted the operation. Never terminal. The platform reconciles it by querying the PSP, or a late webhook resolves it. Do not re-send the operation; retry the same Idempotency-Key or poll GET /v1/transactions/{publicId}.
  • COMPLETED — settled. Terminal and final.
  • FAILED — the PSP refused. Terminal.
  • CANCELLED — cancelled before completion. Terminal and final.
  • EXPIRED — the platform's housekeeping guess that an abandoned PENDING payin will never be paid. Terminal, but it is our guess rather than the PSP's answer, so a late PSP truth outranks it: EXPIRED may still become COMPLETED or FAILED.

The other exception to terminality: for some methods a PAYIN that FAILED on a PSP answer carries a provider-declared provisional window during which the PSP may still revise it to COMPLETED (slow open-banking rails do this). It is narrow on three axes at once — which adapter, which method, and for how long — and a client learns of it only by receiving the callback.

method
required
string
amount
required
number (Money) decimal places <= 2 > 0

A decimal money amount. Sent as a JSON number (a JSON string parses too); returned as a JSON number that preserves the stored scale (25.00, not 25). At most 17 integer digits and 2 fraction digits.

A client that does arithmetic on these should read them as decimal strings from the raw body rather than through a double — and must never re-serialize a callback body it intends to verify.

currency
required
string
settledAmount
required
number or null
settledCurrency
required
string or null
provider
required
string or null
userRef
required
string
clientTxRef
required
string or null
errorCode
required
string or null
errorMessage
required
string or null
occurredAt
required
string <date-time>

When the transition happened. The ordering key — arrival order is not.

Responses

Request samples

Content type
application/json
{
  • "event": "transaction.reversed",
  • "transactionId": "tx_7hK2mNpQrStUvWxYzAbCdEf",
  • "parentTransactionId": "tx_4bQ9xR2mKpLvN7sTfWgHdYcZ",
  • "type": "REVERSAL",
  • "status": "COMPLETED",
  • "method": "CARD",
  • "amount": 25,
  • "currency": "EUR",
  • "settledAmount": 25,
  • "settledCurrency": "EUR",
  • "provider": "EXAMPLEPSP",
  • "userRef": "user-10427",
  • "clientTxRef": "order-8891",
  • "errorCode": null,
  • "errorMessage": null,
  • "occurredAt": "2026-09-18T14:03:02.117400Z"
}