QR Payment Systems for FinTech Engineering Teams

Boitumelo Mosia
Boitumelo Mosia
August 16, 2024
7 min read
QR Payment Systems for FinTech Engineering Teams

What QR Payments Are, Technically

A QR payment encodes payment initiation data in an image that a payer's camera can read. The payer scans, confirms, and the payment network processes the transfer. The QR code itself is not the payment: it is a data transport mechanism. What the code contains, how it expires, and what happens after the scan determines whether the system is fast, secure, and reconcilable.

This post covers the engineering decisions for teams building payment systems that include QR acceptance: static vs dynamic QR, standards, transaction state management, security controls, and reconciliation. If you are also building the wallet that accepts QR payments, see digital wallet development for FinTech teams for the ledger, KYC, and compliance architecture that sits beneath the QR layer.

Static vs Dynamic QR: Different Risk Profiles

Static QR codes encode a fixed payload (typically a merchant identifier and optionally a fixed amount). The same image can be reused indefinitely. Dynamic QR codes are generated per transaction: they include a unique transaction reference, an amount, an expiry time, and a checksum.

The fraud profile differs significantly. Static QR codes are vulnerable to substitution attacks: a bad actor prints a QR sticker and places it over the merchant's legitimate code. The payer scans, enters an amount, pays, and the funds go to the attacker's account. This attack is well-documented in markets where static QR adoption is high (Southeast Asia, Sub-Saharan Africa). Mitigations are limited: merchant name confirmation on the payer's confirmation screen is the main defence, but users who scan quickly skip this check.

Dynamic QR codes have a much better fraud profile. The transaction reference in the payload ties the payment to a specific initiated transaction on your server: substituted codes either fail (transaction not found) or expire. The trade-off is infrastructure: you need a QR generation service that creates codes on demand and a backend that can match inbound payment confirmations to outstanding QR sessions.

For consumer-facing in-store or peer-to-peer payments, dynamic QR is the correct default. Static QR has legitimate use cases (low-value, low-risk contexts like small merchant self-checkout or tipping), but the risk profile needs to be understood and accepted explicitly, not defaulted into.

Standards: EMVCo and Proprietary Schemes

EMVCo QR codes (EMV QRCPS Merchant Presented Mode) are the interoperable standard. The payload format is defined, allowing any compliant payer app to read any compliant merchant QR, regardless of which acquiring bank or wallet provider they use. India's UPI, Singapore's PayNow, and several national payment systems in Southeast Asia have built on EMVCo as the QR encoding standard. If you are building a merchant-facing QR acceptance product intended to work across multiple wallet providers, EMVCo is the standard to target.

Proprietary QR schemes (WeChat Pay, Alipay, M-Pesa) use their own payload formats. Each requires scheme-specific integration. Alipay and WeChat Pay are relevant for merchants serving Chinese tourists and for cross-border use cases; M-Pesa QR is relevant for Kenyan and East African markets. These are closed-loop: a WeChat Pay QR can only be paid by WeChat Pay users. You do not get cross-network interoperability without a separate integration per scheme.

In practice, many merchant acquirers and payment orchestration layers (Adyen, Stripe, Checkout.com) abstract the QR scheme differences behind a unified API, generating scheme-appropriate QR codes based on the payer's wallet type. If you are building a merchant acceptance product rather than a wallet product, this abstraction layer is usually the right starting point. For the broader build vs buy decision on payment infrastructure, see custom payment gateway vs Stripe or Adyen.

QR Generation Service

A QR generation service takes a payment initiation request (amount, currency, merchant ID, expiry) and returns a QR code image and a session token. The session token is the reference your payment processing pipeline uses to match inbound payment events to the right transaction.

Key design decisions for the generation service:

Expiry enforcement. Dynamic QR codes should expire after a configurable window (typically 5 to 15 minutes for in-store, shorter for high-velocity environments). Expired sessions must be rejected server-side even if the QR image is technically scannable. Store the expiry time in your session record and check it on every inbound payment attempt.

Single-use enforcement. A dynamic QR session should only be payable once. Set the session state to PAID or EXPIRED atomically when the first successful payment confirmation arrives. Use database-level locking or an idempotency key to prevent double-spend on concurrent payment confirmations hitting the same session.

Amount confirmation. Where the QR encodes the amount (as in most dynamic implementations), the payer's app should display the amount explicitly before the user confirms. Your server must also validate that the amount presented to the payer matches the session amount: reject payments that arrive with a different amount than the session specifies.

For FinTech software development organisations building QR acceptance at merchant scale, session management and idempotency design are where most production incidents originate: build these with the same rigour as the transaction engine.

Transaction State and Status Delivery

After a payer scans and confirms, your system needs to know whether the payment succeeded. There are two standard patterns: polling and webhooks.

Polling works for in-store contexts where the merchant terminal or POS is waiting for confirmation: the POS polls the QR session endpoint every one to two seconds. Simple to implement, slightly chatty. Set a reasonable polling timeout (30 to 60 seconds) and handle the session expiry case cleanly: a timed-out session is not a failed payment, it is an unknown outcome that may need manual reconciliation.

Webhooks are better for mobile and asynchronous contexts. When the payment network confirms the transaction, your payment processor pushes an event to your endpoint. You update the session state and push a notification to the merchant or payee. Webhooks require endpoint security (HMAC signature verification on every inbound event) and a retry-safe processing handler (idempotency on the event ID).

For high-reliability implementations, use both: webhooks for speed, polling as a fallback for cases where the webhook did not arrive.

Security Controls

Beyond static vs dynamic QR distinction, the security controls that matter most for production QR payment systems are:

Anti-spoofing confirmation. The payer's app should display the merchant name (pulled from the session record on your server, not from the QR payload) before confirmation. This catches substituted QR codes where the attacker has a valid QR but for their own account: the merchant name will not match what the payer expects.

Velocity controls. Monitor for unusual patterns: multiple QR sessions generated in rapid succession from one merchant, multiple payment attempts on the same session, high-value transactions from new payer accounts. These patterns indicate either a misconfigured integration or active fraud.

Amount limits. Apply transaction limits per QR session at the network layer, not just the application layer. Limits enforced only in the UI can be bypassed by API-level attacks.

Session auditing. Log every event in the QR session lifecycle: generated, scanned, payment initiated, confirmed, expired, rejected. The full audit trail is required for dispute resolution and will be requested in any significant fraud investigation.

Reconciliation

QR payment reconciliation requires matching three records: the QR session (generated by your system), the payment network's transaction record (from your processor's settlement file), and the ledger entry in the recipient's wallet or merchant settlement account.

Breaks occur when: a payment network confirms a transaction that has no matching QR session (possible if session data was not correctly persisted); a QR session is marked PAID but no corresponding network transaction appears in settlement (possible if the payment network callback was received but the network transaction was later reversed); or the amount on the network record differs from the session amount.

Run reconciliation at settlement cycle frequency (typically end of day for most payment networks, more frequently for faster payment schemes). Automated reconciliation should flag breaks within minutes of the settlement file arriving: manual review should only be required for exceptions, not as the primary reconciliation method.

FAQ

How do QR payments compare to NFC for in-store acceptance?

NFC (contactless card and mobile wallet tap) requires POS terminal hardware with an NFC reader. QR requires only a screen (merchant-presented mode) or a camera (payer-presented mode). QR is significantly cheaper to deploy at scale, particularly relevant for small merchants in emerging markets where NFC terminal penetration is low. NFC is faster at the point of interaction and is the default in markets with high card penetration (US, UK, Europe). For FinTech products targeting underserved merchant segments or markets with low NFC infrastructure, QR is the practical choice.

Can QR payments work offline?

Fully offline QR payments are not possible for real-time payment networks: the payment confirmation requires a live connection to the payment network. Some implementations allow the QR to be generated offline (merchant pre-generates a static QR or a locally-generated dynamic QR), but payment confirmation still requires connectivity at some point. Offline QR implementations that defer settlement introduce credit risk and reconciliation complexity. For most FinTech use cases, assume online-only and design the UX around connectivity states rather than trying to support offline payments.

What fraud controls are most effective for QR payment systems?

The three highest-impact controls are: dynamic QR codes (eliminates substitution attacks), merchant name confirmation on the payer's confirmation screen (catches substitution attacks in static QR deployments), and velocity monitoring on QR session generation and payment attempts. Beyond these, payer-side device fingerprinting and transaction risk scoring (Sardine, Sift, or your payment processor's built-in risk tools) add a layer for account takeover and mule activity that QR-specific controls do not address.

Eliminate Delivery Risks with Real-Time Engineering Metrics

Our Software Engineering Orchestration Platform (SEOP) powers speed, flexibility, and real-time metrics.

As Seen On Over 400 News Platforms