RevolunoCloud

How to accept card payments from your own till software

Your POS already rings up the sale. Here is how to hand the amount to a card terminal and know for sure whether the customer paid, without writing a single line of card-handling code.

Updated · Revoluno

If you build or run your own till software (a restaurant POS, a retail system, a pharmacy dispensing system or a fuel forecourt controller), sooner or later someone asks for card payments on the same screen as the sale. The cashier should not type the amount twice, and the sale should not close until the money is in.

This guide explains the pattern that solves that, and how it works with the Revoluno Cloud API.

The problem with standalone card machines

With a standalone terminal the cashier rings up the sale on the till, then keys the same amount into the card machine. Three things go wrong:

  • Keying errors. US$15.00 becomes US$150.00, or US$1.50.
  • No proof in the till. The till marks the sale paid because the cashier pressed a button, not because a payment was approved.
  • Slow reconciliation. At the end of the day someone matches card slips to till receipts by hand.

The integrated pattern

In an integrated setup the till tells the terminal what to charge, and the terminal tells the till what happened. With a cloud integration, both happen over the internet:

  1. The till sends the amount and its own sale reference to the API.
  2. The terminal, which is polling the API, picks the payment up and shows the amount.
  3. The customer taps, inserts or swipes their card. The terminal authorises it.
  4. The API posts the result to your till's webhook URL. The till closes the sale only on an approval.

Your software never sees the card number, the PIN or the chip data. It only sees the outcome.

What it looks like in code

Starting a payment is one request. The amount is in cents and foreign_transaction_id is your own sale number:

POST /v0.1/merchants/{merchant_code}/readers/{reader_id}/checkout
Authorization: Bearer rk_live_…
Content-Type: application/json

{
  "total_amount": { "currency": "USD", "minor_unit": 2, "value": 1500 },
  "foreign_transaction_id": "SALE-000123",
  "description": "Table 4",
  "return_url": "https://your-till.example.com/revoluno/webhook"
}

The answer comes back straight away with a client_transaction_id. That means the payment was accepted, not that it was paid. Show the cashier a "waiting for card" screen.

A few seconds later your webhook receives the result:

{
  "event_type": "reader.transaction.updated",
  "payload": {
    "client_transaction_id": "087292db-…",
    "foreign_transaction_id": "SALE-000123",
    "status": "successful",
    "outcome": "APPROVED",
    "card": { "masked_pan": "535522******1234", "entry_mode": "07" },
    "auth_code": "A1B2C3"
  }
}

Mark sale SALE-000123 paid, print the receipt with the masked card number and authorisation code, and you are done.

Things your till should handle

  • Declines and cancellations. status is failed and outcome says why: DECLINED, CANCELLED on the terminal, TERMINATED by you, or EXPIRED. Let the cashier try again or take another payment method.
  • A busy terminal. Each terminal takes one payment at a time. A second request gets 409 READER_BUSY.
  • An offline terminal. If the terminal is off or out of cloud mode you get 422 READER_OFFLINE straight away, before the customer waits.
  • A missed webhook. If your server was down, fetch the checkout by its id. See handling payment webhooks.

Getting started

Revoluno sets up your business and gives you a console login. From there you create an API key, register your terminals by serial number, and send a test payment from the console before writing any code. The API reference has every endpoint with examples in cURL, Node.js, Python, PHP, C# and Java.