PAYATHON 2026

如何解析和解码 Revolut UK Faster Payments 的 37 字符交易标识符?

支付阿杰

明确结论

仅凭这段字符串,无法可靠解码 Revolut 的交易元数据结构。通过逆向分析猜测出的字段含义,也不应直接用于自动记账或 ISO 20022 映射。

在没有 Revolut 官方字段规范、接口契约或足够多已核验样本的情况下,应将它视为不透明的外部标识符(opaque identifier):完整保存并精确匹配,不要拆成日期、账户、序列号等业务字段。

示例:

REV859314XXXXXXXXXXXXXXXX20260805826230120

实际包含 42 个 ASCII 字符,而不是 37 个:

REV       = 3
859314    = 6
XXXXXXXXXXXXXXXX = 16
20260805826230120 = 17
合计      = 42

因此,首先要确认“37-character”究竟来自接口定义、日志字段宽度,还是人工描述错误。如果长度前提有误,所有固定位置解析都会发生偏移。

为什么不能根据外观直接解码

这个值可以按外观切成几段:

REV | 859314 | XXXXXXXXXXXXXXXX | 20260805826230120

但这种切分只说明字符串存在某种视觉模式,无法证明各段分别代表什么。

例如,末尾以 20260805 开头,看起来很像 YYYYMMDD 日期,但它也可能是:

  • 随机或半随机编号;
  • 内部流水号的一部分;
  • 分片键或批次编号;
  • 编码后的时间信息;
  • 多个内部字段拼接后的结果;
  • 仅供特定渠道或历史系统使用的显示引用。

即使某批样本的前八位都与付款日期相同,也不能认定这个位置始终表示日期。系统升级、付款通道差异,以及重试、退回或冲正交易,都可能采用不同的生成规则。

X 也不一定是实际字段内容。它可能是脱敏占位符、日志导出时添加的掩码,或者示例数据经过替换后的字符。如果原始日志中也固定出现 X,还需要确认掩码发生在 Revolut、Open Banking provider、中间件,还是日志采集层。

推荐的数据处理方式

1. 保存未经修改的原始值

数据库应保留完整标识符,不要进行数字转换、大小写归一化或截断:

CREATE TABLE payment_identifiers (
    id BIGSERIAL PRIMARY KEY,
    provider VARCHAR(32) NOT NULL,
    identifier_type VARCHAR(64) NOT NULL,
    raw_identifier VARCHAR(255) NOT NULL,
    observed_length INTEGER NOT NULL,
    source_system VARCHAR(64),
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),

    UNIQUE (provider, identifier_type, raw_identifier)
);

唯一键可以设计为:

provider + identifier_type + raw_identifier

不要假设这段字符串在所有支付机构、账户和时间范围内都全局唯一。

2. 只做结构验证,不做语义解码

可以检查字符集、长度和已经观察到的格式,但不要把未经确认的片段写入正式业务字段:

import re
from dataclasses import dataclass


@dataclass(frozen=True)
class RevolutReference:
    raw: str
    length: int
    prefix: str | None
    format_matched: bool


OBSERVED_PATTERN = re.compile(
    r"^(?P<prefix>REV)(?P<section_a>[0-9]{6})"
    r"(?P<section_b>[A-Z0-9]{16})"
    r"(?P<section_c>[0-9]{17})$"
)


def inspect_reference(value: str) -> RevolutReference:
    if not isinstance(value, str):
        raise TypeError("identifier must be a string")

    # 不调用 strip(),避免无意中改变原始标识符。
    if not value:
        raise ValueError("identifier must not be empty")

    if not value.isascii():
        raise ValueError("identifier contains non-ASCII characters")

    match = OBSERVED_PATTERN.fullmatch(value)

    return RevolutReference(
        raw=value,
        length=len(value),
        prefix=match.group("prefix") if match else None,
        format_matched=match is not None,
    )


reference = inspect_reference(
    "REV859314XXXXXXXXXXXXXXXX20260805826230120"
)

print(reference.length)          # 42
print(reference.format_matched)  # True

这段正则表达式只描述示例表现出的格式,并不代表 Revolut 的官方字段定义。使用 section_a、section_b 和 section_c 这样的中性名称,可以避免给未知字段附加虚构的业务含义。

3. 将推测字段放入独立的分析层

如果需要研究它的生成规律,可以把候选切片保存为实验性数据,但不能用它们覆盖权威字段:

def extract_observed_sections(value: str) -> dict[str, str | int | bool]:
    match = OBSERVED_PATTERN.fullmatch(value)

    if not match:
        return {
            "raw": value,
            "length": len(value),
            "format_matched": False,
        }

    return {
        "raw": value,
        "length": len(value),
        "format_matched": True,
        "observed_prefix": match.group("prefix"),
        "observed_section_a": match.group("section_a"),
        "observed_section_b": match.group("section_b"),
        "observed_section_c": match.group("section_c"),
    }

字段名应明确包含 observed、candidate 或 unverified。取得正式规范并完成回归验证后,才能考虑改用 payment_date、sequence_number 等带有业务含义的名称。

如何与 ISO 20022 对接

不要自行根据字符串中的某个位置生成或填充 ISO 20022 业务字段。映射时,应优先使用上游响应中名称明确的值,例如:

  • EndToEndId
  • TxId
  • InstrId
  • UETR
  • clearing system reference
  • payment provider transaction ID

这些标识符含义不同,不能因为格式相似就相互替代。

如果这段 Revolut 字符串只是来源系统提供的未知类型引用,可以采用以下处理方式:

  1. 在内部数据模型中将其保存为 provider-specific reference;
  2. 在 ISO 20022 模块旁设置扩展字段或来源元数据;
  3. 只有接口契约明确说明对应关系时,才写入标准字段;
  4. 保留原始值,以便审计和重新处理时还原输入。

内部模型示例:

{
  "payment_provider": "revolut",
  "payment_scheme": "UK_FASTER_PAYMENTS",
  "provider_reference": {
    "type": "undocumented_raw_identifier",
    "value": "REV859314XXXXXXXXXXXXXXXX20260805826230120",
    "length": 42,
    "format_version": null
  },
  "iso20022": {
    "instruction_id": null,
    "end_to_end_id": null,
    "transaction_id": null,
    "uetr": null
  }
}

与其根据外观猜测映射,保留 null 更安全。错误的 EndToEndId 或 TxId 可能导致误匹配、重复入账,甚至将两笔不同的付款合并。

如何验证是否存在稳定结构

如果业务确实要求研究这种格式,至少要建立包含已知事实的样本集。样本应覆盖:

  • 不同付款日期和时间;
  • 不同收款账户;
  • 相同金额的多笔付款;
  • 同一收款人的连续付款;
  • 失败、重试、退回和冲正;
  • 批量付款与 app 单笔付款;
  • 不同 Revolut 实体、账户类型或 API 渠道;
  • 夏令时切换和跨日场景。

随后可以逐个位置比较字符变化:

from collections import Counter


def position_statistics(values: list[str]) -> list[dict]:
    if not values:
        return []

    lengths = Counter(map(len, values))
    if len(lengths) != 1:
        raise ValueError(f"mixed identifier lengths: {dict(lengths)}")

    width = len(values[0])
    result = []

    for index in range(width):
        chars = [value[index] for value in values]
        result.append({
            "offset": index,
            "distinct_count": len(set(chars)),
            "most_common": Counter(chars).most_common(5),
        })

    return result

即使发现了相关性,也只能将其记录为推测。例如:

candidate_date_yyyymmdd
confidence: low
evidence: 126/126 samples matched booking date
officially_confirmed: false

所有样本都匹配可以提高置信度,但不能替代协议定义。分析时还要区分付款创建时间、授权时间、提交时间、清算时间和入账日期,因为它们可能属于不同的自然日。

对账时应使用组合条件

缺少正式唯一键时,不要只依赖这段字符串。对账可以综合使用:

  • 原始 provider reference;
  • 金额和币种;
  • debtor/creditor account 标识;
  • 收款人信息;
  • booking/value timestamp;
  • payment status;
  • 上游 API transaction ID;
  • clearing reference;
  • ingestion source。

匹配结果可以按置信度分类:

EXACT    原始 provider reference 与权威 transaction ID 均一致
STRONG   reference、金额、币种和账户一致
WEAK     仅金额、日期和收款人相似
NONE     无法安全匹配,进入人工复核

弱匹配不应触发自动过账。

注意事项

  • 示例长度是 42,不是 37。应先检查原始字节或原始 JSON。
  • 要区分字符长度和字节长度。该示例使用 ASCII,两者相同,但通用日志处理不能默认所有输入都是如此。
  • 不要把标识符转换为整数,否则可能丢失前导零。
  • 不要静默执行 trim、大小写转换或 Unicode normalization。
  • 正则匹配成功不代表字段语义已经得到确认。
  • 不要依靠数据库列宽截断异常输入。格式发生变化时,应将数据送入隔离队列并触发告警。
  • 日志中的 X 可能是脱敏结果,需要检查更靠近数据源的原始响应。
  • 应向 Revolut 或所用 Open Banking provider 索取字段名称、唯一性范围、稳定性保证和格式版本策略。

在获得正式定义前,最安全的方案是完整保存这段字符串,并将其作为来源限定的不透明引用。任何按位置进行的切片都只能用于观察和调查,不能作为账务处理或 ISO 20022 映射的权威依据。

备注:内容仅供参考。