POS Payment Methods
Provide a payment method the cash register can use, fulfilled by your app.
A POS payment method is what the cashier presses at the register. Most are handled by the ERP itself — cash goes straight into the till. But a payment method can also be handed to an app: the ERP places a payment order, something external fulfils it, and the receipt only closes once the result comes back.
That "something external" is a payment consumer. The VARIO local-link is one — it drives card terminals in the shop. An app can be another.
This page describes the platform mechanism. The reference implementation of a consumer today is the local-link (card terminals over the local network). If you are building an app-side consumer, expect to work directly against the REST resources described here.
The five pieces
Payment backend
Who is responsible: SELF (the ERP) or APP (you)
Payment method
The button on the register — CASH or CARD, pointing at a backend
Payment consumer
The worker that fulfils orders, with a heartbeat
Payment rule
Routes an order to a consumer, based on method, operation, register or sales channel
Payment order
One unit of work, identified by an externalPaymentId
Registering the backend
There is no framework migration helper here — create the backend through the API, with the same identifying fields as every other app backend:
{
"label": "My Terminal App",
"type": "APP",
"appId": "<your-app-identifier>",
"placePayment": true,
"canCancel": true,
"canRefund": true
}placePayment is the switch that matters. It is only meaningful for type: "APP":
With
SELF, the payment is booked directly in the register. WithAPPandplacePayment, an order is placed for an external consumer to fulfil.
canCancel and canRefund advertise what you support. They are not cosmetic — see the warnings below.
The runtime flow
Note what triggers this: the POS adds a document line, not a call to a payment API. The order is a side effect. If no order appears, the POS simply continues — that is the SELF path.
Picking up work
Either be pushed to (the local-link subscribes over a WebSocket) or ask for the next order:
No request body, and it returns at most one order. Send a heartbeat so the ERP can show whether you are alive:
Process one order at a time. A terminal that is already busy rejects a second request, and parallel chains send payments to an occupied device.
Reporting the result
That is the entire write-back surface — status and errorMessage, as query parameters. There is no field for an authorisation code, card reference, receipt number or the actual amount. If you need any of that later (for example a terminal reference required to reverse the payment), you must store it yourself, keyed by externalPaymentId. The local-link keeps its own store for exactly this reason.
The ERP updates the receipt itself once the status is SUCCESSFUL; the POS reloads the document afterwards.
Operations
paymentOperation
Meaning
Extra
PAYMENT
Normal payment
—
CANCEL
Reverse a specific earlier payment
cancelledExternalPaymentId is set
REFUND
Pay money back with no original payment to reverse
—
CANCEL needs the original transaction, so you must be able to find your own record of it. REFUND stands alone.
What to watch out for
Never report a payment as failed because reporting failed. If the card was charged and the status call then errors, retrying the report is correct — retrying the payment is not, and reporting REJECTED tells the register the customer did not pay. That is how double charges happen. Retry the status call, treat 4xx as final, and log loudly if you have to give up: the customer has been charged and the order will sit unresolved.
An order can be cancelled while you are working on it. The cashier can give up and set it to CANCELLED. If the terminal then completes anyway, you have a charged payment against a cancelled order — detect it, don't overwrite the status, and flag it for manual reconciliation. Watch the order while you process it so you can abort at the device.
Aborting is a request, not a command. Once a terminal reaches the point of no return it may ignore the abort and complete normally. Handle the late success.
Timeouts don't line up. The POS gives up waiting for pickup after about 20 seconds and abandons the payment after about three minutes. A consumer that only polls every 30 seconds will trigger the "payment system not responding" dialog even when it is perfectly healthy. Subscribe to pushes, or poll faster than the pickup timeout.
Capabilities have configuration consequences. A backend with neither canCancel nor canRefund can block the register from being activated at all, because cancelling a receipt then becomes impossible — and it prevents splitting a receipt across several card payments. Declare honestly, but understand the cost.
A rule is required. A placePayment backend with no matching payment rule fails at payment time, not at configuration time — the cashier sees "no payment consumer could be determined". Ship setup instructions, or check for a rule during install.
There is no idempotency key. externalPaymentId is the only anchor you get, on both the pickup and the status call. Guard against processing the same order twice yourself.
Fiscal rules interrupt you. Adding a payment line can come back asking for a decision (signature-device handling under German cash-register law). If you drive the POS side, handle that instead of treating it as a failure — otherwise cancellations with partial payments fail silently.
Last updated
Was this helpful?