RFID / NFC Cards
Besides the dynamic QR code, users can identify themselves with a physical RFID card. The user registers the card once in the Cheers app; from then on, the card's serial number (UID) acts as their permanent Transaction Key at the till.
This page describes how the Cheers app reads the card during registration, so your POS or card reader can implement the same reading logic — both sides must derive the identical key from the same card.
How It Works
The key derivation is identical on both sides. If your reader derives the same key the app derived during registration, the API resolves the card to the user.
Card Technology
The Cheers app reads cards over the NFC-A (ISO/IEC 14443 Type A) interface, which covers the MIFARE family (Classic, Ultralight, DESFire, NTAG, ...). Only the UID (serial number) is read — the anti-collision identifier every ISO 14443-A card broadcasts. No sectors are authenticated, nothing is written to the card, and no NDEF payload is involved.
| Property | Value |
|---|---|
| Interface | ISO/IEC 14443 Type A (NFC-A / MIFARE) |
| What is read | UID (serial number) only |
| UID lengths | 4, 7, or 10 bytes — all supported |
| Data written to the card | None — the card is used purely as an identifier |
Because only the UID is read, the card does not need to be issued by Cheers — any ISO 14443-A card with a fixed UID works. Cards or devices with a randomized UID (some bank cards, phones in card-emulation mode) will not work, since the UID changes on every read.
Deriving the Transaction Key
The Transaction Key is the card UID, hex-encoded, lowercased, and left-padded with zeros to 32 characters:
- Read the UID in the byte order the card transmits it (UID byte 0 first).
- Hex-encode it and strip any separators (
:or-) your reader inserts. - Lowercase the result.
- Left-pad with
0to exactly 32 characters.
const transactionKey = uidHex
.replace(/[^0-9a-fA-F]/g, "") // strip ":" / "-" separators
.toLowerCase()
.padStart(32, "0");
Examples
| UID as read from the card | Length | Transaction Key |
|---|---|---|
04:1A:2B:3C:4D:5E:6F | 7 bytes | 000000000000000000041a2b3c4d5e6f |
A1:B2:C3:D4 | 4 bytes | 000000000000000000000000a1b2c3d4 |
Use the result as the X-Transaction-Key header in the preview call. The rest of the payment flow is identical to the QR-code flow — see Integration Flow.
Reader Checklist
Reader hardware (USB, keyboard-wedge, embedded modules) is often configurable, and the defaults vary. Verify all of these, otherwise the derived key will not match the one registered by the app:
| Requirement | Detail |
|---|---|
| Hex output | The UID must be emitted as hexadecimal, not converted to a decimal number. |
| Byte order | UID byte 0 first — the order the card transmits it. Some readers emit the UID reversed (little-endian); disable that option. |
| Full UID | Read the complete UID. Some readers truncate 7-byte UIDs to 4 bytes or drop the leading 04 manufacturer byte common on NXP cards. |
| Leading zeros | Zero bytes inside the UID must be kept (04:00:2B:... → 04002b...). Only the overall left-padding to 32 characters is added afterwards. |
| Lowercase | The app registers the key in lowercase — lowercase your value before sending it. |
Verification: scan a test card in the Cheers app (Profile → Settings → Physical cards → Add card → RFID). After the scan, the app displays the Card ID — the UID as uppercase hex without separators. Your reader must produce the same value; after lowercasing and left-padding it, you have the exact key the app registers.
QR Code vs. RFID Card
The two Transaction Key sources behave differently:
| QR code | RFID card | |
|---|---|---|
| Lifetime | One-time use, short expiry | Permanent until the user revokes the card |
| Reusable | No | Yes — the same key on every visit |
| Value | Generated dynamically by the app | Derived from the card UID |
| Prior registration | Not needed | Required — the user registers the card in the Cheers app first |
Any card produces a syntactically valid key, but a card that was never registered in the Cheers app does not belong to any user — the preview request will be rejected. If a card is declined, ask the guest to register it in the Cheers app (Profile → Settings → Physical cards) and try again.
Reference Implementation
This is how the Cheers app itself reads the card (React Native, using react-native-nfc-manager) — shown here as the reference for what your reader must reproduce:
import NfcManager, { NfcTech } from "react-native-nfc-manager";
import { Platform } from "react-native";
const readCard = async (): Promise<string | null> => {
try {
// MIFARE on iOS, NFC-A on Android — both expose the ISO 14443-A UID
await NfcManager.requestTechnology(
Platform.OS === "ios" ? NfcTech.MifareIOS : NfcTech.NfcA,
);
const tag = await NfcManager.getTag();
if (!tag?.id) return null;
// tag.id is the UID as hex, possibly colon-separated (e.g. "04:1A:2B:3C:4D:5E:6F")
const uidHex = tag.id.replace(/:/g, "");
// The registered Transaction Key: lowercase, left-padded to 32 chars
return uidHex.toLowerCase().padStart(32, "0");
} finally {
NfcManager.cancelTechnologyRequest().catch(() => {});
}
};