PSP payment ( psp_payment )

A psp_payment is Ormuz's record of a payment that went through a payment service provider (PSP). It carries the expected amount, amount actually received, payment method, and collection status — and acts as the bridge between the provider event and platform financial objects.

Role

Every incoming payment handled through a PSP — whether from a Stripe webhook, a bank transfer detected through open banking, or a manual record — is represented as a psp_payment. It documents raw collection: how much was received, by which method, and which order or receivable it relates to.

The psp_payment then feeds reconciliation: once applied through reconcile_payment, it updates the settlement_status of the relevant invoices and triggers recalculation of the buyer's receivable for the buyer.

Identifier and structure

Every PSP payment has a stable identifier prefixed with psp_.

JSON
"psp_payment":{20 items
"object":"psp_payment"
"id":"psp_7e3b9f2a1c4d8e5f"
"merchant_id":"mer_1a2b3c4d5e6f7a8b"
"buyer_id":"cmp_3a8f1d9c2b4e7f6a"
"checkout_session_id":null
"order_id":null
"invoice_id":"inv_4a7b2e9f1c3d8a5e"
"payment_id":null
"basis":"invoice"
"basis_snapshot_at":null
"basis_snapshot_amount":null
"source_reference":"pi_3Nx8kLInvalidExample"
"metadata":{}0 items
"amount":120000
"amount_received":120000
"currency":"eur"
"payment_method":"sepa_debit"
"status":"succeeded"
"created_at":"2026-06-17T12:00:00.000Z"
"updated_at":"2026-06-17T12:05:00.000Z"
}
{
"object": "psp_payment",
"id": "psp_7e3b9f2a1c4d8e5f",
"merchant_id": "mer_1a2b3c4d5e6f7a8b",
"buyer_id": "cmp_3a8f1d9c2b4e7f6a",
"checkout_session_id": null,
"order_id": null,
"invoice_id": "inv_4a7b2e9f1c3d8a5e",
"payment_id": null,
"basis": "invoice",
"basis_snapshot_at": null,
"basis_snapshot_amount": null,
"source_reference": "pi_3Nx8kLInvalidExample",
"metadata": {},
"amount": 120000,
"amount_received": 120000,
"currency": "eur",
"payment_method": "sepa_debit",
"status": "succeeded",
"created_at": "2026-06-17T12:00:00.000Z",
"updated_at": "2026-06-17T12:05:00.000Z"
}

Fields

FieldTypeDescription
idstringPSP payment identifier (prefix `psp_`).
objectstringAlways "psp_payment".
merchant_idstringOwning merchant.
buyer_idstring | nullBuyer company (`cmp_…`). May be derived from the order or invoice when not supplied explicitly.
checkout_session_idstring | nullAssociated payment session (`cs_…`). Optional.
typeenumIntent nature: `payment` or `refund`.
source_psp_payment_idstring | nullParent PSP payment when this is a refund.
amount_refundedintegerAmount of successful child PSP refunds on a payment.
amount_refundableintegerRemaining refundable capacity after successful refunds and active intents.
reasonstring | nullBusiness reason for a refund intent.
order_idstring | nullAssociated order (`ord_…`). Required when `basis = order`.
invoice_idstring | nullAssociated invoice (`inv_…`). Required when `basis = invoice`.
payment_idstring | nullLinked platform payment (`pay_…`). Optional — establishes the bridge to the reconciled payment.
basisenum | nullAttachment basis: `order`, `invoice`, `receivable`, or null.
basis_snapshot_atdatetime | nullReceivable snapshot timestamp (required when `basis = receivable`).
basis_snapshot_amountinteger | nullReceivable amount at snapshot time (required when `basis = receivable`).
source_referencestring | nullProvider reference written after intent creation, for example a Stripe PaymentIntent. It is unique per merchant and is no longer used as the key when creating the draft.
metadataobjectFlat map of `string | number | boolean` scalars, following the API metadata convention.
amountintegerTotal expected amount in cents (> 0).
amount_receivedintegerAmount actually received in cents (≥ 0). Cannot exceed `amount`.
currencystringISO 4217 currency code in lowercase (for example `eur`).
payment_methodstringPayment method used (for example `manual`, `sepa_debit`, `bank_transfer`, `card`).
statusenumStatus: `draft`, `pending`, `partially_funded`, `succeeded`, `failed`, `cancelled`, or `disputed`.
created_atdatetimeCreation date.
updated_atdatetimeLast update date.

Status

The status describes the lifecycle of the payment intent and then its collection state. A PSP payment normally starts in draft : Ormuz has defined the intent, but no provider payment has yet been established. pending instead means that the provider created or accepted the intent and collection is still awaited.

StatusConstraint on amount_receivedDescription
draftamount_received = 0Ormuz intent created before the provider established the payment.
pendingamount_received < amountThe provider established the payment; collection is still awaited.
partially_funded0 < amount_received < amountAmount partially received. Common for bank transfers.
succeededamount_received > 0Payment fully received.
failed—Failed payment attempt.
cancelled—Payment canceled before receipt.
disputed—Open dispute (chargeback or contestation).

Status may be set at creation — useful when the PSP signal has already been processed — or updated through POST /v1/psp-payments/:id. A transition to a status other than pending emits the corresponding event.

Attachment basis

The field basis qualifies the commercial intent of the payment. It determines which linking fields are required or forbidden and provides reconciliation context.

basisRequiredExcluDescription
orderorder_idinvoice_idThe payment is attached to a specific order.
invoiceinvoice_idorder_idThe payment is attached to a specific invoice.
receivablebasis_snapshot_at, basis_snapshot_amountorder_id, invoice_idThe payment covers all or part of the buyer's open receivable. The snapshot captures the receivable at initiation time.
null——No declared basis — free-form use.
`buyer_id` and currency consistency

If buyer_id is supplied together with an order_id or invoice_id, it must match the buyer_id of the order or invoice. Currency must also match. When buyer_id is absent, it is automatically derived from the order or invoice.

Intent idempotency and provider reference

A nouveau psp_payment is first created as an Ormuz intent in status draft, without a provider reference. If a retry attempts to recreate the same draft before any external execution, Ormuz reuses the existing intent when its type, amount, currency, and business attachment key are identical.

source_reference is populated later, when the provider has actually created or identified its object. This reference is unique per merchant and written once; it then correlates provider updates and events to the same PSP payment. It does not replace draft deduplication before the external call.

PSP refunds

A PSP refund is also a persisted intent before the provider call. The node refund_psp_payment creates a psp_payment of type refund with status draft, linked through source_psp_payment_id to an already-successful source PSP payment. The PSP extension then materializes this refund and converges its status with the provider. The parent payment remains succeeded: its amount_refunded field aggregates successful child refunds; there is no parent status refunded.

For credit notes, create_credit_note_psp_refunds resolves the PSP payments that actually funded the relevant invoices, reserves remaining refundable capacity, and creates the corresponding intents. Creation may remain separate per credit note or be aggregated by source PSP payment according to process needs.

An active intent reserves its share of refundable capacity, so two concurrent processes cannot silently commit the same amount. Provider execution remains an explicit step, distinct from creating the Ormuz intent.

In processes

create_psp_payment

This helper creates or reuses an intent psp_payment draft from the supplied business basis. It does not request a provider source_reference reference at creation: that reference is attached later by the capability that actually executes or synchronizes the payment.

Main parameters

JSON
{11 items
"company":"platform.company"
"order":"platform.order"
"invoice":"platform.invoice"
"receivable":"platform.receivable"
"checkout_session":"platform.checkout_session"
"amount":120000
"amount_received":120000
"currency":"eur"
"payment_method":"sepa_debit"
"status":"succeeded"
"source_reference":"pi_3Nx8kL"
}
{
"company": "platform.company",
"order": "platform.order",
"invoice": "platform.invoice",
"receivable": "platform.receivable",
"checkout_session": "platform.checkout_session",
"amount": 120000,
"amount_received": 120000,
"currency": "eur",
"payment_method": "sepa_debit",
"status": "succeeded",
"source_reference": "pi_3Nx8kL"
}

Output

JSON
{1 item
"psp_payment":"platform.psp_payment"
}
{
"psp_payment": "platform.psp_payment"
}

reconcile_payment

Allocates a PSP payment or bank payment to one or more Ormuz receivables according to an allocation proposal. Updates document statuses and receivable according to the received source.

Parameters

JSON
{2 items
"payment":"platform.payment | platform.psp_payment"
"receivable_allocation":"common.receivable_allocation"
}
{
"payment": "platform.payment | platform.psp_payment",
"receivable_allocation": "common.receivable_allocation"
}

Output

JSON
{1 item
"payment_allocation":"platform.payment_allocation"
}
{
"payment_allocation": "platform.payment_allocation"
}

The receivable_allocation is typically produced by the node propose_payment_allocation which identifies candidate invoices for a given amount and company.

Nodes Stripe

Several Stripe integration nodes produce or consume a psp_payment :

NodeRole
stripe.sync_psp_paymentExplicitly synchronizes a Stripe PaymentIntent into its Ormuz PSP payment. Useful for forcing a state refresh without waiting for the webhook.
stripe.create_bank_transfer_payment_intentCreates or reuses a bank-transfer PaymentIntent from an Ormuz PSP payment draft, then converges its state with the provider.
stripe.trigger_sepa_debitTriggers a SEPA direct debit from an Ormuz PSP payment draft and durably associates the Stripe PaymentIntent with that payment.

Events

EventTrigger
psp_payment.createdThe PSP payment was just created.
psp_payment.partially_fundedThe status changed to `partially_funded`.
psp_payment.succeededThe status changed to `succeeded`.
psp_payment.failedThe status changed to `failed`.
psp_payment.cancelledThe status changed to `cancelled`.
psp_payment.disputedThe status changed to `disputed`.

When the PSP payment is created directly with a status other than pending (ex. succeeded), both events psp_payment.created and psp_payment.succeeded are emitted at creation. Later status transitions emit only the event for the new status.