QR Payments
QR payments use their own resource, /qr-payments. A QR-only host does not need to implement the card endpoints.
Two modes are supported:
- MPQR (merchant-presented): your host returns a QR code, the device shows it, the customer scans it with a wallet.
- CPQR (consumer-presented): the merchant scans the code in the customer's wallet, your host charges it.
Endpoints
| Direction | Endpoint | Purpose | Required |
|---|---|---|---|
| MineSec → you | POST {baseUrl}/qr-payments | Create: return a QR (MPQR) or charge a code (CPQR) | Yes |
| MineSec → you | POST {baseUrl}/qr-payments/inquiries | Current status of a payment or refund | Yes |
| MineSec → you | POST {baseUrl}/qr-payments/cancellations | Close an unpaid order or reverse a paid one | Yes |
| MineSec → you | POST {baseUrl}/qr-payments/refunds | Full or partial refund | Only if qrSupportsRefund is on |
| You → MineSec | POST {notifyUrl} | Push the final result | Only if notifyProfile and notifyKey are set |
Refunds, notify and the optional uses of cancellation are switched on in the extraParams of your acceptance;
see Configuration & Go-live.
Cancellation itself is always required: MineSec cancels a QR payment whenever the device reverses it, whatever these
settings are. See Cancellation.
Status
Only status decides the outcome.
| Status | Meaning |
|---|---|
PENDING | Order created, waiting for the customer (MPQR only) |
SUCCESS | Paid. paymentMethod must be the concrete wallet |
FAILED | Not paid. Include failureCode and respMessage |
CLOSED | Cancelled or expired before any money moved |
REVERSED | Charged, then returned by a cancellation |
respCode is kept for diagnostics and must be 00 when status is PENDING or SUCCESS.
MPQR: merchant-presented
- Answer the create at once with
status: PENDINGand theqrCodeto display. qrCode.formattells the device how to render it:EMVCO_MPM,URLorRAW.qrExpirySecondsin the request is the expiry MineSec asks for. YourexpiresAtis shown on the device, but MineSec stops polling and closes the order at whichever comes first: yourexpiresAtor the requested expiry. Do not return anexpiresAtlater than the requested expiry.- A
PENDINGanswer withoutqrCodeis treated as a decline. - If the create times out or fails, MineSec declines the payment and does not cancel it. Let the order expire on your side.
CPQR: consumer-presented
- Answer a final
SUCCESSorFAILEDwithin 20 seconds. Do not answerPENDING. If the create does not settle, MineSec sends/qr-payments/inquiriesevery 2 seconds inside the same 20-second window. - After a timeout cancellation, only
CLOSEDorREVERSEDsettles the payment. Any other answer leaves it in processing and flags it for reconciliation. consumerCode.formatisBARCODE_NUMERIC,QR_TEXTorEMV_CPM_BASE64.- MineSec may cancel a payment whose create never reached you. Answer
CLOSED, withoutrrn.
Cancellation
MineSec sends a cancellation in these cases:
| Case | reason | Sent |
|---|---|---|
| The device reverses the sale: the cashier closes the QR, or the device stops waiting | USER_CANCELLED (MPQR), TIMEOUT (CPQR) | Always |
| CPQR without a final answer within 20 s | TIMEOUT | Always |
| MPQR still unpaid at expiry | TIMEOUT | Only if qrSupportsCancel is on |
| The merchant voids a paid payment | MERCHANT_VOID | Only if qrSupportsCancel is on |
Without qrSupportsCancel, an expired MPQR order is not closed by MineSec and can only be settled by a notify, and QR
payments cannot be voided.
A cancellation for a device reversal must be answered CLOSED or REVERSED. Any other answer, or no answer, leaves
the reversal pending: MineSec sends it again before the device's next card or CPQR payment, refund, void or
settlement on your host, and those requests fail until it is cleared. If you never received the payment, answer
CLOSED, without rrn.
Answer with what actually happened to the money:
| Answer | When |
|---|---|
CLOSED | No money moved; the order is closed |
REVERSED | The customer was charged and the money was returned |
FAILED | The payment could not be cancelled |
reason is USER_CANCELLED, TIMEOUT or MERCHANT_VOID. A void of a QR payment is a MERCHANT_VOID cancellation;
CLOSED and REVERSED both count as voided.
MineSec may retry a cancellation. Each retry carries the same enbrActionId, so use it to recognise a repeated
cancellation. A different cancellation of the same payment gets a new enbrActionId.
Refund
A refund is a transaction of its own, with its own enbrTranId. linkedEnbrTranId and orgRrn identify the original
payment. Partial and multiple refunds are supported; MineSec rejects a refund above the remaining amount before it
reaches you, and a payment without rrn cannot be refunded.
Answer a final SUCCESS or FAILED within 20 seconds. A refund without a final answer is not sent again and is not
polled. It stays unresolved until a refund notify arrives or it is reconciled with you. Refunds do not change the
status of the original payment.
Inquiry
MineSec polls /qr-payments/inquiries whether or not you send notifies:
| Payment | Polling |
|---|---|
| MPQR | Every 3 seconds, from 3 seconds after the create until expiry |
| CPQR | Every 2 seconds, inside the 20-second window |
| Refund | Not polled |
inquiryType is PAYMENT or REFUND. Return the same result body as the create or refund.
Notify (optional)
A notify pushes the final result to MineSec without waiting for the next inquiry.
- Send it only when the request carried
notifyUrl. Call the URL unchanged; never build it yourself. - Send it once per final status, never for
PENDING. The body is the result pluseventId,eventTypeandeventTime. - Sign it with the notify key MineSec issues at onboarding (separate from the request HMAC key). The path is not signed:
x-request-time: 2025-02-07T02:35:10Z
x-signature: HmacSHA256 hex( HMAC-SHA256( notifyKey, "POST.." + x-request-time + ".." + body ) )amountValueandcurrencyare the order amount of the request, unchanged.- If no
200arrives within 10 seconds, retry, except after a400or404. Retry 5 s, 10 s, 15 s, 1 min, 5 min, 30 min, 2 h and 6 h after the first attempt, then stop; MineSec's inquiries still settle the transaction. Keep the sameeventIdand body; use a newx-request-timeand signature for each retry. A notify can be delivered more than once.
| Answer | Meaning |
|---|---|
200 | {"respCode":"00"}: received. Also the answer when the notify reports another amount or a final status other than the one MineSec holds (MineSec reconciles these, and sending them again changes nothing), and when the transaction is already final (nothing changes) |
400 | The notify does not match the transaction: enbrTranId in the body differs from the URL, eventType does not match a payment or a refund, the body is not a notify, or the notify profile in the URL is not the one set up for your host. Nothing changed. Do not retry the same content |
401 | Missing or wrong signature, or x-request-time more than 5 minutes off. Nothing changed |
404 | Unknown transaction. Nothing changed. Do not retry |
Unified QR
With paymentMethod: UNIFIED_QR, one code accepts several wallets and your host identifies the wallet.
- On
SUCCESS, return the concretepaymentMethod, neverUNIFIED_QR. - CPQR: if the merchant does not accept the scanned wallet, answer
FAILEDwithfailureCode: UNSUPPORTED_WALLET.
Rules
- Idempotency:
enbrTranIdis the key on every QR endpoint. A repeated create returns the first result and does not create a second order. - References: return your
rrnfrom the create onward, in every response includingPENDING, with the same value each time. A payment that succeeded withoutrrncannot be traced or refunded. Put the wallet's own transaction ID, if different, inextra.walletTransactionId. - Amounts: echo
amountValueandcurrencyfrom the request. ASUCCESSwith a different amount is not approved; it is flagged for reconciliation. rrnlength: at most 64 characters.- Failures: use a listed
failureCodewhere one fits:CODE_EXPIRED,CODE_INVALID,INSUFFICIENT_FUNDS,USER_CANCELLED,RISK_REJECTED,LIMIT_EXCEEDED,UNSUPPORTED_WALLET,DUPLICATE,SYSTEM_ERROR. Other codes are accepted as a generic failure. - Settlement: QR is captured by your host.
batchIdandbatchNoare for reference only.
Example: MPQR create
{
"enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
"srsTranId": "M1866239185331064832",
"trace": "000187",
"pboId": "<your-pbo-id>",
"profileId": "prof_01JDK7CS51N7DM47JK9M4R9XPP",
"acptId": "acpt_01JDK7CS6QKRN5K9BP053H0WBP",
"primaryMid": "111",
"primaryTid": "222",
"merchantDevice": "SOFTPOS",
"qrMode": "MPQR",
"tranType": "SALE",
"amountValue": "25.00",
"currency": "HKD",
"paymentMethod": "UNIFIED_QR",
"qrExpirySeconds": 120,
"notifyUrl": "https://<minesec-notify-url-issued-per-transaction>",
"merchantName": "ACME",
"merchantAddress": "No. 7, Park Avenue, Sunset Beach",
"merchantCity": "Hong Kong",
"merchantCountry": "HK",
"mcc": "5999",
"timestamp": "2025-02-07T02:34:33.732Z"
}{
"respCode": "00",
"status": "PENDING",
"enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
"rrn": "QR20250207000187",
"amountValue": "25.00",
"currency": "HKD",
"qrCode": { "content": "00020101021226...6304ABCD", "format": "EMVCO_MPM" },
"expiresAt": "2025-02-07T02:36:33Z"
}Example: MPQR inquiry
MineSec polls the MPQR order above. trace is the payment's own.
{
"enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
"srsTranId": "M1866239185331064832",
"trace": "000187",
"pboId": "<your-pbo-id>",
"profileId": "prof_01JDK7CS51N7DM47JK9M4R9XPP",
"acptId": "acpt_01JDK7CS6QKRN5K9BP053H0WBP",
"primaryMid": "111",
"primaryTid": "222",
"inquiryType": "PAYMENT",
"rrn": "QR20250207000187",
"batchId": "batch_01JKF2QRZJFZ5BHF0SC0X9ZR5M",
"batchNo": "029290",
"timestamp": "2025-02-07T02:35:06.020Z"
}{
"respCode": "00",
"status": "SUCCESS",
"enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
"rrn": "QR20250207000187",
"networkMerchantId": "2088621900012345",
"primaryMid": "111",
"primaryTid": "222",
"paymentMethod": "ALIPAY",
"amountValue": "25.00",
"currency": "HKD",
"paidAt": "2025-02-07T02:35:01Z",
"payer": { "id": "2088102112345678", "fundingSource": "BALANCE" },
"extra": { "walletTransactionId": "2025020722001412345678901234" }
}{
"respCode": "00",
"respMessage": "Waiting for payer",
"status": "PENDING",
"enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
"rrn": "QR20250207000187",
"paymentMethod": "UNIFIED_QR",
"amountValue": "25.00",
"currency": "HKD"
}Example: CPQR create
{
"enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
"srsTranId": "M1866239402170769408",
"trace": "000188",
"pboId": "<your-pbo-id>",
"profileId": "prof_01JDK7CS51N7DM47JK9M4R9XPP",
"acptId": "acpt_01JDK7CS6QKRN5K9BP053H0WBP",
"primaryMid": "111",
"primaryTid": "222",
"merchantDevice": "SOFTPOS",
"qrMode": "CPQR",
"tranType": "SALE",
"amountValue": "128.00",
"currency": "HKD",
"paymentMethod": "WECHAT",
"consumerCode": { "value": "134567890123456789", "format": "BARCODE_NUMERIC", "scanMethod": "CAMERA" },
"notifyUrl": "https://<minesec-notify-url-issued-per-transaction>",
"merchantName": "ACME",
"merchantAddress": "No. 7, Park Avenue, Sunset Beach",
"merchantCity": "Hong Kong",
"merchantCountry": "HK",
"mcc": "5999",
"batchId": "batch_01JKF2QRZJFZ5BHF0SC0X9ZR5M",
"batchNo": "029290",
"timestamp": "2025-02-07T02:40:38.104Z"
}{
"respCode": "00",
"respMessage": "Approved",
"status": "SUCCESS",
"enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
"rrn": "QR20250207000188",
"networkMerchantId": "1900012345",
"paymentMethod": "WECHAT",
"amountValue": "128.00",
"currency": "HKD",
"paidAt": "2025-02-07T02:40:40Z",
"payer": { "id": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o", "fundingSource": "BALANCE" },
"extra": { "walletTransactionId": "4200002891202502075566778899" }
}{
"respCode": "51",
"respMessage": "Insufficient balance",
"status": "FAILED",
"failureCode": "INSUFFICIENT_FUNDS",
"enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
"rrn": "QR20250207000188",
"paymentMethod": "WECHAT",
"amountValue": "128.00",
"currency": "HKD"
}Example: cancellation
The CPQR payment above had no final answer within 20 seconds.
{
"enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
"enbrActionId": "act_01JQ8V5D2F4H6K8M0P2R4T6V8X",
"srsTranId": "M1866239402170769408",
"trace": "000188",
"pboId": "<your-pbo-id>",
"profileId": "prof_01JDK7CS51N7DM47JK9M4R9XPP",
"acptId": "acpt_01JDK7CS6QKRN5K9BP053H0WBP",
"primaryMid": "111",
"primaryTid": "222",
"reason": "TIMEOUT",
"rrn": "QR20250207000188",
"batchId": "batch_01JKF2QRZJFZ5BHF0SC0X9ZR5M",
"batchNo": "029290",
"timestamp": "2025-02-07T02:40:58.512Z"
}{
"respCode": "00",
"respMessage": "Order closed",
"status": "CLOSED",
"enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
"rrn": "QR20250207000188",
"paymentMethod": "WECHAT",
"amountValue": "128.00",
"currency": "HKD"
}{
"respCode": "00",
"respMessage": "Order not found, nothing to cancel",
"status": "CLOSED",
"enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
"amountValue": "128.00",
"currency": "HKD"
}Example: refund
A partial refund of the MPQR payment above. The refund has its own enbrTranId and trace.
{
"enbrTranId": "tran_01JQ8VR1N3P5R7T9V1X3Z5B7D9",
"srsTranId": "M1866251377260482560",
"trace": "000192",
"pboId": "<your-pbo-id>",
"profileId": "prof_01JDK7CS51N7DM47JK9M4R9XPP",
"acptId": "acpt_01JDK7CS6QKRN5K9BP053H0WBP",
"primaryMid": "111",
"primaryTid": "222",
"amountValue": "5.00",
"currency": "HKD",
"notifyUrl": "https://<minesec-notify-url-issued-per-transaction>",
"linkedEnbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
"orgRrn": "QR20250207000187",
"batchId": "batch_01JKF2QRZJFZ5BHF0SC0X9ZR5M",
"batchNo": "029290",
"timestamp": "2025-02-07T03:10:08.310Z"
}{
"respCode": "00",
"status": "SUCCESS",
"enbrTranId": "tran_01JQ8VR1N3P5R7T9V1X3Z5B7D9",
"linkedEnbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
"rrn": "RF20250207000031",
"orgRrn": "QR20250207000187",
"paymentMethod": "ALIPAY",
"amountValue": "5.00",
"currency": "HKD",
"refundedAt": "2025-02-07T03:10:11Z",
"extra": { "walletTransactionId": "2025020722001412345678909999" }
}Example: notify
Your host reports the MPQR payment above to the notifyUrl from the create request.
Content-Type: application/json
x-request-time: 2025-02-07T02:35:02Z
x-signature: HmacSHA256 <hex HMAC-SHA256 of "POST.." + x-request-time + ".." + body, under the notify key>
{
"eventId": "evt_8842117",
"eventType": "PAYMENT",
"eventTime": "2025-02-07T02:35:02.902Z",
"respCode": "00",
"status": "SUCCESS",
"enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
"rrn": "QR20250207000187",
"networkMerchantId": "2088621900012345",
"primaryMid": "111",
"primaryTid": "222",
"paymentMethod": "ALIPAY",
"amountValue": "25.00",
"currency": "HKD",
"paidAt": "2025-02-07T02:35:01Z",
"payer": { "id": "2088102112345678", "fundingSource": "BALANCE" },
"extra": { "walletTransactionId": "2025020722001412345678901234" }
}{ "respCode": "00" }Full field lists: Data Model › QR.
Implementation checklist: answer MPQR create at once with PENDING and a qrCode; answer CPQR create and refunds
with a final status within 20 seconds; implement inquiries and cancellations; return a stable rrn; de-duplicate
cancellations on enbrActionId; answer CLOSED to a cancellation for an order you never received.