HomeDocumentationCashier & POS API
Partner integration

Cashier & POS API

A stable server-to-server contract for cash registers, kiosks, order-management systems, and multi-merchant commerce platforms.

Keep credentials off the register

A cashier client calls your backend; your backend calls XRPay with a secret key or Connect access token. Never embed a secret credential in browser, mobile, or POS-extension code.

Integration flow

1

Create

Send an integer minor-unit amount, external order ID, register context, and a durable idempotency key.

2

Present

Display the returned QR image or open the hosted action while the order remains unpaid.

3

Confirm

Verify the signed webhook, deduplicate its stable event ID, then retrieve the intent if needed.

4

Reconcile

Record XRPay as an external tender only after succeeded, preserving the intent and provider references.

Create a payment intent

Amounts are exact integer minor units: 1250 USD means USD 12.50, while 1250 JPY means JPY 1,250. Reuse an idempotency key only for the same logical request.

BASH
curl https://api.xrpay.it/api/v1/payment-intents \
  -H 'Authorization: Bearer sk_test_...' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: store-42-order-1008-attempt-1' \
  -d '{
    "amount_minor": 1250,
    "currency": "USD",
    "external_order_id": "order-1008",
    "integration_type": "pos",
    "location_id": "store-42",
    "terminal_id": "register-3",
    "operator_id": "employee-91"
  }'

Persist the returned id, external_order_id, status, amount, currency, location, terminal, and idempotency key with the cashier order. The complete schemas and error responses are in the OpenAPI 3.1 contract.

Cashier status behavior

StatusRequired behavior
requires_customer_actionDisplay next_action.qr_image or open hosted_url. Keep the order unpaid.
processingLock the tender attempt and continue reconciliation.
succeededRecord an external tender and close the order.
failed / expiredRelease the attempt and let the operator retry with a new idempotency key.
canceledThe unpaid attempt was canceled. Still accept a later authoritative event at the payment boundary.

Deterministic sandbox

Test credentials do not submit canonical cashier intents to a ledger. Create an intent, then POST /api/v1/payment-intents/{id}/simulate with {"outcome":"succeeded"}, failed, or expired. Exercise cancellation, retry, duplicate delivery, network loss, and full/partial refund paths before certification.

Signed, durable webhooks

Register a public HTTPS URL with POST /api/v1/webhooks using the test or live credential mode it should receive. Verify X-XRPay-Signature over the exact raw request bytes, durably record the body before returning 2xx, and deduplicate on the stable event id. The required top-level livemode flag provides an additional environment check.

JAVASCRIPT
import crypto from "node:crypto";

export function verifyXRPay(rawBody, signature, secret) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  const left = Buffer.from(expected);
  const right = Buffer.from(signature || "");
  return left.length === right.length &&
    crypto.timingSafeEqual(left, right);
}

Inspect failures with GET /api/v1/webhook-deliveries?status=failed and replay a delivery with POST /api/v1/webhook-deliveries. Payment and refund failures are retried with exponential backoff.

Refunds and platforms

Create a full or partial refund with POST /api/v1/refunds and an idempotency key. Pending refunds reserve the refundable balance, preventing concurrent over-refunds. Live non-custodial refunds require merchant approval and become final only at completed.

Multi-merchant platforms use OAuth 2.0 Authorization Code with PKCE. Request only the required checkout, transaction, refund, and webhook scopes; every resource remains isolated to its connected merchant and live/test mode.

Related