TrueMoney QR

Topics covered on this page

TrueMoney QR (Offline)

Accept offline payments from TrueMoney users through your store using the TrueMoney QR (Offline) payment method.

This guide walks you through the payment flow and details how to implement it.


How to enable

  • Supported countries: Thailand
  • Minimum API version: 2017-11-02

To enable TrueMoney QR, send an email requesting this feature to support@omise.co. You will need to review and accept new terms and conditions.


Payment flow

TrueMoney QR payment flow

Customers paying via TrueMoney QR go through an offline payment flow. Once the charge is created, it can only be authorized offline — the customer must scan the generated QR code with the TrueMoney app on their phone to complete the payment.

After the customer selects TrueMoney QR as their preferred payment method, your site generates the QR code. The customer scans it to complete the payment.

TrueMoney QR supports the following payment methods:

  • TrueMoney Wallet (Wallet Balance)
  • Bank Account
  • Credit/Debit Card
  • Pay Next (Full payment)
  • Pay Next Extra (Full payment)

Implementation

To create a charge using TrueMoney QR, make the following API requests:

  1. Create a payment source (type: truemoney_qr) using Omise.js or one of the mobile SDKs (iOS or Android).
  2. Create a charge using the source identifier from step 1.
  3. After receiving the charge completion webhook event, retrieve the charge to verify its status (optional, but recommended).

Use your public key to create the TrueMoney QR source on the client (a customer's browser or mobile phone). Use your secret key to create the TrueMoney QR charge on the server.

If both source creation and charge must happen server-side, you can combine them in a single API request using your secret key.

Creating a source

When the customer confirms they wish to pay with TrueMoney QR, create a source specifying amount, currency, and type.

Parameter Type Description
amount integer (Required) See Limits
currency string (Required) THB
type string (Required) truemoney_qr

The following examples demonstrate creating a TrueMoney QR source for ฿4,000. Replace omise_public_key and $OMISE_PUBLIC_KEY with the test public key from your dashboard.

In Omise.js, the type parameter is the first argument passed to the createSource method.

Omise.setPublicKey(omise_public_key);

Omise.createSource('truemoney_qr', {
  "amount": 400000,
  "currency": "THB",
}, function(statusCode, response) {
  console.log(response);
});

For testing, you can make the same request using curl:

curl https://api.omise.co/sources \
  -u $OMISE_PUBLIC_KEY: \
  -d "amount=400000" \
  -d "currency=THB" \
  -d "type=truemoney_qr"

Example response:

{
  "object": "source",
  "id": "src_test_616e6qe9duz2cthauum",
  "livemode": false,
  "location": "/sources/src_test_616e6qe9duz2cthauum",
  "amount": 400000,
  "barcode": null,
  "bank": null,
  "created_at": "2024-09-23T03:18:39Z",
  "currency": "THB",
  "email": null,
  "flow": "offline",
  "installment_term": null,
  "ip": "35.198.236.178",
  "absorption_type": null,
  "name": null,
  "mobile_number": null,
  "phone_number": null,
  "platform_type": null,
  "scannable_code": null,
  "billing": null,
  "shipping": null,
  "items": [],
  "references": null,
  "provider_references": null,
  "store_id": null,
  "store_name": null,
  "terminal_id": null,
  "type": "truemoney_qr",
  "zero_interest_installments": null,
  "charge_status": "unknown",
  "receipt_amount": null,
  "discounts": [],
  "promotion_code": null
}

The id attribute is the source identifier (begins with src).

Creating a charge

Create a charge specifying source, amount, and currency:

  • source — the source identifier returned in the previous step.
  • amount and currency — must match the values used when creating the source.

Replace $OMISE_SECRET_KEY with the test secret key from your dashboard. Replace $SOURCE_ID with the id of the source.

curl https://api.omise.co/charges \
  -u $OMISE_SECRET_KEY: \
  -d "amount=400000" \
  -d "currency=THB" \
  -d "return_uri=http://example.com/orders/345678/complete" \
  -d "source=$SOURCE_ID"

Example response:

{
  "object": "charge",
  "id": "chrg_test_616e6qhdvp3gv5hb31y",
  "location": "/charges/chrg_test_616e6qhdvp3gv5hb31y",
  "amount": 400000,
  "acquirer_reference_number": null,
  "net": 0,
  "fee": 0,
  "fee_vat": 0,
  "interest": 0,
  "interest_vat": 0,
  "funding_amount": 400000,
  "refunded_amount": 0,
  "transaction_fees": {
    "fee_flat": null,
    "fee_rate": null,
    "vat_rate": "7.0"
  },
  "platform_fee": {
    "fixed": null,
    "amount": null,
    "percentage": null
  },
  "currency": "THB",
  "funding_currency": "THB",
  "ip": null,
  "refunds": {
    "object": "list",
    "data": [],
    "limit": 20,
    "offset": 0,
    "total": 0,
    "location": "/charges/chrg_test_616e6qhdvp3gv5hb31y/refunds",
    "order": "chronological",
    "from": "1970-01-01T00:00:00Z",
    "to": "2024-09-23T03:18:40Z"
  },
  "link": null,
  "description": null,
  "metadata": {},
  "card": null,
  "source": {
    "object": "source",
    "id": "src_test_616e6q22leajy1hszrg",
    "livemode": false,
    "location": "/sources/src_test_616e6q22leajy1hszrg",
    "amount": 400000,
    "barcode": null,
    "bank": null,
    "created_at": "2024-09-23T03:18:38Z",
    "currency": "THB",
    "email": null,
    "flow": "offline",
    "installment_term": null,
    "ip": "35.198.236.178",
    "absorption_type": null,
    "name": null,
    "mobile_number": null,
    "phone_number": null,
    "platform_type": null,
    "scannable_code": {
      "object": "barcode",
      "type": "qr",
      "image": {
        "object": "document",
        "livemode": false,
        "id": "docu_test_616e6qjb5979wb1nopf",
        "deleted": false,
        "filename": "qrcode.png",
        "location": "/charges/chrg_test_616e6qhdvp3gv5hb31y/documents/docu_test_616e6qjb5979wb1nopf",
        "kind": "qr",
        "download_uri": "https://api.omise.co/charges/chrg_test_616e6qhdvp3gv5hb31y/documents/docu_test_616e6qjb5979wb1nopf/downloads/0AF28F77E29F13C1",
        "created_at": "2024-09-23T03:18:40Z"
      }
    },
    "billing": null,
    "shipping": null,
    "items": [],
    "references": null,
    "provider_references": {
      "reference_number_1": "pay2_test_616e6qhftlwd4241yc1",
      "reference_number_2": null
    },
    "store_id": null,
    "store_name": null,
    "terminal_id": null,
    "type": "truemoney_qr",
    "zero_interest_installments": null,
    "charge_status": "pending",
    "receipt_amount": null,
    "discounts": [],
    "promotion_code": null
  },
  "schedule": null,
  "linked_account": null,
  "customer": null,
  "dispute": null,
  "transaction": null,
  "failure_code": null,
  "failure_message": null,
  "status": "pending",
  "authorize_uri": "https://pay.omise.co/payments/pay2_test_616e6qhftlwd4241yc1/authorize?acs=false",
  "return_uri": "http://example.com/orders/345678/complete",
  "created_at": "2024-09-23T03:18:40Z",
  "paid_at": null,
  "authorized_at": null,
  "expires_at": "2024-09-24T03:18:40Z",
  "expired_at": null,
  "reversed_at": null,
  "zero_interest_installments": false,
  "branch": null,
  "terminal": null,
  "device": null,
  "authorized": false,
  "capturable": false,
  "capture": true,
  "disputable": false,
  "livemode": false,
  "refundable": false,
  "partially_refundable": false,
  "reversed": false,
  "reversible": false,
  "voided": false,
  "paid": false,
  "expired": false,
  "can_perform_void": false,
  "approval_code": null
}

Creating a source and charge

Alternatively, you can create and charge a source in a single API request using your secret key:

curl https://api.omise.co/charges \
  -u $OMISE_SECRET_KEY: \
  -d "amount=400000" \
  -d "currency=THB" \
  -d "return_uri=http://example.com/orders/345678/complete" \
  -d "source[type]=truemoney_qr"

Completing the charge

A newly created charge has its status set to pending. Other possible values are successful, failed, and expired.

The following sequence diagram illustrates the full payment flow:

sequenceDiagram participant Customer participant OmiseJS participant Merchant participant OmiseAPI participant OfflineProvider Customer->>OmiseJS: Send payment details for purchase OmiseJS->>OmiseAPI: Request source using payment details OmiseAPI-->>OmiseJS: Return source OmiseJS->>Merchant: Merchant receives source Merchant->>OmiseAPI: Request charge using source and purchase details OmiseAPI-->>Merchant: Send "charge.create" webhook OmiseAPI-->>Merchant: Return charge Customer->>OfflineProvider: Scan QR code to authorize payment OfflineProvider-->>OmiseAPI: Return payment result OmiseAPI-->>Merchant: Send "charge.complete" webhook Merchant-->>Customer: Send charge result (e.g., via email)

Presenting the QR code

After the charge is created, display the QR code to the customer so they can scan it with the TrueMoney app. The QR code image is available as a download_uri nested inside the charge object:

charge
  └── source
        └── scannable_code
              └── image
                    └── download_uri  (URL of the QR code image)

Render the download_uri value as an img tag on your checkout page:

<img src="{download_uri}" alt="TrueMoney QR code" />

Example scannable_code object:

{
  "object": "barcode",
  "type": "qr",
  "image": {
    "object": "document",
    "livemode": false,
    "id": "docu_test_616e6q70sr2xc8a3ugl",
    "deleted": false,
    "filename": "qrcode.png",
    "location": "/charges/chrg_test_616e6q4xsj0jk1y0ozi/documents/docu_test_616e6q70sr2xc8a3ugl",
    "kind": "qr",
    "download_uri": "https://api.omise.co/charges/chrg_test_616e6q4xsj0jk1y0ozi/documents/docu_test_616e6q70sr2xc8a3ugl/downloads/989A209A9C571BFB",
    "created_at": "2024-09-23T03:18:38Z"
  }
}

Note: The QR code is valid for 24 hours from charge creation, as indicated by the expires_at field on the charge object. After this window, the charge status changes to expired and the QR code can no longer be used. You should display a countdown or expiry notice to the customer.

Authorizing the charge

The customer scans the QR code using the TrueMoney app on their phone. This authorizes the charge with TrueMoney's offline provider.

Receiving the charge completion event

Use webhook events to be notified when a charge is completed. Configure a webhook endpoint on your dashboard to receive the following events:

Event Description
charge.create Fired when the charge is created with status: pending.
charge.complete Fired when the charge is authorized (successfully or not).

Checking the charge status

After receiving the charge.complete event, retrieve the charge using its id and verify that the status in the charge object matches the status in the webhook event.

Status Meaning
successful Payment was received. Fulfill the order.
failed Payment failed. Check failure_code and failure_message for details.
expired The customer did not scan the QR code within 24 hours. The charge cannot be reused.

Failure codes:

Failure Code Description Recommended action
failed_processing General payment processing failure. Ask the customer to retry or select an alternative payment method.

Testing

You can simulate the full payment flow in test mode without real funds.

  1. Create a source and charge using your test public and secret keys.
  2. Open the Omise dashboard and navigate to the charge.
  3. Click Actions and select Mark as Successful or Mark as Failed to simulate the customer scanning the QR code.
  4. Verify that your webhook endpoint receives the charge.complete event and that your application updates the order status accordingly.

Test mode keys are prefixed with pkey_test_ (public) and skey_test_ (secret). Live mode keys are prefixed with pkey_ and skey_.


Voids and refunds

TrueMoney charges can only be voided on the same day as the transaction authorization. Refunds can be made up to 30 days after the charge is created.

Cancel within a day (Voids) Cancel next day
(Refunds)
Payment Methods Fully Partial Fully Partial
TrueMoney Wallet
(Wallet Balance)
Bank Account
Credit/Debit Card
Pay Next (Full payment)
Pay Next Extra (Full payment)

See the Refunds API documentation for information on refunding a TrueMoney charge.

  • All five payment methods support full voids on the same day as authorization.
  • No payment method supports partial voids.
  • For refunds, TrueMoney Wallet (Wallet Balance) and Bank Account support both full and partial refunds.
  • Credit/Debit Card, Pay Next, and Pay Next Extra support full refunds only. No partial refunds are available for those three methods.

Limits

All amounts are in the smallest currency unit (satang). For example, ฿20.00 = 2000.

TrueMoney Wallet

Limit Amount (satang) Amount (THB)
Minimum 2000 ฿20.00
Maximum 5000000 ฿50,000.00

Pay Next

Limit Amount (satang) Amount (THB)
Minimum 30000 ฿300.00

Note: Maximum limit for Pay Next depends on each business.

Pay Next Extra

Limit Amount (satang) Amount (THB)
Minimum 100000 ฿1,000.00

Note: Maximum limit for Pay Next Extra depends on each business.


FAQ

What happens if the customer does not scan the QR code in time?

The charge expires after 24 hours, as shown in the expires_at field of the charge object. When this happens, the charge status changes to expired. The QR code can no longer be used, and you must create a new charge to accept payment from the customer.

Can I create a new charge if the previous one expired?

Yes. Create a new source and charge using the same flow. The expired charge has no effect on the new one.

Can I partially void a TrueMoney charge?

No. Voids on TrueMoney charges are full only. Partial cancellations are not supported within the same day.

Which payment methods support partial refunds?

Only TrueMoney Wallet (Wallet Balance) and Bank Account support partial refunds. Credit/Debit Card, Pay Next, and Pay Next Extra support full refunds only.

What should I do if I receive a failed_processing failure code?

This indicates a general processing failure on TrueMoney's side. Ask the customer to retry the payment. If the problem persists, advise the customer to check their TrueMoney app or select a different payment method. Contact support@omise.co if failures continue in live mode.

How do I know whether to void or refund a charge?

If you need to cancel a charge on the same day it was authorized, use a void. If you need to cancel or return funds on a subsequent day (up to 30 days after charge creation), use a refund. See the Voids and refunds table for per-payment-method support.

Is TrueMoney QR available outside Thailand?

No. TrueMoney QR is currently supported in Thailand only and processes payments in THB.

Where can I find my API keys?

See How to access Omise API keys.

What are the minimum and maximum payment amounts for TrueMoney QR?

  • The minimum and maximum amounts depend on the payment method used at the TrueMoney app.
  • TrueMoney Wallet has a minimum of 2000 (฿20.00) and a maximum of 5000000 (฿50,000.00).
  • Pay Next has a minimum of 30000 (฿300.00).
  • Pay Next Extra has a minimum of 100000 (฿1,000.00).
  • The maximum limits for Pay Next and Pay Next Extra depend on each business. See Limits for the full breakdown.

Why is my charge being rejected — could it be a limits issue?

If a charge fails immediately at creation, verify that the amount meets the minimum for the intended payment method and does not exceed the maximum where applicable. All amounts must be submitted in satang (the smallest currency unit) — for example, ฿300.00 must be submitted as 30000, not 300.


Omise uses cookies to improve your overall site experience and collect information on your visits and browsing behavior. By continuing to browse our website, you agree to our Privacy Policy. Learn more