How Does WeChat Enable Secure Offline Payments? Inside Time‑Based One‑Time Codes

This article explains the technical mechanism behind WeChat's offline payment, detailing how a time‑based one‑time dynamic code is generated on the device, verified by the server, and destroyed to protect user accounts even without network connectivity.

Lobster Programming
Lobster Programming
Lobster Programming
How Does WeChat Enable Secure Offline Payments? Inside Time‑Based One‑Time Codes

Offline Payment Overview

In environments without network connectivity, WeChat still allows users to complete payments by generating a QR code that contains a dynamic token. The cashier’s scanner, which has network access, reads this code and contacts the WeChat server to finalize the transaction.

1. One‑Time Dynamic Code Based on Time

Storing a static QR code on the phone would be insecure because anyone who obtains it could reuse it indefinitely. To prevent this, WeChat issues a time‑based one‑time dynamic code that changes every short interval (typically 60 seconds).

2. How the Dynamic Code Works

When the user’s device is online, the WeChat server silently provides a long‑term secret seed key, which is stored locally on the phone. The following diagram (originally in the article) illustrates this process:

When the device is offline and a payment is needed, the phone combines the stored seed key with the current system time and applies HMAC‑SHA256 to produce a dynamic token, which is then encoded into a QR code. The pseudo‑code is:

def generate_offline_code(seed_key, timestamp):
    # 1. Align to time window (usually 60 s per window)
    time_counter = floor(timestamp / 60)
    # 2. Derive temporary session key
    session_key = HMAC_SHA256(seed_key, str(time_counter))
    # 3. Generate dynamic token
    token = truncate(HMAC_SHA256(session_key, user_id + nonce))
    # 4. Assemble payload for QR code
    payload = {
        "user_id": user_id,
        "token": token,
        "time_counter": time_counter,
        "type": "OFFLINE_PAYMENT"
    }
    # 5. Encode as QR code
    return qr_encode(payload)

Although the phone is offline, the cashier’s scanner is online. After scanning the QR code, the scanner sends the payload to the WeChat server (illustrated in the next diagram):

The server retrieves the same seed key for the user, recomputes the expected token using the same algorithm, and compares it with the token received from the QR code. If the tokens match (allowing a ±1 window for clock drift), the payment is approved. Immediately after a successful transaction, the server marks the token as destroyed.

Destroying the token prevents replay attacks: even if the QR code is captured after payment, the server will reject any subsequent use because the token is no longer valid.

Key Security Points

Offline payment relies on a secret seed key and local token generation.

The cashier’s scanner validates the token against the server, including user ID lookup, seed retrieval, token recomputation, and time‑window tolerance.

Tokens are time‑limited (usually refreshed every 60 seconds) to mitigate screenshot replay attacks.

Offline payments are subject to amount limits (e.g., per‑transaction or daily caps) to further reduce risk.

QR codeWeChatHMACoffline paymenttime-based token
Lobster Programming
Written by

Lobster Programming

Sharing insights on technical analysis and exchange, making life better through technology.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.