How to Read EMV Tag 9F02 — Authorized Amount (n6 BCD) Explained
If you’ve ever debugged an EMV transaction and seen 9F0206000000011934 in a TLV stream, you’ve encountered Tag 9F02 — the Amount, Authorized. This 6-byte value encodes the transaction amount in a format called BCD (Binary-Coded Decimal) with a length specifier of n6. Understanding how to read it is fundamental to EMV transaction analysis, payment terminal development, and card simulation.
1. What Is Tag 9F02?
EMV Tag 9F02 is defined in EMV Book 3 (Application Specification) as:
| Field | Value |
|---|---|
| Tag | 9F02 |
| Name | Amount, Authorized |
| Format | n6 (numeric, 6 bytes) |
| Encoding | BCD (Binary-Coded Decimal) |
| Length | 06 bytes (12 nibbles / 12 decimal digits) |
| Source | Terminal → Card (in CDOL1 / CDOL2) |
| Used in | GENERATE AC command, cryptogram computation |
| Mandatory | Yes — always present in CDOL1 |
The terminal sends this value to the card during the GENERATE AC command as part of CDOL1 (Card Risk Management Data Object List 1). The card uses it when computing the ARQC (Authorization Request Cryptogram) or TC (Transaction Certificate), binding the transaction amount into the cryptographic proof.
2. BCD Encoding — How n6 Works
BCD (Binary-Coded Decimal) encodes each decimal digit (0-9) into exactly one nibble (4 bits). Since a byte has 2 nibbles, each byte holds 2 decimal digits. For n6 (6 bytes), you get 12 decimal digits:
Amount: $119.34
Decimal representation: 0 0 0 0 0 0 0 1 1 9 3 4
| | | | | | | | | | | |
Hex bytes: 00 00 00 01 19 34
Tag 9F02 encoded as: 9F 02 06 00 00 00 01 19 34
--- -- -- ---------------
Tag Len 6 bytes BCD
000000011934 = 11934 minor units = $119.34 (if USD with 2 decimal places).BCD Nibble Map (byte by byte)
| Byte | Hex | High Nibble | Low Nibble | Digit Position |
|---|---|---|---|---|
| 1 | 00 | 0 | 0 | d1 d2 |
| 2 | 00 | 0 | 0 | d3 d4 |
| 3 | 00 | 0 | 0 | d5 d6 |
| 4 | 01 | 0 | 1 | d7 d8 |
| 5 | 19 | 1 | 9 | d9 d10 |
| 6 | 34 | 3 | 4 | d11 d12 |
The 12 digits represent: d1 d2 d3 d4 d5 d6 d7 d8 d9 d10 d11 d12 = 000000011934
3. Currency and the Minor Unit
Tag 9F02 alone doesn’t tell you the currency. The terminal also sends Tag 9F42 (Currency Code) and the card references Tag 9F44 (Currency Exponent) to determine how many digits are the minor unit (decimal places).
| Currency Code (9F42) | Currency | Minor Unit (exponent) | 9F02 = 000000011934 means |
|---|---|---|---|
| 0840 | USD | 2 | $119.34 |
| 0978 | EUR | 2 | €119.34 |
| 0826 | GBP | 2 | £119.34 |
| 0156 | CNY | 2 | ¥119.34 |
| 0392 | JPY | 0 | ¥11934 (no decimals) |
| 0364 | IRR | 0 | 11934 rial (no decimals) |
| 0336 | BHD | 3 | 11.934 BHD (3 decimal places) |
4. Parsing 9F02 in Python
# Parse EMV Tag 9F02 from BCD to decimal amount
def parse_9f02(hex_bcd: str, minor_unit: int = 2) -> float:
"""
Parse EMV Tag 9F02 (Amount, Authorized) from BCD hex string.
Args:
hex_bcd: 12-character hex string (e.g. '000000011934')
minor_unit: number of decimal places (from 9F44 or currency code table)
Returns:
Float amount (e.g. 119.34)
Example:
>>> parse_9f02('000000011934', minor_unit=2)
119.34
>>> parse_9f02('000000011934', minor_unit=0) # JPY
11934.0
"""
# BCD: each hex digit IS a decimal digit
# Just read the hex string as a decimal number
raw_value = int(hex_bcd)
return raw_value / (10 ** minor_unit)
# Full example: extract 9F02 from a TLV stream
def extract_amount_from_tlv(tlv_hex: str, minor_unit: int = 2) -> float:
"""Find and parse 9F02 from an EMV TLV data string."""
tag = '9F02'
pos = tlv_hex.upper().find(tag)
if pos == -1:
raise ValueError('9F02 not found in TLV data')
# Skip tag (4 chars) + read length (2 chars)
len_pos = pos + 4
length = int(tlv_hex[len_pos:len_pos+2], 16)
# Read value
val_pos = len_pos + 2
value_hex = tlv_hex[val_pos:val_pos + (length * 2)]
return parse_9f02(value_hex, minor_unit)
# Usage
tlv_data = '9F02060000000119349F420208409F440102'
amount = extract_amount_from_tlv(tlv_data, minor_unit=2)
print(f'Authorized amount: ${amount:.2f}') # $119.34
5. Parsing 9F02 in JavaScript
/**
* Parse EMV Tag 9F02 (Amount, Authorized) from BCD hex string.
* @param {string} hexBcd - 12-char BCD hex (e.g. '000000011934')
* @param {number} minorUnit - decimal places (2 for USD/EUR, 0 for JPY)
* @returns {number} The amount as a float
*/
function parse9F02(hexBcd, minorUnit = 2) {
// BCD digits are directly the hex characters as decimal
const rawValue = parseInt(hexBcd, 10); // parse as base-10
return rawValue / Math.pow(10, minorUnit);
}
// Example
const hex9f02 = '000000011934';
const amount = parse9F02(hex9f02, 2);
console.log(`$${amount.toFixed(2)}`); // $119.34
// For JPY (no minor units)
const jpyAmount = parse9F02(hex9f02, 0);
console.log(`\u00a5${jpyAmount}`); // \u00a511934
// For BHD (3 minor units)
const bhdAmount = parse9F02('000000011934', 3);
console.log(`${bhdAmount} BHD`); // 11.934 BHD
000000011934 contains only digits 0-9 (no A-F), so reading it as base-10 gives the correct decimal value directly. This is the beauty of BCD — no base conversion needed.6. Where 9F02 Appears in EMV Transactions
Tag 9F02 flows through the EMV transaction in these key locations:
| Location | Command/Field | Description |
|---|---|---|
| CDOL1 | GENERATE AC (first) | Terminal sends 9F02 to card for ARQC/TC/AAC generation |
| CDOL2 | GENERATE AC (second) | Present in second GENERATE AC if issuer auth performed |
| Tag 9F10 | Issuer Application Data | Not 9F02 itself, but the card may echo the amount in IAD |
| CVR (within 9F10) | Card Verification Results | Card may indicate if amount exceeded floor limit |
| Tag 9F03 | Amount, Other | Companion tag for cashback amount (000000000000 if none) |
Full GENERATE AC CDOL1 Example
// CDOL1 data sent in GENERATE AC:
9F0206000000011934 // Amount, Authorized: $119.34
9F0306000000000000 // Amount, Other: $0.00 (no cashback)
9F1A020840 // Terminal Country Code: USA
9500020000 // TVR: all zeros (no terminal risk issues)
5F2A020840 // Transaction Currency Code: USD
9A03260805 // Transaction Date: 2026-08-05
9C0100 // Transaction Type: 00 (purchase)
9F370412345678 // Unpredictable Number: 0x12345678
82 // AIP: 0x820
7. Common Mistakes and Pitfalls
parseInt('000000011934', 16), you get 4532 — completely wrong. The BCD digits are decimal digits expressed in hex nibbles. Always parse as base-10.000000011934 means 11,934 yen, not $119.34. Always check Tag 9F42 (Currency Code) and 9F44 (Currency Exponent) to determine how many digits are fractional.06 bytes. If you see 9F0204, the data is malformed — pad with leading zeros to 12 digits.Self-Check: Decode These
| Hex BCD (9F02) | Currency | Minor Unit | Answer |
|---|---|---|---|
| 000000000500 | USD | 2 | $5.00 |
| 000100000000 | USD | 2 | $10,000.00 |
| 000000012345 | JPY | 0 | ¥12,345 |
| 000000123450 | BHD | 3 | 123.450 BHD |
| 000000999999 | EUR | 2 | €9,999.99 |
8. Maximum Amount and Edge Cases
With 12 decimal digits, the maximum n6 value is 999999999999 (12 nines). With a minor unit of 2, that’s $9,999,999,999.99 — nearly $10 billion, which exceeds any practical transaction amount. For currencies with exponent 0 (JPY, KRW), the max is 999,999,999,999 units.
| Edge Case | 9F02 Value | Meaning |
|---|---|---|
| Zero amount | 000000000000 | $0.00 — used in balance inquiry, pre-auth |
| Maximum | 999999999999 | Max representable (unlikely real transaction) |
| Cashback | 9F02 = purchase, 9F03 = cashback | Total = 9F02 + 9F03 |
| Refund | 9C = 20, 9F02 = refund amount | Positive BCD amount with type=refund |
9. Related EMV Tags
| Tag | Name | Format | Relation to 9F02 |
|---|---|---|---|
| 9F03 | Amount, Other | n6 BCD | Cashback amount; 000000000000 if none |
| 9F42 | Currency Code | n3 (hex) | ISO 4217 numeric code (0840=USD, 0978=EUR, ...) |
| 9F44 | Currency Exponent | n1 | Minor unit (decimal places) for 9F02 |
| 9C | Transaction Type | n1 | 00=purchase, 20=refund, 01=cash advance |
| 9F01 | ATC | n2 | Application Transaction Counter — pairs with amount in ARQC |
| 9F37 | Unpredictable Number | b4 | Fresh nonce for each GENERATE AC — with 9F02 in CDOL1 |
10. Quick Reference
Encoding: Each byte = 2 BCD digits (0-9) | Parse as: parseInt(hexStr, 10) / 10^exponent
Range: 000000000000 to 999999999999 | Sent by: Terminal → Card in CDOL1/CDOL2
Used in: GENERATE AC, ARQC/ARPC/TC/AAC computation | Mandatory: Yes