RevKeen Docs

Payments API

Initiate, track, cancel, refund, and void terminal payments

The Terminal Payments API lets you initiate card-present payments on a terminal device and manage the full payment lifecycle.

Payment Lifecycle

requested → in_progress → approved / declined / error / timed_out
                        → cancelled (if cancelled before completion)
StatusDescription
requestedPayment command sent to terminal. Waiting for card presentation.
in_progressTerminal is processing the transaction.
approvedPayment succeeded. Card was charged.
declinedCard was declined by the issuer or processor.
cancelledPayment was cancelled before the customer presented their card.
errorTerminal encountered an error (hardware failure, connection issue).
timed_outTerminal did not respond within 3 minutes.

Create Payment

Send a payment command to a specific terminal device. The payment is processed asynchronously -- the API returns immediately with a requested status, and you receive the result via webhook or by polling.

const payment = await revkeen.terminalPayments.create({
  device_id: 'd1e2f3a4-b5c6-7890-abcd-ef1234567890',
  amount_minor: 5000,  // £50.00
  currency: 'GBP',
  invoice_id: 'inv_xxxxxxxx',  // Optional
  reference: 'walk-in-sale-001',  // Optional
});

console.log(payment.data.id);      // Terminal payment attempt ID
console.log(payment.data.status);  // "requested"
payment = client.terminal_payments.create(
    device_id="d1e2f3a4-b5c6-7890-abcd-ef1234567890",
    amount_minor=5000,
    currency="GBP",
    invoice_id="inv_xxxxxxxx",
    reference="walk-in-sale-001"
)

print(payment.data.id)
print(payment.data.status)  # "requested"
cURL
curl -X POST "https://api.revkeen.com/v2/terminal-payments" \
  -H "x-api-key: rk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -d '{
    "device_id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890",
    "amount_minor": 5000,
    "currency": "GBP",
    "invoice_id": "inv_xxxxxxxx",
    "reference": "walk-in-sale-001"
  }'

Parameters

ParameterTypeRequiredDescription
device_idUUIDYesTarget terminal device. Call GET /v2/terminal-devices to discover available devices.
amount_minorintegerYesAmount in minor units (e.g., pence for GBP, cents for USD).
currencystringYesISO 4217 currency code (e.g., GBP, USD).
invoice_idUUIDNoInvoice to associate with the payment. Omit for walk-in or ad-hoc payments.
referencestringNoCustom reference for the payment. Auto-generated if not provided.

You must always specify a device_id. There is no auto-routing -- even merchants with a single terminal must pass the device ID explicitly. Call GET /v2/terminal-devices first to discover available devices.

List Payments

Retrieve terminal payments with optional filters.

// List all approved terminal payments for an invoice
const payments = await revkeen.terminalPayments.list({
  invoice_id: 'inv_xxxxxxxx',
  status: 'approved',
});

// List all terminal payments on a specific device
const devicePayments = await revkeen.terminalPayments.list({
  device_id: 'd1e2f3a4-b5c6-7890-abcd-ef1234567890',
  limit: 50,
});
cURL
curl -X GET "https://api.revkeen.com/v2/terminal-payments?status=approved&limit=50" \
  -H "x-api-key: rk_live_your_api_key"

Retrieve a Payment

const payment = await revkeen.terminalPayments.retrieve('tpa_xxxxxxxx');

if (payment.data.status === 'approved') {
  console.log('Auth code:', payment.data.auth_code);
  console.log('Card:', payment.data.card_scheme, payment.data.masked_pan);
  console.log('Entry mode:', payment.data.entry_mode);
}
cURL
curl -X GET "https://api.revkeen.com/v2/terminal-payments/tpa_xxxxxxxx" \
  -H "x-api-key: rk_live_your_api_key"

Cancel a Payment

Cancel a payment that is still waiting for card presentation (status requested or in_progress):

await revkeen.terminalPayments.cancel('tpa_xxxxxxxx');
cURL
curl -X POST "https://api.revkeen.com/v2/terminal-payments/tpa_xxxxxxxx/cancel" \
  -H "x-api-key: rk_live_your_api_key"

Refund a Payment

Refund a completed terminal payment. The refund command is sent to the same terminal:

// Full refund
await revkeen.terminalPayments.refund('tpa_xxxxxxxx');

// Partial refund
await revkeen.terminalPayments.refund('tpa_xxxxxxxx', {
  amount_minor: 2500,  // Refund £25.00 of the original amount
  reason: 'Customer return',
});
# Full refund
client.terminal_payments.refund("tpa_xxxxxxxx")

# Partial refund
client.terminal_payments.refund("tpa_xxxxxxxx",
    amount_minor=2500,
    reason="Customer return"
)

Void a Payment

Void a completed terminal payment before settlement:

await revkeen.terminalPayments.void('tpa_xxxxxxxx', {
  reason: 'Duplicate charge',
});

Response Shape

All terminal payment endpoints return a consistent response:

{
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "invoice_id": "inv_xxxxxxxx",
    "device_id": "d1e2f3a4-b5c6-7890-abcd-ef1234567890",
    "type": "sale",
    "status": "approved",
    "amount_minor": 5000,
    "currency": "GBP",
    "reference": "walk-in-sale-001",
    "terminal_serial": "21032100001",
    "uti": "900012345678901234567890",
    "auth_code": "123456",
    "response_code": "00",
    "rrn": "123456789012",
    "card_scheme": "VISA",
    "masked_pan": "************1234",
    "entry_mode": "contactless",
    "error_message": null,
    "created_at": "2026-03-11T10:00:00Z",
    "completed_at": "2026-03-11T10:00:15Z"
  }
}

Response Fields

FieldDescription
typesale, refund, or void
terminal_serialSerial number of the PAX terminal that processed the payment
utiUnique Transaction Identifier from the terminal
auth_codeAuthorization code from the payment processor
response_codeTerminal response code (00 = approved)
rrnRetrieval Reference Number for settlement matching
card_schemeCard network (VISA, MASTERCARD, AMEX, etc.)
masked_panMasked card number (only last 4 digits visible)
entry_modeHow the card was read: contactless, emv_chip, magnetic_stripe, manual_entry, or fallback

Concurrency and Per-Device Locking

RevKeen enforces a per-device concurrency lock. Only one payment can be in progress on a given terminal at a time. If you attempt to initiate a second payment on a device that is already processing, the API returns a 409 Conflict:

{
  "error": "device_busy",
  "message": "Another payment is in progress on this terminal"
}

Wait for the current payment to complete (or cancel it) before sending a new one.

Error Codes

HTTP StatusError CodeMeaning
201-Payment initiated successfully
400validation_errorInvalid request parameters
404device_not_foundDevice ID does not exist or does not belong to this merchant
409device_busyAnother payment is already in progress on this device
422device_offlineDevice is not online or heartbeat is stale
422device_not_pairedDevice connector is registered but terminal is not paired
403insufficient_scopesAPI key lacks terminal:write or terminal:read scope

Idempotency

All terminal payment POST endpoints support idempotency. Include the Idempotency-Key header to safely retry requests:

curl -X POST "https://api.revkeen.com/v2/terminal-payments" \
  -H "Idempotency-Key: unique-request-id-123" \
  -H "x-api-key: rk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "device_id": "...", "amount_minor": 5000, "currency": "GBP" }'

Sending the same Idempotency-Key returns the cached response from the first request. Keys are valid for 24 hours.

On this page