POS Billing Application Integration
Integrate your billing application, such as Zoho Invoice, Zoho Books, or any other billing software you use, with Zoho Payments POS to initiate payment sessions, send them to the terminal for customer authentication, and poll for the final transaction status, all through simple REST APIs.
How It Works
Your billing application communicates with the POS device through the Zoho Payments Server. When a customer is ready to check out, the cashier triggers a payment from the billing application, which calls the Zoho Payments API to create a payment session.
The cashier then taps Fetch Payments on the POS device to pull the session. The customer completes the payment using their card or UPI, and the device processes the transaction through the acquiring bank. Once complete, the device reports the result back to the Zoho Payments Server. Your billing application polls the server for the final status and updates the invoice accordingly.
Scenario: Charles runs a retail electronics store and uses a billing application connected to Zoho Payments POS. When a customer checks out, the cashier completes the sale entirely from the billing screen. The payment is sent to the POS device, the customer taps their card, and the invoice is automatically marked as paid in the billing application without any manual reconciliation.
Here’s the end-to-end flow for the Zoho Payments billing application integration:
Get Started
To integrate your billing application with the POS device, you need to create a payment session via the API, have the cashier fetch it on the terminal, and then poll for the transaction result. All requests use HTTPS. Pass the organization ID as the account_id query parameter and your OAuth scope token in the Authorization header.
Prerequisites:
- Register your application in Zoho Developer Console to obtain your Client ID and Client Secret.
- A Zoho Payments Point of Sale (POS) device synced with an active Zoho Payments account.
1. Create a Payment Session
Call the Create Payment Session API with the amount, currency, terminal ID, and payment method. The API returns a unique payments_session_id that tracks this transaction through its full lifecycle.
OAuth Scope: ZohoPay.payments.CREATE
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/terminal/paymentsessions |
Create a new POS payment session for a specific terminal and amount. |
{
"amount": 100.50,
"currency": "INR",
"type": "pos_payment",
"terminal_id": "1234r5tr",
"invoice_number": "INV-12345",
"description": "Payment for Order #12345",
"meta_data": [
{ "key": "Key1", "value": "Value1" }
],
"payment_method_details": {
"type": "card"
}
}
{
"code": 0,
"message": "success",
"payments_session": {
"payments_session_id": "2000000012001",
"currency": "INR",
"amount": "100.50",
"terminal_id": "1234r5tr",
"created_time": 1708950672,
"payment_session_type": "pos_payment",
"session_status": "in_progress",
"invoice_number": "INV-12345",
"description": "Payment for Order #12345",
"meta_data": [
{ "key": "Key1", "value": "Value1" }
]
}
}
Store the payments_session_id against the corresponding invoice or order in your billing system. This ID is required for all subsequent operations, including status polling, settlement reconciliation, and dispute handling.
Multi-counter setup
If your business operates multiple counters, assign a unique terminal_id to each counter in your billing system. When creating a payment session, always pass the terminal_id of the counter where the customer is currently checking out. This ensures the session appears only on that counter’s terminal and not on others.
Scenario: Rahul manages a supermarket with three billing counters, each mapped to a unique terminal ID (Counter A → TRM001, Counter B → TRM002, Counter C → TRM003). When a customer checks out at Counter B, the billing system creates the session with "terminal_id": "TRM002", ensuring the payment appears only on that counter’s POS device.
2. Fetch the Payment on the Terminal
The cashier taps Fetch Payments on the POS device. The device pulls the active payment session assigned to it over a secure connection, based on the terminal_id specified during session creation. Once fetched, the terminal displays the payment details and is ready for the customer to pay.
Note: Only one active session is supported per terminal at a time. Ensure the previous session has reached a final status (succeeded or failed) before creating a new one for the same terminal.
3. Process the Payment
The POS device displays the payment details, the customer completes the payment using their preferred method, and the device forwards the authorization request to the processor. Once the processor responds, the device updates the session status on the Zoho Payments server and prints a receipt on success. For a detailed breakdown, refer to Transaction Flow at the Terminal.
While the device processes the payment, your billing application should begin polling the Zoho Payments server for the session status.
4. Poll for Payment Status
Poll the Retrieve Payment Session API every 5 seconds until session_status reaches a final state (succeeded or failed). Once a final status is returned, proceed to update your invoice.
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/terminal/paymentsessions/{payments_session_id}?account_id=ORG_ID |
Retrieve the latest status and payment details. |
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
payments_session_id |
string | Yes | The payments_session_id returned during session creation. |
{
"code": 0,
"message": "success",
"payments_session": {
"payments_session_id": "2000000012001",
"currency": "INR",
"amount": "100.50",
"session_status": "in_progress",
"payment_session_type": "pos_payment",
"created_time": 1708950672,
"invoice_number": "INV-12345",
"description": "Payment for Order #12345"
}
}
{
"code": 0,
"message": "success",
"payments_session": {
"payments_session_id": "2000000012001",
"currency": "INR",
"amount": "100.50",
"session_status": "succeeded",
"payment_session_type": "pos_payment",
"created_time": 1708950672,
"invoice_number": "INV-12345",
"description": "Payment for Order #12345",
"payments": [
{
"payment_id": "173000002314886",
"status": "succeeded",
"date": 1758174448,
"date_formatted": "Sep 18, 2025, 11:17 am",
"payment_method": {
"type": "card",
"type_formatted": "Card"
}
}
]
}
}
{
"code": 0,
"message": "success",
"payments_session": {
"payments_session_id": "2000000012001",
"currency": "INR",
"amount": "100.50",
"session_status": "failed",
"payment_session_type": "pos_payment",
"created_time": 1708950672,
"invoice_number": "INV-12345",
"description": "Payment for Order #12345",
"payments": [
{
"payment_id": "173000002314886",
"status": "failed",
"date": 1758174448,
"date_formatted": "Sep 18, 2025, 11:17 am",
"failure_code": "insufficient_funds",
"payment_method": {
"type": "card",
"type_formatted": "Card"
}
}
]
}
}
Once you receive a succeeded or failed status, stop polling and update the corresponding invoice in your billing system.
5. Update the Invoice
Once the Retrieve Payment Session API returns a final status (succeeded or failed), use the invoice_number from the response to locate and update the corresponding record in your billing system. This step does not involve any Zoho Payments API call; the action is performed entirely within your own application.
| Payment Status | Merchant Action |
|---|---|
succeeded |
Mark invoice or order as paid. Trigger downstream fulfilment. |
failed |
Mark payment as failed. Surface the failure code to the cashier. |
in_progress |
Continue polling. Do not assume failure on a slow response. |
Note: Polling ends when the session status reaches a final state (succeeded or failed). Session status should always be confirmed via the Payment Retrieve API.