Skip to main content
Retail Outlet lets customers who prefer cash pay at thousands of Alfamart and Indomaret stores across Indonesia. You create a payment code through the API, give it to the customer, and the customer shows it to the cashier and pays the exact amount. Similar to QRIS, each payment code belongs to one transaction and can only be paid once.
Retail Outlet is currently offered to selected merchants only. To enable it for your merchant, contact your Singapay account manager. Create requests from merchants without access are rejected with 422.

Cash payments

Accept cash from customers who don’t have a bank account, e-wallet, or card.

Nationwide coverage

Customers pay at any Alfamart or Indomaret cashier — no app or registration needed.

Webhook on payment

Singapay notifies your transaction_notif_url as soon as the cashier confirms the payment.

Payment flow


Key difference: one code = one transaction

Unlike Virtual Account or Payment Link, each payment code is valid for one payment only. Once it is paid or expires, it cannot be used again. Create a new payment code for every new transaction.

Cannot do

  • Reuse a payment code for another payment
  • Accept partial payments or overpayments
  • Change the amount or outlet after the code is created
  • Create a code without an expiry time

Must do

  • Create a new payment code for each transaction
  • Set the exact amount and the outlet (retail_code) at creation
  • Set expired_at to a time in the future
  • Tell the customer which store to go to: Alfamart or Indomaret

Supported outlets

Alfamart

ALFAMART
Payment code can only be paid at Alfamart cashiers.

Indomaret

INDOMARET
Payment code can only be paid at Indomaret cashiers.
A payment code only works at the outlet you selected in retail_code. An Alfamart code cannot be paid at Indomaret, and an Indomaret code cannot be paid at Alfamart.

Transaction status

Settlement is not a separate status. Check the has_settle and settle_at fields to see whether a paid transaction has been settled.

1

Create a payment code

Call Create Payment Code with retail_code, amount, customer_name, and expired_at. Send your own order ID in merchant_reff_no so you can match the transaction later.
2

Save the transaction

Store the transaction_id and payment_code from the response. You need transaction_id to look up the transaction later.
3

Give the code to the customer

Show the payment_code, the amount, the outlet (Alfamart or Indomaret), and the expiry time to the customer.
4

Customer pays at the cashier

The customer tells the cashier they want to make a payment, shows the payment code, and pays the exact amount in cash.
5

Receive the webhook

Singapay sends a retail-transaction webhook to your transaction_notif_url when the transaction becomes paid. Update the order on your side.
6

Verify the status (optional)

Call Show Transaction to confirm the latest status, for example if you did not receive the webhook.

Important notes

By default, the amount must be between IDR 15,000 and IDR 2,500,000 per transaction. Your merchant may have different limits based on its configuration. If the amount is outside the limit, the request is rejected with 422 and a message showing the allowed min or max.
expired_at is required and must be a Unix timestamp in milliseconds (13 digits) set to a future time. After it passes, an unpaid transaction changes to expired and the code can no longer be paid. If you subscribe to the Transaction Money-In Expiration Webhook, expired Retail transactions are included in it.
When a transaction becomes paid, Singapay sends a POST request to your transaction_notif_url with event = retail-transaction. This URL is also used by other money-in products, so route requests by the event field. See Shared Webhook Endpoints and verify every request as described in Security and Signature Validation.
The API has two endpoints only: create and show. There is no separate inquiry-status endpoint — Show Transaction always returns the latest status. To browse or export Retail transactions, use the Singapay merchant dashboard.
A fee is charged per transaction and returned in fees.amount. The balance from a paid transaction follows your merchant’s configured settlement method, the same as Virtual Account. Track settlement with the has_settle and settle_at fields.
In API responses, expired_at, processed_timestamp, post_timestamp, and settle_at are Unix timestamps in milliseconds. processed_timestamp and settle_at stay null until the transaction is paid or settled.
The Create Payment Code endpoint is rate-limited per merchant. If you send too many requests, you will receive 429 Too Many Requests. Wait before you try again.

Available endpoints

Create Payment Code

Create a one-time Alfamart or Indomaret payment code for a fixed amount.

Show Transaction

Get the details and latest status of a Retail transaction by its transaction_id.
All endpoints require authentication. See Authentication.