Alipay+ User-Presented Mode (UPM)
Accept in-store payments from Alipay+ users by scanning the customer's payment barcode through your point-of-sale system (POS).
Overviewโ
User-Presented Mode (B scan C) allows merchants to scan a barcode or QR code displayed by the customer in their wallet app. This is ideal for high-volume retail environments where the merchant has a barcode scanner at their point-of-sale terminal.
Key Features:
- โ Fast checkout - Scan and process in seconds
- โ Multiple wallets - Accept payments from 12+ Alipay+ partner wallets
- โ Familiar flow - Similar to scanning product barcodes
- โ High volume - Perfect for busy retail environments
- โ Cross-border - Accept payments from international tourists
Supported Regionsโ
| Region | Currency | Min Amount | Max Amount | API Version |
|---|---|---|---|---|
| Thailand | THB | เธฟ20.00 | เธฟ150,000.00 | 2017-11-02 |
| Singapore | SGD | $1.00 | $20,000.00 | 2017-11-02 |
Supported Walletsโ
| Wallet | Thailand | Singapore |
|---|---|---|
| Alipay CN | โ | โ |
| Alipay HK | โ | โ |
| KakaoPay | โ | โ |
| GCash | โ | โ |
| Touch 'n Go | โ | โ |
| TrueMoney | โ | โ |
How It Worksโ
Payment Flow:
- Customer opens their wallet app
- Customer displays their payment barcode/QR code
- Merchant scans the barcode using POS terminal
- Charge is created automatically with the barcode
- Payment is processed instantly
- Both parties receive confirmation
Typical completion time: 5-15 seconds
Implementationโ
Create Charge with Barcodeโ
For UPM, you create the charge directly with the scanned barcode:
- cURL
- Node.js
- PHP
curl https://api.omise.co/charges \
-u $OMISE_SECRET_KEY: \
-d "amount=150000" \
-d "currency=THB" \
-d "source[type]=alipayplus_upm" \
-d "source[barcode]=2897991359827699709"
const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});
const charge = await omise.charges.create({
amount: 150000,
currency: 'THB',
source: {
type: 'alipayplus_upm',
barcode: '2897991359827699709' // Scanned from customer's wallet
}
});
console.log('Charge status:', charge.status);
<?php
$charge = OmiseCharge::create([
'amount' => 150000,
'currency' => 'THB',
'source' => [
'type' => 'alipayplus_upm',
'barcode' => '2897991359827699709'
]
]);
echo "Status: " . $charge['status'];
?>
Handle Responseโ
app.post('/pos/payment', async (req, res) => {
const { amount, barcode } = req.body;
try {
const charge = await omise.charges.create({
amount: amount,
currency: 'THB',
source: {
type: 'alipayplus_upm',
barcode: barcode
}
});
if (charge.status === 'successful') {
// Payment successful immediately
printReceipt(charge);
res.json({ success: true, charge_id: charge.id });
} else if (charge.status === 'pending') {
// Wait for webhook
res.json({ success: false, message: 'Awaiting confirmation' });
} else {
res.json({ success: false, message: charge.failure_message });
}
} catch (error) {
res.json({ success: false, message: error.message });
}
});
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') {
notifyPOS(charge.id, 'success');
} else if (charge.status === 'failed') {
notifyPOS(charge.id, 'failed', charge.failure_message);
}
}
res.status(200).send('OK');
});
Charge Status Valuesโ
| Status | Description |
|---|---|
pending | Awaiting authorization from wallet |
successful | Payment completed |
failed | Payment declined or failed |
expired | Charge time limit exceeded |
Failure Codesโ
| Code | Description |
|---|---|
payment_expired | Payment expired |
payment_rejected | Payment rejected by issuer |
insufficient_fund | Insufficient funds or limit reached |
failed_processing | General processing failure |
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โ
- Train staff - Ensure staff know how to prompt customers to show barcode
- Good scanner - Use a reliable barcode scanner
- Handle errors - Display clear error messages
- Fast timeout - UPM should be nearly instant
FAQโ
What is User-Presented Mode (UPM)?
UPM, also known as "B scan C" (Business scans Customer), is an in-store payment method where the customer displays a barcode/QR code from their wallet app and the merchant scans it. This is faster than MPM and ideal for high-volume retail.
What's the difference between MPM and UPM?
In MPM, the merchant displays a QR code and the customer scans it. In UPM, the customer displays a barcode and the merchant scans it. UPM is typically faster (5-15 seconds) because it requires fewer steps from the customer.
What type of scanner do I need?
Any standard barcode scanner that can read 1D and 2D barcodes will work. Most modern POS systems already include compatible scanners. The scanner needs to read the numeric barcode from the customer's wallet app.
Which wallets support UPM?
UPM is supported by all Alipay+ partner wallets: Alipay CN, Alipay HK, KakaoPay, GCash, Touch 'n Go, and TrueMoney. Availability depends on your region.
How do I handle payment failures?
Payment failures are returned immediately in the charge response. Check the failure_code field for details (e.g., insufficient_fund, payment_rejected, payment_expired). Display a clear error message and allow retry.
Is UPM faster than MPM?
Yes, UPM is typically faster. Since you're scanning a pre-generated barcode from the customer's app, there's no need to generate a QR code or wait for the customer to scan. Typical completion time is 5-15 seconds.
Related Resourcesโ
- Alipay+ Overview - Full Alipay+ ecosystem guide
- Alipay+ MPM - Merchant-Presented Mode
- QR Payments - Other QR payment methods