POS Terminal 与 Bank 之间的 ISO 8583 支付通信
结论
一条 ISO 8583 授权请求可以同时携带交易金额和磁道数据。金额通常放在 Data Element 4,Track 2 数据通常放在 Data Element 35,Track 1 数据可以放在 Data Element 45。
不过,这不意味着应该同时发送 Track 1 和 Track 2,也不意味着连接银行提供的 IP 和端口后就能直接交易。实际使用的报文字段、编码、报文头、长度前缀、加密方式和交易状态机,都必须按照收单银行的 POS 接入规范实现,并通过银行的联调、认证和安全审核。
生产系统一般优先发送 Track 2 Equivalent Data,不会同时发送两条磁道。最终要求取决于银行和卡组织的接入规范,不能只根据通用 ISO 8583 文档判断。
正确的业务链路
多数商户 POS 不直接连接发卡银行,而是连接收单银行或支付处理机构。典型链路如下:
Card
→ POS Terminal
→ Acquirer / Payment Processor
→ Card Network
→ Issuer Bank
只有目标银行明确提供了主机直连接口,程序才应直接连接该银行的 switching host。正式开发前,至少需要从银行取得以下资料:
- ISO 8583 接口规范,以及使用的版本或私有变体
- 每种交易对应的 MTI、Processing Code 和必填字段
- TCP 报文长度前缀和自定义 Header 格式
- 字段编码方式,例如 ASCII、EBCDIC、BCD 或二进制
- Bitmap、LLVAR、LLLVAR 的具体编码规则
- Terminal ID、Merchant ID、Acquirer ID 等分配信息
- MAC 算法、密钥交换和密钥轮换方案
- PIN Block 格式和 HSM 接入要求
- Sign-on、Echo、Key Exchange、Sign-off 流程
- 超时、重试、冲正、撤销、退款和结算规则
- 测试环境、测试卡、认证案例和上线流程
ISO 8583 定义的是报文结构,并没有完整规定 TCP 传输、报文头、安全机制和所有业务语义。即使两家银行都声称使用 ISO 8583,它们的报文也未必能够互通。
一次消费交易的大致过程
简化后的磁条卡消费流程通常包括:
- POS 读取卡片数据。
- POS 获取金额,并在终端侧进行校验。
- 如果交易需要验证持卡人,则采集 PIN 或签名。
- POS 生成唯一的终端流水号。
- 组装金融授权请求,例如
0200。 - 通过银行指定的安全连接发送请求。
- 银行返回响应,例如
0210。 - POS 根据响应码判断交易是批准、拒绝还是出现异常。
- 如果请求结果无法确定,应按照规范进行查询或冲正,不能直接重发消费请求。
- 后续按照银行要求完成批结算或清算。
MTI 并不是全球统一且固定不变的。某些接口使用 0100/0110 处理授权,使用 0200/0210 处理金融交易,另一些接口可能采用不同的安排。具体应以银行规范为准。
金额和磁道数据如何放入同一报文
按照常见的 ISO 8583 字段安排,一条消费请求可能包含:
| Data Element | 常见含义 |
|---|---|
| DE 2 | Primary Account Number |
| DE 3 | Processing Code |
| DE 4 | Amount, Transaction |
| DE 7 | Transmission Date and Time |
| DE 11 | Systems Trace Audit Number |
| DE 14 | Expiration Date |
| DE 22 | Point of Service Entry Mode |
| DE 25 | POS Condition Code |
| DE 35 | Track 2 Data |
| DE 41 | Card Acceptor Terminal Identification |
| DE 42 | Card Acceptor Identification Code |
| DE 49 | Currency Code |
| DE 52 | PIN Data,在需要联机 PIN 时使用 |
| DE 55 | EMV ICC Data |
| DE 64/128 | Message Authentication Code,具体位置取决于协议 |
金额和 Track 2 数据可以放在同一条消息中,例如:
MTI = 0200
DE 3 = 000000
DE 4 = 000000012345
DE 11 = 123456
DE 22 = 021
DE 35 = <Track 2 data>
DE 41 = TERM0001
DE 42 = MERCHANT0000001
DE 49 = 156
DE 4 通常使用固定长度,以货币的最小单位表示金额。如果该货币有两位小数:
000000012345
可能表示 123.45。但不能把小数位写死,程序应根据币种指数和银行规范处理。
Track 2 一般以 DE 35 的 LLVAR 数据发送。ISO 8583 报文通常不包含读取磁条时的起始符和结束符。因此,是否移除 ; 和 ?、使用 = 还是 D 作为分隔符,以及如何计算长度,都要以银行规定的字段编码规则为准。
不建议同时发送 Track 1 和 Track 2
DE 35 和 DE 45 虽然可以同时存在,但正常交易一般不需要这样处理。常见情况包括:
- 磁条交易发送 DE 35。
- 某些特定交易或主机规范要求发送 DE 45。
- EMV 芯片交易主要发送 DE 55,也可能在 DE 35 中携带 Track 2 Equivalent Data。
- 非接触式 EMV 交易同样不能按普通磁条数据处理。
- 手工输入卡号时,一般发送 DE 2 和 DE 14,并设置相应的 Entry Mode,而不是伪造磁道数据。
银行会按照自己的字段组合规则进行校验,是否允许同时发送 DE 35 和 DE 45,取决于这套规则。多发字段可能造成格式错误、交易被拒绝,甚至触发风控。
磁道数据解析不能只靠 Split
字符串分割适合用来演示基本思路,但不能直接用于支付终端,原因包括:
- 没有处理
%、;、?、换行和回车。 - Track 1 和 Track 2 同时出现时,
if/else if只会处理 Track 1。 - 没有检查字段数量和最小长度。
Substring遇到截断的数据会抛出异常。- 没有验证 LRC 或读卡器返回状态。
- 将卡号和磁道数据放入普通 UI 控件,会增加敏感数据的暴露范围。
- 完成支付授权通常不需要持卡人姓名。
如果只在隔离的测试环境中解析模拟数据,可以先分别提取两条磁道:
using System;
using System.Text.RegularExpressions;
public sealed record MagneticStripeData(
string? Track1,
string? Track2,
string? Pan,
string? ExpirationYYMM,
string? ServiceCode);
public static class MagneticStripeParser
{
private static readonly Regex Track1Regex = new(
@"%B(?<pan>[0-9]{1,19})\^(?<name>[^^]{0,26})\^(?<expiry>[0-9]{4})(?<service>[0-9]{3})(?<discretionary>[^?]*)\?",
RegexOptions.CultureInvariant);
private static readonly Regex Track2Regex = new(
@";(?<pan>[0-9]{1,19})=(?<expiry>[0-9]{4})(?<service>[0-9]{3})(?<discretionary>[^?]*)\?",
RegexOptions.CultureInvariant);
public static MagneticStripeData Parse(string raw)
{
ArgumentNullException.ThrowIfNull(raw);
Match track1 = Track1Regex.Match(raw);
Match track2 = Track2Regex.Match(raw);
if (!track1.Success && !track2.Success)
{
throw new FormatException("No valid Track 1 or Track 2 data was found.");
}
Match source = track2.Success ? track2 : track1;
return new MagneticStripeData(
Track1: track1.Success ? track1.Value : null,
Track2: track2.Success ? track2.Value : null,
Pan: source.Groups["pan"].Value,
ExpirationYYMM: source.Groups["expiry"].Value,
ServiceCode: source.Groups["service"].Value);
}
}
这段代码只负责识别输入格式,不代表它符合 PCI、EMV 或任何银行的生产要求。正则表达式也无法替代读卡器状态检查、设备认证和终端内核。
Track 1 中的姓名通常采用 SURNAME/FIRST NAME 格式,不能据此可靠判断固定的“名/姓”顺序。有效期格式是 YYMM,展示时可以转换成 MM/YY,但发送给主机时必须保留协议要求的格式。
ISO 8583 组包示意
如果不使用第三方库,就需要自行实现字段定义、Bitmap、变长字段和编码。可以先建立清晰的数据模型:
public sealed record IsoFieldDefinition(
int Number,
int MaxLength,
bool VariableLength,
int LengthDigits,
IsoEncoding Encoding);
public enum IsoEncoding
{
Ascii,
Bcd,
Binary
}
生成 Bitmap 时,每个存在的字段对应一个 bit。下面的代码只演示主 Bitmap 的位设置,不包括银行 Header、长度前缀和字段编码:
using System;
public static class IsoBitmap
{
public static byte[] CreatePrimary(params int[] fields)
{
var bitmap = new byte[8];
foreach (int field in fields)
{
if (field < 2 || field > 64)
{
throw new ArgumentOutOfRangeException(
nameof(fields),
"Primary bitmap supports fields 2 through 64.");
}
int bitIndex = field - 1;
int byteIndex = bitIndex / 8;
int bitInByte = bitIndex % 8;
bitmap[byteIndex] |= (byte)(0x80 >> bitInByte);
}
return bitmap;
}
}
实际组包还要处理以下内容:
[Length Prefix]
[Bank-specific Header]
[MTI]
[Primary Bitmap]
[Secondary Bitmap, if present]
[DE 2]
[DE 3]
[DE 4]
...
[MAC]
其中任何一项都可能使用 ASCII、BCD 或二进制编码。LLVAR 的长度头也可能是两个 ASCII 数字,或者一个压缩 BCD 字节。没有银行的接口文档,就无法安全地确定最终字节流。
连接不仅是 IP + Port
银行提供的地址和端口只是连接入口,还需要确认:
- 连接使用普通 TCP、TLS、VPN、专线还是双向 TLS。
- 是否需要将固定源 IP 加入白名单。
- 每条消息前是否包含 2 字节或 4 字节长度。
- 长度采用大端、Little Endian、ASCII 还是 BCD。
- 是否需要 TPDU、Network Header 或银行私有 Header。
- 使用长连接,还是每笔交易重新建立连接。
- 空闲连接如何保活。
- 是否需要
0800/0810Sign-on 和 Echo。 - 请求和响应如何关联。
- Socket 断开或超时时,如何判断交易状态。
需要特别注意,超时并不等于交易失败。银行可能已经处理了请求,只是响应在传输过程中丢失。此时直接重发 0200 可能造成重复扣款。通常应按照银行规范发送冲正,例如 0400/0410 或其他约定消息。
安全和合规要求
磁道数据是高度敏感的支付数据,不能按普通字符串处理。
以下要求尤其需要注意:
- 不要记录完整的 Track 1 或 Track 2。
- 不要在异常、调试日志、数据库或监控系统中保存磁道数据。
- 不要保存 CVV/CVC、PIN、PIN Block 或完整磁道数据。
- 展示 PAN 时,只显示必要的位数,例如掩码后的后四位。
- PIN 必须通过经过认证的安全 PIN 输入设备采集。
- PIN Block 和支付密钥通常必须交给 HSM 处理。
- 不要将 Track 数据长期保存在托管字符串中。
- 网络传输必须使用银行认可的加密和消息认证机制。
- 系统必须满足适用的 PCI DSS、PCI PTS、EMV 和卡组织规则。
- 读卡设备及其固件通常也需要经过认证,生产卡数据不能只靠 PC 上的文本输入框接收。
在生产环境中,“完全不使用 third party tools”通常并不现实。支付系统依赖经过认证的 POS 设备、EMV Kernel、密码设备、HSM、密钥管理设施和收单银行认证。自行编写 ISO 8583 编解码器在技术上可行,但自行替代这些安全组件通常很难通过认证,也会带来很高的资金和合规风险。
建议的实施顺序
- 与收单银行签订接入协议,取得正式接口规范。
- 确认交易类型,包括磁条、EMV 接触式、非接触式、手工输入或预授权。
- 获取银行分配的 Terminal ID、Merchant ID 和测试密钥。
- 使用经过认证的支付终端和安全 PIN 输入设备。
- 根据银行字段表实现 ISO 8583 Pack/Unpack。
- 实现网络 Header、长度前缀、TLS/VPN 和 MAC。
- 建立完整的交易状态机,覆盖超时、冲正、撤销、退款和结算。
- 使用银行测试环境和测试卡联调,不使用真实卡数据。
- 执行 PCI 和银行要求的安全评估。
- 通过银行认证后再进入生产环境。
参考资料
可以从以下规范入手,但部分完整标准需要购买或通过合作银行取得:
- ISO 8583 标准目录
- PCI Security Standards Council
- PCI SSC Document Library
- EMVCo Specifications
- EMVCo Contactless Specifications
- IBM:ISO 8583 概念说明
这些通用资料可以帮助理解基础结构,但最终决定字段、字节编码和业务流程的,仍是目标银行提供的 host interface specification、security specification 和 certification test cases。
备注:内容仅供参考。
