A valid request URL is required to generate request examples{
"provider": "stripe",
"ok": true,
"steps": [
{
"operation": "authorize",
"ok": true,
"detail": "approved pi_3Qk1"
},
{
"operation": "void",
"ok": true,
"detail": "voided pi_3Qk1"
}
]
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123,
"fieldErrors": [
{
"field": "<string>",
"message": "<string>"
}
]
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}Run the card-terminal round-trip self-test
Commands the resolved payment provider through ONE reserve-and-release probe: authorize -> void. It exists so a human on the till’s peripherals screen can prove the terminal is wired; an agent should reach for it ONLY on an explicit operator request to test the terminal, never to ‘check’ anything. Use getPaymentTerminalConfig for that. IT SETTLES NOTHING: the probe reserves amountCents and releases it again, and no code path here can capture (solya-pos#959), so a green run leaves no payment on the merchant’s statement. What that still proves: the credentials authenticate, the provider is reachable, the request mapping is accepted, the reader engages and approves a card, and the release path works. What it no longer proves: the capture/settlement leg, which cannot be exercised without moving money. WHY it is still avoid: (1) it talks to the provider with the deployment’s REAL credentials, so with a live Adyen POI it engages the physical reader mid-shift and asks a person to present a card. It is nonetheless REFUSED with a 422 in three cases, before anything is sent to the provider: the operator explicitly set psp.enabled to false (PSP_DISABLED); the selected provider has no credentials here (PSP_NOT_CONFIGURED); or the credentials are not test ones (PSP_NOT_SANDBOX — a Stripe secret without an sk_test_/rk_test_ prefix, or an Adyen Terminal API URL that is not a *-test.adyen.com host). Sandbox mode is now enforced in code, not merely assumed from the environment. NOT IDEMPOTENT: every call issues a FRESH authorization. There is no read-back endpoint for the result, so a call that times out must NOT be retried blindly — the authorization may already have been taken (and, if the void never landed, may still be held). Ask the operator to inspect the provider dashboard instead. Body: send at least {}; amountCents (integer minor units, default 100 = 1.00, max 1 000 000) and currency (3-letter ISO 4217, default EUR) override the reserved amount — keep it small, it is a real hold on a real card. Unknown keys are rejected. Always inspect ok (true only when both steps passed); steps[].detail is free-form engineering text, never a machine field. ERROR-VOCABULARY TRAP (solya-pos#955): the 422 codes this route returns (“PSP_DISABLED”, “PSP_NOT_CONFIGURED”, “PSP_NOT_SANDBOX”) are NOT members of the kernel ResultCode enum the shared ErrorResponse component documents. Match those string literals rather than expecting a documented code.
A valid request URL is required to generate request examples{
"provider": "stripe",
"ok": true,
"steps": [
{
"operation": "authorize",
"ok": true,
"detail": "approved pi_3Qk1"
},
{
"operation": "void",
"ok": true,
"detail": "voided pi_3Qk1"
}
]
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123,
"fieldErrors": [
{
"field": "<string>",
"message": "<string>"
}
]
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}Authorizations
Authorization: Bearer <token>. Accepts EITHER a Keycloak access token (scopes-in-token) OR an opaque POS session token; both resolve to the same pos.* scope vocabulary the route guards enforce.
Body
Response
The round-trip report. A 200 means the probes RAN, not that they passed — read ok.

