Skip to main content

Alipay+ Merchant-Presented Mode (MPM)

Accept offline in-store payments via Alipay+ users scanning merchant-generated QR codes through supported wallet applications.

Overviewโ€‹

Merchant-Presented Mode (C scan B) allows customers to pay by scanning a QR code displayed by the merchant at their point-of-sale (POS) terminal. This is ideal for in-store payments where the merchant generates a unique QR code for each transaction.

Key Features:

  • โœ… In-store payments - Perfect for retail, restaurants, and physical stores
  • โœ… Multiple wallets - Accept payments from 12+ Alipay+ partner wallets
  • โœ… Quick checkout - Customers scan and pay in seconds
  • โœ… No hardware required - Display QR on any screen or print
  • โœ… Cross-border - Accept payments from international tourists

Supported Regionsโ€‹

RegionCurrencyMin AmountMax AmountAPI Version
ThailandTHBเธฟ20.00เธฟ150,000.002017-11-02
SingaporeSGD$1.00$20,000.002017-11-02

Supported Walletsโ€‹

WalletThailandSingapore
Alipay CNโœ…โœ…
Alipay HKโœ…โœ…
KakaoPayโœ…โœ…
GCashโœ…โ€”
Touch 'n Goโœ…โœ…
TrueMoneyโœ…โ€”

How It Worksโ€‹

Payment Flow:

  1. Merchant creates payment source with Omise
  2. QR code is displayed on POS terminal
  3. Customer opens their wallet app (GCash, KakaoPay, etc.)
  4. Customer scans the QR code
  5. Customer confirms payment in their app
  6. Merchant receives webhook confirmation

Typical completion time: 30 seconds - 1 minute

Implementationโ€‹

Step 1: Create Sourceโ€‹

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

Step 2: Create Chargeโ€‹

curl https://api.omise.co/charges \
-u $OMISE_SECRET_KEY: \
-d "amount=150000" \
-d "currency=THB" \
-d "source=src_test_xxx"

Step 3: Display QR Codeโ€‹

const qrCodeUrl = charge.source.scannable_code.image.download_uri;
// Display this QR code on your POS terminal

Step 4: Handle Webhookโ€‹

app.post('/webhooks/omise', (req, res) => {
const event = req.body;

if (event.key === 'charge.complete') {
const charge = event.data;
if (charge.status === 'successful') {
// Payment successful - fulfill order
console.log('Payment received!');
}
}

res.status(200).send('OK');
});

Charge Lifecycleโ€‹

StatusDescription
pendingQR code displayed, awaiting payment
successfulPayment completed
failedPayment failed or declined
expiredPayment window expired (24 hours)

Refunds & Voidingโ€‹

  • Void window: Until 16:15 UTC on the transaction date
  • Refund window: Within 1 year of the transaction
  • Partial refunds: Supported

Best Practicesโ€‹

  1. Display clear instructions - Show which wallets are accepted
  2. Set appropriate timeout - Show countdown timer for QR expiry
  3. Handle webhooks - Don't rely solely on customer return
  4. Test thoroughly - Test with each supported wallet

FAQโ€‹

What is Merchant-Presented Mode (MPM)?

MPM, also known as "C scan B" (Customer scans Business), is an in-store payment method where the merchant displays a QR code and the customer scans it with their wallet app. This is ideal for retail environments with POS terminals.

Which wallets can customers use to scan my QR code?

Customers can use any Alipay+ partner wallet including: Alipay CN, Alipay HK, KakaoPay, GCash, Touch 'n Go, and TrueMoney. The specific wallets available depend on your region (Thailand or Singapore).

How long is the QR code valid?

QR codes expire 24 hours after creation. However, for better customer experience, we recommend displaying a countdown timer and regenerating QR codes for transactions that take longer than expected.

Can I void or refund MPM transactions?

Yes. You can void transactions until 16:15 UTC on the transaction date. Refunds (full or partial) are supported within 1 year of the original transaction.

Do I need special hardware for MPM?

No special hardware is required. You can display the QR code on any screen (tablet, monitor, phone) or even print it. The customer uses their own phone to scan and pay.

What happens if the customer's payment fails?

If payment fails, you'll receive a webhook with status: failed and a failure_code explaining the reason (e.g., insufficient_fund, payment_rejected). Display an appropriate error message and allow the customer to retry.