Skip to main content

QR Payments

Accept instant payments via QR codes through national real-time payment systems including PromptPay (Thailand), PayNow (Singapore), and DuitNow QR (Malaysia).

Overview

Information Accuracy

Payment method features, limits, user statistics, and fees are subject to change. Information is based on publicly available sources and may not reflect your specific merchant agreement. Always refer to official Omise documentation and your merchant dashboard for current, binding information.

QR payments allow customers to scan a QR code with their banking or wallet app to make instant payments. These are national payment schemes backed by central banks, offering real-time settlement and wide bank support.

Why QR Payments?

  • Instant - Real-time payment confirmation
  • 🏦 Bank-backed - National payment infrastructure
  • 📱 Convenient - Scan with any banking app
  • 💰 Low cost - Often lower fees than cards
  • 🔐 Secure - Bank-grade security
  • 🌐 Universal - Works with all banks in the country

Supported QR Payment Methods

Thailand 🇹🇭

MethodUsersTypeRefundableSettlement
PromptPay60M+National QR✅ YesInstant
TrueMoney QR30M+Wallet QR✅ Yes1-3 days

Singapore 🇸🇬

MethodUsersTypeRefundableSettlement
PayNow5M+National QR✅ YesInstant

Malaysia 🇲🇾

MethodUsersTypeRefundableSettlement
DuitNow QR30M+National QR✅ YesInstant
Maybank QR10M+Bank-specific✅ Yes1-3 days

How QR Payments Work

Customer Experience:

  1. Merchant displays QR code
  2. Customer opens their banking/wallet app
  3. Scans QR code with camera
  4. Reviews payment details
  5. Confirms with PIN/biometric (5 seconds)
  6. Receives instant confirmation

Typical completion time: 10-30 seconds

Implementation Overview

Basic Integration

const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});

// Create QR payment source
const source = await omise.sources.create({
type: 'promptpay', // or paynow, duitnow_qr, etc.
amount: 50000,
currency: 'THB'
});

// Get QR code image
console.log('QR Code:', source.scannable_code.image.download_uri);

// Create charge
const charge = await omise.charges.create({
amount: 50000,
currency: 'THB',
source: source.id
});

// Poll for payment status or use webhooks

Display QR Code and Poll for Status

<div class="qr-payment">
<h3>สแกนเพื่อชำระเงิน (Scan to Pay)</h3>
<img id="qr-code" src="{{ qr_code_url }}" alt="QR Code">

<div id="status">
<p>รอการชำระเงิน...</p>
<div class="spinner"></div>
</div>

<p class="instructions">
เปิดแอพธนาคารและสแกน QR Code ด้านบน
</p>
</div>

<script>
// Poll for payment status
const chargeId = '{{ charge_id }}';
const pollInterval = setInterval(async () => {
const response = await fetch(`/api/charges/${chargeId}/status`);
const data = await response.json();

if (data.status === 'successful') {
clearInterval(pollInterval);
window.location = '/payment-success';
} else if (data.status === 'failed') {
clearInterval(pollInterval);
document.getElementById('status').innerHTML =
'<p class="error">การชำระเงินล้มเหลว กรุณาลองใหม่</p>';
}
}, 3000); // Check every 3 seconds

// Timeout after 5 minutes
setTimeout(() => {
clearInterval(pollInterval);
document.getElementById('status').innerHTML =
'<p class="error">QR Code หมดอายุ กรุณาลองใหม่</p>';
}, 300000);
</script>

Comparison Matrix

FeaturePromptPayPayNowDuitNow QRMaybank QR
CountryThailandSingaporeMalaysiaMalaysia
Users60M+5M+30M+10M+
BanksAll Thai banksAll SG banksAll MY banksMaybank only
SpeedInstantInstantInstant1-3 days
Refunds✅ Yes✅ Yes✅ Yes✅ Yes
CurrencyTHBSGDMYRMYR

QR Payments vs Other Methods

FeatureQR PaymentsMobile BankingDigital Wallets
Speed10-30 sec30-90 sec30-90 sec
PlatformAny deviceMobile onlyMobile only
Bank SupportAll banksMajor banksSpecific wallets
Desktop✅ Yes❌ No❌ No
App RequiredBanking appBanking appWallet app
SetupNoneNoneWallet account

Use Cases

Perfect For:

In-Store Payments

  • Point of sale systems
  • Restaurant bills
  • Retail checkout

E-commerce (Desktop)

  • Desktop shoppers without mobile banking
  • Customers who prefer QR over cards
  • Cross-device payments

Bills and Invoices

  • Utility payments
  • Invoice settlements
  • B2B payments

Not Ideal For:

  • Mobile-only experiences (use mobile banking)
  • International customers (country-specific)
  • Very small amounts (minimum limits apply)

Implementation Patterns

Responsive QR Display

.qr-payment {
text-align: center;
padding: 30px;
}

#qr-code {
width: 100%;
max-width: 300px;
height: auto;
margin: 20px auto;
display: block;
border: 10px solid white;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}

@media (max-width: 768px) {
#qr-code {
max-width: 250px;
}
}

Status Polling

async function pollPaymentStatus(chargeId) {
const maxAttempts = 100; // 5 minutes (100 * 3 seconds)
let attempts = 0;

return new Promise((resolve, reject) => {
const interval = setInterval(async () => {
attempts++;

try {
const response = await fetch(`/api/charges/${chargeId}`);
const charge = await response.json();

if (charge.status === 'successful') {
clearInterval(interval);
resolve(charge);
} else if (charge.status === 'failed') {
clearInterval(interval);
reject(new Error('Payment failed'));
} else if (attempts >= maxAttempts) {
clearInterval(interval);
reject(new Error('Payment timeout'));
}
} catch (error) {
clearInterval(interval);
reject(error);
}
}, 3000);
});
}

Multi-Language Instructions

const instructions = {
th: {
title: 'สแกนเพื่อชำระเงิน',
step1: 'เปิดแอพธนาคาร',
step2: 'สแกน QR Code',
step3: 'ยืนยันการชำระเงิน'
},
en: {
title: 'Scan to Pay',
step1: 'Open your banking app',
step2: 'Scan the QR code',
step3: 'Confirm the payment'
}
};

Best Practices

1. Show Clear Instructions

<div class="qr-instructions">
<h4>วิธีชำระเงิน (How to Pay):</h4>
<ol>
<li>เปิดแอพธนาคารหรือ Mobile Banking</li>
<li>เลือก "สแกน QR" หรือ "Scan"</li>
<li>สแกน QR Code ด้านบน</li>
<li>ตรวจสอบจำนวนเงินและยืนยัน</li>
</ol>
<div class="bank-support">
<p><small>รองรับทุกธนาคารใน{{ country }}</small></p>
</div>
</div>

2. Handle QR Expiration

const QR_EXPIRY = 5 * 60 * 1000; // 5 minutes

setTimeout(() => {
if (!paymentCompleted) {
showMessage('QR Code หมดอายุ คลิกเพื่อสร้างใหม่');
enableRetry();
}
}, QR_EXPIRY);

3. Use Webhooks

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

if (event.key === 'charge.complete') {
const charge = event.data;

if (['promptpay', 'paynow', 'duitnow_qr'].includes(charge.source.type)) {
if (charge.status === 'successful') {
processOrder(charge.metadata.order_id);
}
}
}

res.sendStatus(200);
});

4. Optimize QR Size

// Larger QR for desktop, smaller for mobile
const qrSize = isMobile() ? 250 : 300;

img.style.width = `${qrSize}px`;
img.style.maxWidth = '100%';

5. Show Payment Apps

<div class="compatible-apps">
<p>ใช้ได้กับแอพ:</p>
<div class="app-icons">
<img src="/apps/banking-apps.svg" alt="Banking Apps">
<span>+ แอพธนาคารทุกธนาคาร</span>
</div>
</div>

Common Issues

Issue: QR Code not scanning

Solution:

  • Ensure proper contrast and size
  • Check QR code image quality
  • Display against white background
  • Minimum size: 200x200px

Issue: Payment timeout

Solution:

if (Date.now() - chargeCreatedAt > 5 * 60 * 1000) {
showMessage('QR Code expired. Generate new code?');
enableRetryButton();
}

Issue: Customer confusion

Solution: Show supported banks and apps clearly:

<div class="supported-banks">
<h4>รองรับทุกธนาคาร:</h4>
<div class="bank-logos">
<!-- Show bank logos -->
</div>
</div>

FAQ

What are QR payments?

QR payments use national payment schemes (PromptPay, PayNow, DuitNow) where customers scan a QR code with their banking app to make instant payments. Works with all banks in the country.

Which QR method should I use?

Use the national QR scheme for your target country:

  • Thailand: PromptPay
  • Singapore: PayNow
  • Malaysia: DuitNow QR (all banks) or Maybank QR (Maybank only)
Do customers need a special app?

No, customers can use their regular banking app. All banks in each country support their national QR payment scheme.

How long are QR codes valid?

Typically 5 minutes. After expiration, generate a new QR code for the customer.

Can I refund QR payments?

Yes, all QR payment methods support both full and partial refunds.

Do QR payments work on mobile?

Yes, but mobile banking apps provide better UX on mobile devices. QR payments are ideal for:

  • Desktop users
  • In-store payments
  • Cross-device payments (display QR on one device, scan with another)

Next Steps

  1. Choose your target country's QR method
  2. Implement source creation
  3. Display QR code on your site
  4. Set up status polling or webhooks
  5. Handle QR expiration
  6. Test with actual banking apps
  7. Go live

Ready to start? Choose your QR payment method: