15 转账对接文档

目录

java 复制代码
1.报文基础信息与适用范围
2.全量必填 / 选填字段清单(分层:AppHdr、GrpHdr、CdtTrfTxInf)
3.SWIFT MT103 与 pacs.008 V08 字段一一映射表
4.生产级强制校验规则(CBPR + 跨境标准)
5.异常与合规校验清单(反洗钱 / 制裁筛查)
6.XSD 引用、XML 编码、日期金额统一规范

1 报文基础信息

1.1 标准标识

java 复制代码
报文名称:FIToFICustomerCreditTransferV08
报文 ID:`pacs.008.001.08`
命名空间:`urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08`
XSD 文件名:`pacs.008.001.08.xsd`
业务定位:行间客户贷记转账,替代 SWIFT MT103,CBPR + 跨境支付强制报文

1.2 参与方与流转

java 复制代码
1.企业下发 pain.001 → 付款行
2.付款行内部记账,组装 pacs.008.001.08
3.SWIFT / 清算系统转发中间行
4.收款行解析报文入账
5.回执:pacs.002 交易状态回执

1.3 硬性约束(CBPR+)

java 复制代码
1.单报文仅允许 1 条 `<CdtTrfTxInf>`,多笔必须拆多份报文
2.UETR 全局 36 位 UUID,跨境场景**强制必填**
3.收付款人、最终收付款人身份结构化字段不可使用纯自由文本
4.行间清算金额与客户交易金额分离,禁止混用

2 分层字段清单(M = 必选 O = 可选 C = 条件必填)

2.1 顶层结构

xml 复制代码
<Document>
  <AppHdr/>        <!-- 应用头:路由层,所有场景M -->
  <FIToFICstmrCdtTrf>
    <GrpHdr/>      <!-- 批次组头,M -->
    <CdtTrfTxInf/> <!-- 交易明细,M,仅1条 -->
  </FIToFICstmrCdtTrf>
</Document>

2.2 AppHdr 应用报文头

2.3 GrpHdr 批次组头(整批公共信息)

2.4 CdtTrfTxInf 单笔交易核心

2.4.1 交易标识 PmtId
2.4.2 清算、费用、金额
2.4.3 付款人 Dbtr(付款客户)M

强制结构化字段(跨境全部 M)

  1. Nm:姓名 / 企业全称 M
  2. PstlAdr:结构化地址(StrtNm/BldgNb/PstCd/TwnNm/Ctry)C
  3. Id:身份证件 / 法人 LEI C
    • 自然人:PrvtId (IdCardTp/IdNb/DtOfBirth)
    • 法人:OrgId/LEI(跨境强制 LEI)
2.4.4 DbtrAcct 付款人账户 M

Id/IBAN:跨境优先 IBAN

Id/Othr/Id:境内本地账号

2.4.5 DbtrAgt 付款行 M

FinInstnId/BICFI:11 位 BICFI

2.4.6 IntrmyAgt1~3 中间行 O

跨境中转时 C,最多 3 级,BICFI 必填

2.4.7 CdtrAgt 收款行 M

BICFI 必填

2.4.8 Cdtr 收款人 M

同 Dbtr 结构化身份规范

2.4.9 CdtrAcct 收款人账户 M

IBAN / 本地账号

2.4.10 UltmtDbtr 最终付款人 C

跨境反洗钱强制 M;多层代付场景必填

2.4.11 UltmtCdtr 最终收款人 C

跨境强制 M

2.4.12 RmtInf 附言 O
  • Ustrd:自由文本附言(兼容旧系统)
  • Strd:结构化发票 / 订单号(推荐用于自动化对账)
2.4.13 Purp 交易用途 O

交易代码:SALA 工资 / TRADE 贸易 / INVS 投资等

3 MT103 VS pacs.008.001.08 字段映射表

4 生产级强制校验规则

4.1 基础格式校验

java 复制代码
1.所有日期时间严格 ISO8601,时区 Z(UTC),禁止本地时间无时区
2.金额:
  JPY 小数位 0 位;CNY/USD/EUR 2 位
  数值必须大于 0,禁止 0 / 负数
3.币种必须 3 位 ISO4217 大写编码
4.BICFI 固定 11 位,最后三位分支码 XXX,大小写统一大写
5.UETR 正则校验:`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`

4.2 CBPR + 业务强校验(跨境支付必实现)

java 复制代码
1.GrpHdr.NbOfTxs 必须等于 1
2.AppHdr.UETR 与 CdtTrfTxInf.PmtId.UETR 完全一致
3.Dbtr、Cdtr、UltmtDbtr、UltmtCdtr 必须携带结构化身份,禁止仅传名称无证件 / LEI
4.SttlmMtd 只能取 CLRG / INDA / COVE 三者之一
5.ChrgBr 只能 SHAR / DEBT / CRDT
6.CtrlSum 必须等于 IntrBkSttlmAmt(单笔场景)

4.3 路由校验

  1. Fr (BICFI) != To (BICFI),禁止自转发报文
  2. 中间行 BIC 不可与付款行 / 收款行重复,防止循环路由

5 合规 / 反洗钱校验清单(上线必过)

  1. 法人客户必须携带 LEI 编号(跨境 FATF 强制)
  2. 自然人必须携带证件类型、证件号、出生日期
  3. 地址分层填写:街道、门牌号、邮编、城市、国家二字母代码
  4. 存在代付 / 代收款场景必须填充 UltmtDbtr/UltmtCdtr
  5. 名称字段禁止特殊非法字符(&<> 等 XML 转义必须处理)
  6. 禁止姓名、地址、证件号为空或仅空格

6 统一编码与 XML 规范

6.1 编码

  • 文件编码:UTF-8
  • XML 头部固定:<?xml version="1.0" encoding="UTF-8"?>

6.2 XSD 校验引入

开发环境校验报文合法性,导入官方 xsd:

pacs.008.001.08.xsd + 公共基础 xsd(iso20022_Common.xsd)

7 完整可校验生产报文(跨境 USD 汇款)

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08">
  <!-- 应用头 路由层 -->
  <AppHdr>
    <MsgId>SHANGHAIBANK202608080001</MsgId>
    <CreDtTm>2026-08-08T08:30:25Z</CreDtTm>
    <Fr>
      <BICFI>ABCICNSHXXX</BICFI>
    </Fr>
    <To>
      <BICFI>CHASUSNYXXX</BICFI>
    </To>
    <MsgTp>pacs.008.001.08</MsgTp>
    <BizMsgIdr>TRACK26080800001</BizMsgIdr>
    <UETR>550e8400-e29b-41d4-a716-446655440000</UETR>
  </AppHdr>

  <FIToFICstmrCdtTrf>
    <GrpHdr>
      <MsgId>GRP00000001</MsgId>
      <CreDtTm>2026-08-08T08:30:25Z</CreDtTm>
      <NbOfTxs>1</NbOfTxs>
      <CtrlSum>15800.50</CtrlSum>
      <SttlmInf>
        <SttlmMtd>CLRG</SttlmMtd>
        <SttlmDt>2026-08-08</SttlmDt>
        <TtlIntrBkSttlmAmt Ccy="USD">15800.50</TtlIntrBkSttlmAmt>
      </SttlmInf>
    </GrpHdr>

    <!-- 单笔交易 -->
    <CdtTrfTxInf>
      <PmtId>
        <EndToEndId>ERP-SUP-20260808-001</EndToEndId>
        <UETR>550e8400-e29b-41d4-a716-446655440000</UETR>
      </PmtId>
      <IntrBkSttlmAmt Ccy="USD">15800.50</IntrBkSttlmAmt>
      <ChrgBr>SHAR</ChrgBr>

      <!-- 付款企业客户 -->
      <Dbtr>
        <Nm>上海国际贸易有限公司</Nm>
        <PstlAdr>
          <StrtNm>张江大道</StrtNm>
          <BldgNb>1200</BldgNb>
          <PstCd>201203</PstCd>
          <TwnNm>上海</TwnNm>
          <Ctry>CN</Ctry>
        </PstlAdr>
        <Id>
          <OrgId>
            <LEI>549300XYZABC12345678</LEI>
          </OrgId>
        </Id>
      </Dbtr>
      <DbtrAcct>
        <Id>
          <Othr>
            <Id>0208001234567890</Id>
          </Othr>
        </Id>
      </DbtrAcct>
      <DbtrAgt>
        <FinInstnId>
          <BICFI>ABCICNSHXXX</BICFI>
        </FinInstnId>
      </DbtrAgt>

      <!-- 中间行(可选) -->
      <IntrmyAgt1>
        <FinInstnId>
          <BICFI>BOFAUSNYXXX</BICFI>
        </FinInstnId>
      </IntrmyAgt1>

      <!-- 收款行 -->
      <CdtrAgt>
        <FinInstnId>
          <BICFI>CHASUSNYXXX</BICFI>
        </FinInstnId>
      </CdtrAgt>

      <!-- 境外收款人自然人 -->
      <Cdtr>
        <Nm>Michael Smith</Nm>
        <PstlAdr>
          <StrtNm>Park Ave</StrtNm>
          <BldgNb>50</BldgNb>
          <PstCd>10001</PstCd>
          <TwnNm>New York</TwnNm>
          <Ctry>US</Ctry>
        </PstlAdr>
        <Id>
          <PrvtId>
            <IdCardTp>PASS</IdCardTp>
            <IdNb>123456789</IdNb>
            <DtOfBirth>1985-03-12</DtOfBirth>
          </PrvtId>
        </Id>
      </Cdtr>
      <CdtrAcct>
        <Id>
          <IBAN>US21CHAS01234567890123</IBAN>
        </Id>
      </CdtrAcct>

      <!-- 最终付款人(跨境合规必填) -->
      <UltmtDbtr>
        <Nm>上海国际贸易有限公司</Nm>
        <Id>
          <OrgId>
            <LEI>549300XYZABC12345678</LEI>
          </OrgId>
        </Id>
      </UltmtDbtr>

      <!-- 附言 -->
      <RmtInf>
        <Ustrd>August 2026 goods payment PO2026070018</Ustrd>
      </RmtInf>
      <Purp>
        <Cd>TRAD</Cd>
      </Purp>
    </CdtTrfTxInf>
  </FIToFICstmrCdtTrf>
</Document>

8 报文校验

xml 复制代码
<!-- pom.xml -->
<dependencies>
    <!-- JAXB jakarta 适配 JDK17+/21 -->
    <dependency>
        <groupId>jakarta.xml.bind</groupId>
        <artifactId>jakarta.xml.bind-api</artifactId>
        <version>4.0.2</version>
    </dependency>
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>4.0.2</version>
        <scope>runtime</scope>
    </dependency>
    <!-- 工具类校验 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.30</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

8.1. 顶层文档对象 Document.java

java 复制代码
package com.iso20022.pacs008.v08.model;

import jakarta.xml.bind.annotation.*;
import lombok.Data;

@Data
@XmlRootElement(name = "Document", namespace = "urn:iso:std:iso:20022:tech:xsd:pacs.008.001.08")
@XmlAccessorType(XmlAccessType.FIELD)
public class Document {
    @XmlElement(name = "AppHdr")
    private AppHdr appHdr;

    @XmlElement(name = "FIToFICstmrCdtTrf")
    private FIToFICstmrCdtTrf fiToFiCstmrCdtTrf;
}

8.2. 应用头 AppHdr.java(路由层)

java 复制代码
package com.iso20022.pacs008.v08.model;

import jakarta.xml.bind.annotation.*;
import lombok.Data;

@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class AppHdr {
    @XmlElement(name = "MsgId")
    private String msgId;

    @XmlElement(name = "CreDtTm")
    private String creDtTm;

    @XmlElement(name = "Fr")
    private BicFi fr;

    @XmlElement(name = "To")
    private BicFi to;

    @XmlElement(name = "MsgTp")
    private String msgTp;

    @XmlElement(name = "BizMsgIdr")
    private String bizMsgIdr;

    @XmlElement(name = "UETR")
    private String uetr;
}

@Data
@XmlAccessorType(XmlAccessType.FIELD)
class BicFi {
    @XmlElement(name = "BICFI")
    private String bicfi;
}

8.3. 外层业务主体 FIToFICstmrCdtTrf.java

java 复制代码
package com.iso20022.pacs008.v08.model;

import jakarta.xml.bind.annotation.*;
import lombok.Data;

@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class FIToFICstmrCdtTrf {
    @XmlElement(name = "GrpHdr")
    private GrpHdr grpHdr;

    @XmlElement(name = "CdtTrfTxInf")
    private CdtTrfTxInf cdtTrfTxInf;
}

8.4. 批次组头 GrpHdr.java

java 复制代码
package com.iso20022.pacs008.v08.model;

import jakarta.xml.bind.annotation.*;
import lombok.Data;

import java.math.BigDecimal;

@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class GrpHdr {
    @XmlElement(name = "MsgId")
    private String msgId;

    @XmlElement(name = "CreDtTm")
    private String creDtTm;

    @XmlElement(name = "NbOfTxs")
    private Integer nbOfTxs;

    @XmlElement(name = "CtrlSum")
    private BigDecimal ctrlSum;

    @XmlElement(name = "SttlmInf")
    private SttlmInf sttlmInf;
}

@Data
@XmlAccessorType(XmlAccessType.FIELD)
class SttlmInf {
    @XmlElement(name = "SttlmMtd")
    private String sttlmMtd;

    @XmlElement(name = "SttlmDt")
    private String sttlmDt;

    @XmlElement(name = "TtlIntrBkSttlmAmt")
    private Amount ttlIntrBkSttlmAmt;
}

@Data
@XmlAccessorType(XmlAccessType.FIELD)
class Amount {
    @XmlAttribute(name = "Ccy")
    private String ccy;

    @XmlValue
    private BigDecimal value;
}

8.5. 核心交易明细 CdtTrfTxInf.java

java 复制代码
package com.iso20022.pacs008.v08.model;

import jakarta.xml.bind.annotation.*;
import lombok.Data;

@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class CdtTrfTxInf {
    @XmlElement(name = "PmtId")
    private PmtId pmtId;

    @XmlElement(name = "IntrBkSttlmAmt")
    private Amount intrBkSttlmAmt;

    @XmlElement(name = "ChrgBr")
    private String chrgBr;

    // 付款人
    @XmlElement(name = "Dbtr")
    private Party dbtr;
    // 付款人账户
    @XmlElement(name = "DbtrAcct")
    private Account dbtrAcct;
    // 付款行
    @XmlElement(name = "DbtrAgt")
    private FinInstAgt dbtrAgt;
    // 中间行1
    @XmlElement(name = "IntrmyAgt1")
    private FinInstAgt intrmyAgt1;
    // 收款行
    @XmlElement(name = "CdtrAgt")
    private FinInstAgt cdtrAgt;
    // 收款人
    @XmlElement(name = "Cdtr")
    private Party cdtr;
    // 收款人账户
    @XmlElement(name = "CdtrAcct")
    private Account cdtrAcct;
    // 最终付款人
    @XmlElement(name = "UltmtDbtr")
    private Party ultmtDbtr;
    // 最终收款人
    @XmlElement(name = "UltmtCdtr")
    private Party ultmtCdtr;
    // 附言
    @XmlElement(name = "RmtInf")
    private RmtInf rmtInf;
    // 交易用途
    @XmlElement(name = "Purp")
    private Purp purp;
}

// 交易流水标识
@Data
@XmlAccessorType(XmlAccessType.FIELD)
class PmtId {
    @XmlElement(name = "EndToEndId")
    private String endToEndId;
    @XmlElement(name = "UETR")
    private String uetr;
    @XmlElement(name = "InstrId")
    private String instrId;
}

// 机构行(付款行/收款行/中间行)
@Data
@XmlAccessorType(XmlAccessType.FIELD)
class FinInstAgt {
    @XmlElement(name = "FinInstnId")
    private BicFi finInstnId;
}

// 收付款人主体(自然人/企业)
@Data
@XmlAccessorType(XmlAccessType.FIELD)
class Party {
    @XmlElement(name = "Nm")
    private String nm;
    @XmlElement(name = "PstlAdr")
    private PostalAddr pstlAdr;
    @XmlElement(name = "Id")
    private PartyId id;
}

// 结构化地址
@Data
@XmlAccessorType(XmlAccessType.FIELD)
class PostalAddr {
    @XmlElement(name = "StrtNm")
    private String strtNm;
    @XmlElement(name = "BldgNb")
    private String bldgNb;
    @XmlElement(name = "PstCd")
    private String pstCd;
    @XmlElement(name = "TwnNm")
    private String twnNm;
    @XmlElement(name = "Ctry")
    private String ctry;
}

// 客户身份:企业LEI / 自然人证件
@Data
@XmlAccessorType(XmlAccessType.FIELD)
class PartyId {
    @XmlElement(name = "OrgId")
    private OrgId orgId;
    @XmlElement(name = "PrvtId")
    private PrvtId prvtId;
}

@Data
@XmlAccessorType(XmlAccessType.FIELD)
class OrgId {
    @XmlElement(name = "LEI")
    private String lei;
}

@Data
@XmlAccessorType(XmlAccessType.FIELD)
class PrvtId {
    @XmlElement(name = "IdCardTp")
    private String idCardTp;
    @XmlElement(name = "IdNb")
    private String idNb;
    @XmlElement(name = "DtOfBirth")
    private String dtOfBirth;
}

// 账户信息 IBAN/本地账号
@Data
@XmlAccessorType(XmlAccessType.FIELD)
class Account {
    @XmlElement(name = "Id")
    private AccountId id;
}

@Data
@XmlAccessorType(XmlAccessType.FIELD)
class AccountId {
    @XmlElement(name = "IBAN")
    private String iban;
    @XmlElement(name = "Othr")
    private OtherAccount othr;
}

@Data
@XmlAccessorType(XmlAccessType.FIELD)
class OtherAccount {
    @XmlElement(name = "Id")
    private String id;
}

// 附言信息
@Data
@XmlAccessorType(XmlAccessType.FIELD)
class RmtInf {
    @XmlElement(name = "Ustrd")
    private String ustrd;
}

// 交易用途代码
@Data
@XmlAccessorType(XmlAccessType.FIELD)
class Purp {
    @XmlElement(name = "Cd")
    private String cd;
}

9 报文统一校验工具类 Pacs008ValidatorUtil

java 复制代码
package com.iso20022.pacs008.v08.util;

import com.iso20022.pacs008.v08.model.Document;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.regex.Pattern;

public class Pacs008ValidatorUtil {
    // UETR UUID正则
    private static final Pattern UETR_PATTERN = Pattern.compile("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$");
    // BICFI 11位大写字母数字
    private static final Pattern BICFI_PATTERN = Pattern.compile("^[A-Z0-9]{11}$");
    // 支持币种小数位数配置
    private static final Map<String, Integer> CURR_DECIMAL_MAP = new HashMap<>();
    // 清算方式枚举
    public static final Set<String> VALID_STTL_MTD = Set.of("CLRG", "INDA", "COVE");
    // 费用承担枚举
    public static final Set<String> VALID_CHARGE_BR = Set.of("SHAR", "DEBT", "CRDT");

    static {
        CURR_DECIMAL_MAP.put("JPY", 0);
        CURR_DECIMAL_MAP.put("USD", 2);
        CURR_DECIMAL_MAP.put("CNY", 2);
        CURR_DECIMAL_MAP.put("EUR", 2);
        CURR_DECIMAL_MAP.put("GBP", 2);
    }

    // 1. UETR 格式校验
    public static boolean validUetr(String uetr) {
        if (uetr == null || uetr.isBlank()) return false;
        return UETR_PATTERN.matcher(uetr).matches();
    }

    // 2. BICFI 11位校验
    public static boolean validBicFi(String bic) {
        if (bic == null || bic.isBlank()) return false;
        return BICFI_PATTERN.matcher(bic).matches();
    }

    // 3. 金额校验:正数、小数位匹配币种
    public static String validAmount(BigDecimal amt, String ccy) {
        if (amt == null) return "金额不能为空";
        if (amt.compareTo(BigDecimal.ZERO) <= 0) return "金额必须大于0";
        int expectDec = CURR_DECIMAL_MAP.getOrDefault(ccy, 2);
        BigDecimal scaled = amt.setScale(expectDec, RoundingMode.FLOOR);
        if (!scaled.equals(amt)) {
            return String.format("币种%s仅允许%d位小数", ccy, expectDec);
        }
        return null;
    }

    // 4. 校验ISO8601 UTC时间格式 CreDtTm
    public static boolean validIsoDateTime(String dtStr) {
        if (dtStr == null || dtStr.isBlank()) return false;
        try {
            OffsetDateTime.parse(dtStr, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
            return true;
        } catch (DateTimeParseException e) {
            return false;
        }
    }

    // 5. 交割日校验 yyyy-MM-dd
    public static boolean validSttlmDate(String dateStr) {
        if (dateStr == null || dateStr.isBlank()) return false;
        try {
            LocalDate.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE);
            return true;
        } catch (DateTimeParseException e) {
            return false;
        }
    }

    // 6. XML特殊字符转义(避免报文解析报错)
    public static String xmlEscape(String text) {
        if (text == null) return "";
        return text.replace("&", "&amp;")
                .replace("<", "&lt;")
                .replace(">", "&gt;")
                .replace("\"", "&quot;")
                .replace("'", "'");
    }

    // 7. CBPR+ 全量业务校验入口,返回异常信息列表
    public static List<String> cbprFullCheck(Document doc) {
        List<String> errList = new ArrayList<>();
        if (doc == null) {
            errList.add("报文根节点Document为空");
            return errList;
        }
        // 7.1 AppHdr 校验
        var appHdr = doc.getAppHdr();
        if (appHdr == null) errList.add("AppHdr应用头缺失");
        else {
            if (!validIsoDateTime(appHdr.getCreDtTm())) errList.add("AppHdr.CreDtTm时间格式非法");
            if (!validBicFi(appHdr.getFr().getBicfi())) errList.add("发送行Fr.BICFI格式错误");
            if (!validBicFi(appHdr.getTo().getBicfi())) errList.add("接收行To.BICFI格式错误");
            if (!"pacs.008.001.08".equals(appHdr.getMsgTp())) errList.add("MsgTp必须为pacs.008.001.08");
            // CBPR+强制UETR
            if (!validUetr(appHdr.getUetr())) errList.add("跨境报文UETR格式非法/缺失");
        }

        // 7.2 GrpHdr 校验
        var fiTrf = doc.getFiToFiCstmrCdtTrf();
        if (fiTrf == null) {
            errList.add("FIToFICstmrCdtTrf业务主体缺失");
            return errList;
        }
        var grp = fiTrf.getGrpHdr();
        if (grp == null) errList.add("GrpHdr批次头缺失");
        else {
            if (!Integer.valueOf(1).equals(grp.getNbOfTxs())) errList.add("CBPR+单报文交易数NbOfTxs必须=1");
            var sttlm = grp.getSttlmInf();
            if (sttlm == null) errList.add("SttlmInf清算信息缺失");
            else {
                if (!VALID_STTL_MTD.contains(sttlm.getSttlmMtd())) errList.add("SttlmMtd仅支持CLRG/INDA/COVE");
                if (!validSttlmDate(sttlm.getSttlmDt())) errList.add("SttlmDt交割日期格式错误");
                var totalAmt = sttlm.getTtlIntrBkSttlmAmt();
                String amtErr = validAmount(totalAmt.getValue(), totalAmt.getCcy());
                if (amtErr != null) errList.add("批次总金额校验失败:" + amtErr);
            }
        }

        // 7.3 单笔交易校验
        var tx = fiTrf.getCdtTrfTxInf();
        if (tx == null) {
            errList.add("CdtTrfTxInf交易明细缺失");
            return errList;
        }
        // UETR上下一致
        if (!appHdr.getUetr().equals(tx.getPmtId().getUetr())) {
            errList.add("AppHdr.UETR与PmtId.UETR不一致");
        }
        // 费用承担
        if (!VALID_CHARGE_BR.contains(tx.getChrgBr())) {
            errList.add("ChrgBr费用承担仅支持SHAR/DEBT/CRDT");
        }
        // 交易金额
        String txAmtErr = validAmount(tx.getIntrBkSttlmAmt().getValue(), tx.getIntrBkSttlmAmt().getCcy());
        if (txAmtErr != null) errList.add("交易清算金额校验失败:" + txAmtErr);
        // 付款行/收款行BIC
        if (!validBicFi(tx.getDbtrAgt().getFinInstnId().getBicfi())) errList.add("付款行DbtrAgt BICFI非法");
        if (!validBicFi(tx.getCdtrAgt().getFinInstnId().getBicfi())) errList.add("收款行CdtrAgt BICFI非法");

        return errList;
    }
}

10 JAXB 序列化 / 反序列化工具

java 复制代码
package com.iso20022.pacs008.v08.util;

import com.iso20022.pacs008.v08.model.Document;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;

public class Pacs008XmlUtil {
    private static JAXBContext context;

    static {
        try {
            context = JAXBContext.newInstance(Document.class);
        } catch (JAXBException e) {
            throw new RuntimeException("初始化JAXB上下文失败", e);
        }
    }

    // 对象转XML字符串
    public static String marshal(Document doc) throws JAXBException {
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
        StringWriter sw = new StringWriter();
        marshaller.marshal(doc, sw);
        return sw.toString();
    }

    // XML字符串转POJO对象
    public static Document unmarshal(String xml) throws JAXBException {
        Unmarshaller unmarshaller = context.createUnmarshaller();
        return (Document) unmarshaller.unmarshal(new StringReader(xml));
    }
}
java 复制代码
// 组装Document对象后执行全量校验
List<String> errors = Pacs008ValidatorUtil.cbprFullCheck(document);
if (!errors.isEmpty()) {
    throw new RuntimeException("报文校验不通过:" + errors);
}
// 序列化为XML
String xml = Pacs008XmlUtil.marshal(document);
相关推荐
西凉的悲伤1 小时前
Java 基于 MySQL 行锁实现高并发业务序列号生成器
java·mysql·springboot·序列号生成
xieliyu.1 小时前
Java 文件IO :File 文件类、字节流、字符流讲解
java·ide·笔记·javaee
云烟成雨TD1 小时前
Micrometer 系列【28】Spring Boot Actuator | 其他指标速览
java·spring boot·云原生
meilindehuzi_a1 小时前
深入理解 Linux 进程地址空间:从虚拟内存、页表映射到 fork 写时拷贝与 VMA 管理
java·linux·服务器
莫得感情 o2 小时前
设计模式 13 · 桥接模式
java·设计模式·桥接模式
吠品2 小时前
Java中实现拓扑排序的两种方式:Kahn算法和DFS
java·数据库·notepad++
TunerT_TQ2 小时前
企业智能体工程体系v1.1|企业智能体工程卷 · 第5期· 数据飞轮——用观察—分析—计划—执行持续改进
java·运维·网络·#agent工程化·#企业智能体·#多智能体系统·#数据飞轮
小当家.1052 小时前
工具并行调用原理与实现:CompletableFuture 实战
java·agent·线程池·工具·并行
wangjialelele2 小时前
Spring Cloud 全体系学习笔记(REST、Nacos、Feign、Gateway)
java·spring boot·笔记·学习·spring·spring cloud·gateway