Article

支付SDK接入

更新于:2026-07-14

第一章:支付系统概述

1.1 支付的基本概念与流程

概念名称说明注意事项
支付用户通过某种方式将资金转移给商户以完成商品或服务购买的行为。需区分”支付成功”与”交易成功”,支付成功不等于订单最终完成。
交易订单商户系统创建的待支付订单,包含金额、商品信息、商户订单号等。商户订单号必须在商户系统内唯一,避免重复下单导致资金错乱。
统一下单商户调用支付平台接口,将订单信息提交至支付平台生成预支付交易单。调用前需完成签名,确保请求合法性;返回的预支付标识用于后续支付流程。
支付网关连接商户系统与支付平台的中间服务,负责协议转换、路由、安全控制等。自建支付网关需考虑高可用、安全性、对账能力;建议使用成熟平台或中间件。
同步回调用户完成支付后,支付平台将用户重定向回商户指定页面,并携带结果参数。同步回调不可靠,仅用于展示结果,不能作为支付成功的唯一依据。
异步通知支付平台在支付结果确定后,主动向商户服务器发送 HTTP/HTTPS 通知消息。必须校验签名并返回正确响应,否则可能被重复通知;是支付成功的权威依据。
交易状态描述支付流程所处阶段(未支付、已支付、已关闭、已退款等)。应建立状态机模型管理订单生命周期,防止状态错乱或重复操作。
对账商户定期核对自身交易记录与支付平台提供的官方对账单是否一致。是发现资金差异、防止漏单/错单的核心手段,建议每日自动对账。

1.2 常见支付场景分类(线上、线下、APP、小程序)

场景类型说明注意事项
线上网页支付用户在 PC 或移动端浏览器中完成支付,如支付宝即时到账、微信 H5 支付。H5 支付受微信限制较多,需企业资质;注意浏览器兼容性和支付成功率优化。
APP 支付集成 SDK 在移动应用内调起支付控件(如微信、支付宝 App 内支付)。需配置应用包名、签名、回调 URL;注意 SDK 版本更新和兼容性问题。
小程序支付在微信/支付宝小程序内调用支付接口完成交易。需绑定小程序与商户号关系;使用 JSAPI 下单,注意 openid 获取和授权流程。
线下扫码支付用户扫描商户二维码完成支付(被扫),或商户扫描用户付款码收款(主扫)。被扫适用于静态码(如门店贴码),主扫适用于 POS、收银台等动态场景。
刷卡支付用户使用实体银行卡在 POS 机上刷卡、插卡或非接支付。通常由银联或第三方支付公司提供 POS 服务,涉及机具管理和结算。
公众号支付用户关注商户公众号后,在公众号文章或菜单中触发支付。仅限关注用户使用,需获取用户 openid;适用于内容电商、会员服务等场景。
无感支付车辆进出停车场、加油站等场景下自动扣款,无需手动操作。依赖代扣协议(如微信代扣、支付宝代扣),需用户事先授权。

1.3 支付系统的角色与参与方(商户、用户、银行、支付平台、清算机构)

参与方说明注意事项
用户(买家)发起支付行为的个人消费者,使用银行卡、余额、信用额度等完成付款。需保障用户支付体验流畅,信息加密安全,避免敏感信息泄露。
商户(卖家)提供商品或服务的一方,负责创建订单、接收支付结果、发货或提供服务。需合规经营,具备相应支付资质;承担对账、退款、客户服务等责任。
支付平台提供支付通道和技术服务的第三方机构,如微信支付、支付宝、银联商务等。负责交易处理、资金结算、风控、对账文件提供;商户需遵守其接入规范。
银行用户资金存放机构,负责资金划转、结算、清算。分为发卡行(用户银行)和收单行(商户银行),涉及跨行交易时需清算机构介入。
清算机构负责银行间交易清算的组织,如中国银联、网联(非银行支付机构网络清算)。确保交易资金准确、及时清算,维护支付系统稳定运行。
第三方支付机构持有支付牌照的公司,作为中介连接商户与银行,提供支付解决方案。如财付通(微信支付)、支付宝、拉卡拉等;需接受央行监管。

1.4 主流支付方式简介(银行卡、扫码、快捷、代扣、红包等)

支付方式说明注意事项
银行卡支付用户输入银行卡号、密码、CVV、有效期等信息完成支付。安全风险较高,需符合 PCI DSS 标准;建议使用支付平台托管卡信息。
扫码支付用户通过扫描二维码完成支付(如支付宝/微信扫码),或商户扫描用户付款码。分为主扫(商户扫用户)和被扫(用户扫商户);主扫安全性更高。
快捷支付用户首次绑定银行卡后,后续支付无需输入卡密,仅需验证短信或密码。提升支付体验,需用户授权;注意风控策略防止盗刷。
代扣(自动扣款)用户授权后,商户可在约定条件下自动从用户账户扣款(如会员费、水电费)。需用户明确签约授权;支持微信代扣、支付宝代扣等;注意扣款失败重试机制。
红包支付用户领取或发送红包,资金进入零钱账户,可用于支付或提现。常用于营销活动;红包资金有有效期,涉及税务问题需注意合规。
余额支付用户使用支付平台账户余额(如支付宝余额、微信零钱)完成支付。资金受限于平台账户,需用户提前充值或收款。
分期付款将支付金额分多期偿还,通常由平台或银行提供信贷支持。涉及利息和手续费,需明确告知用户;风控审核较严格。
刷脸支付通过人脸识别技术完成身份验证和支付扣款。依赖专用设备,安全性高;适用于商超、餐饮等高频小额场景。

第二章:支付协议与基础技术

2.1 HTTP/HTTPS 与支付通信安全

概念名称说明注意事项
HTTP超文本传输协议,用于客户端与服务器之间的数据传输。明文传输,不适用于支付场景,存在被窃听和篡改风险。
HTTPS基于 SSL/TLS 加密的 HTTP 协议,确保通信内容加密、身份认证和完整性保护。支付接口必须使用 HTTPS,防止敏感信息(如订单、签名)被中间人窃取。
SSL/TLS安全套接层/传输层安全协议,用于建立加密通道。建议使用 TLS 1.2 及以上版本,禁用不安全的加密套件。
数字证书由 CA 签发的文件,用于验证服务器身份,包含公钥和持有者信息。商户需正确配置服务器证书;支付平台通常也提供其根证书用于验证响应签名。
CA(证书颁发机构)受信任的第三方机构,负责签发和管理数字证书(如 DigiCert、Let’s Encrypt)。使用自签名证书需手动信任,生产环境建议使用权威 CA 签发的证书。
中间人攻击攻击者在通信双方之间截获并可能篡改数据。HTTPS 可有效防止此类攻击,前提是证书验证通过且未被伪造。
证书验证客户端验证服务器证书的有效性(是否过期、是否由可信 CA 签发等)。调用支付 API 时,应启用证书校验,避免连接到伪造的支付网关。

2.2 RESTful API 设计规范在支付中的应用

概念名称说明注意事项
REST表述性状态转移,一种基于 HTTP 的 API 设计风格。支付平台如微信、支付宝虽不完全遵循 REST,但其接口设计借鉴了 REST 理念。
资源(Resource)将支付中的实体抽象为资源,如订单(order)、退款(refund)、对账单等。接口 URL 应体现资源路径,如 /v1/orders, /v1/refunds
统一接口使用标准 HTTP 方法操作资源:GET(查询)、POST(创建)、PUT(更新)等。支付下单通常用 POST,查询订单用 GET,避免使用非标准方法。
状态无关每次请求应包含所有必要信息,服务器不保存客户端状态。有利于扩展和负载均衡,但需通过 token 或 session 机制管理用户上下文。
JSON 格式多数支付 API 使用 JSON 作为请求和响应的数据格式。请求体和响应体应使用 UTF-8 编码,避免中文乱码。
HTTP 状态码使用标准状态码表示结果,如 200(成功)、400(参数错误)、500(服务异常)。需根据状态码做相应处理,不能仅依赖响应体中的业务结果码。
版本控制在 URL 或 Header 中指定 API 版本,如 /v1/payments便于向后兼容,升级接口时不影响老版本调用方。

2.3 签名机制原理(MD5、HMAC-SHA256、RSA)

签名方式说明注意事项
MD5哈希算法,将任意长度数据映射为 128 位固定长度摘要。已不推荐用于安全场景,易被碰撞攻击;仅用于兼容老系统。
HMAC-SHA256基于密钥的哈希消息认证码,使用 SHA256 算法和密钥生成签名。安全性高,性能好,微信支付、支付宝推荐使用;密钥需严格保密。
RSA非对称加密算法,使用私钥签名、公钥验签。安全性最高,适合高敏感场景;私钥必须本地保存,不可泄露。
签名生成流程将请求参数按规则排序,拼接成字符串,加入密钥后进行哈希或加密运算。参数拼接规则(如是否包含空值、分隔符)必须与文档一致,否则验签失败。
签名验证接收方使用相同算法和密钥重新计算签名,并与收到的签名比对。用于防止请求被篡改,确保来源可信。
密钥管理对称密钥(如 HMAC 密钥)需双方共享;非对称密钥中私钥由商户保管。建议将密钥存储在环境变量或配置中心,避免硬编码在代码中。
签名字符串构造规则通常要求:参数按 key 升序排列,过滤空值和 sign 字段,用 & 连接 key=value微信支付 v3 要求使用特定格式(如 key1=value1&key2=value2),注意 URL 编码处理。

2.4 异步通知与同步回调机制解析

概念名称说明注意事项
同步回调用户支付完成后,支付平台将浏览器重定向回商户指定 return_url仅用于展示结果,不可靠,可能因网络中断未到达;不能作为支付成功的依据。
异步通知支付平台在后台通过 POST 请求将支付结果发送至商户 notify_url是支付成功的权威通知,必须处理并返回 success,否则会重复通知(最多 8 次)。
notify_url商户提供的异步通知接收地址,需公网可访问。必须部署在 HTTPS 服务上,且能处理高并发请求;建议使用独立服务接收。
return_url用户支付后跳转的页面地址,通常为订单详情或支付结果页。可携带参数如 out_trade_nototal_fee,但需再次查询订单状态确认结果。
通知内容包含交易状态、订单号、金额、支付时间、签名等字段。必须校验签名,防止伪造通知;解析 XML 或 JSON 格式数据。
通知重试机制支付平台在未收到 success 响应时会多次重试通知。商户需保证通知处理接口的幂等性,避免重复发货或记账。
响应要求成功处理后必须返回纯文本 success(小写),失败可返回 fail返回其他内容(如 HTML 页面)视为失败,将触发重试。
幂等性设计同一通知多次到达时,只应产生一次业务效果(如只确认一次订单)。可通过数据库唯一索引或 Redis 记录已处理的 notify_id 来实现。

2.5 支付状态机与交易生命周期管理

状态名称说明注意事项
待支付(created)商户创建订单,调用统一下单接口成功,等待用户支付。设置合理超时时间(如 30 分钟),超时后自动关闭订单。
支付中(paying)用户已发起支付,等待支付平台返回最终结果。此状态为中间态,不应持久化;可通过查询接口获取最终状态。
支付成功(paid)支付平台确认资金已到账,异步通知已接收并验签通过。可触发发货、记账等后续业务流程;需记录支付流水号。
支付失败(failed)用户取消支付或支付被拒绝。允许用户重新支付,但需生成新订单或重试原单(需支持)。
已关闭(closed)订单未支付成功且已超时,或被商户主动关闭。不可再支付,需引导用户重新下单。
已退款(refunded)订单已全额或部分退款,资金已退回用户账户。需记录退款单号、金额、时间;退款成功后订单不可再次退款。
退款中(refunding)退款申请已提交,等待支付平台处理。需定时查询退款状态,避免长时间挂起。
已撤销(revoked)部分支付方式支持在当日未结算前撤销交易(如 POS 消费)。撤销后资金立即释放,不进入结算流程;仅限当日交易。
对账完成(reconciled)订单已纳入对账范围,与平台账单一致。是财务确认收入的依据;差异订单需进入差错处理流程。
状态变更规则状态转移需符合业务逻辑,如:待支付 → 支付成功 / 支付失败 / 已关闭。使用状态机模式控制流转,禁止非法跳转(如直接从待支付到已退款)。
查询机制调用支付平台”查询订单”接口获取真实状态,用于补偿通知丢失或超时场景。建议在用户跳转回商户页时主动查询一次,确保结果准确。

第三章:主流支付服务商对比与选型

3.1 微信支付 vs 支付宝 vs 银联云闪付 功能对比

对比维度微信支付支付宝银联云闪付(UnionPay App)
覆盖用户基数基于微信 12 亿+月活用户,社交属性强,渗透率高拥有超 10 亿用户,金融属性强,理财、生活服务生态丰富依托银联网络,支持所有银联卡,银行合作广泛
主要使用场景小程序、公众号、APP、线下扫码、H5 支付APP、网页支付、小程序、线下扫码、刷脸支付线下扫码、NFC 支付、银行卡管理、优惠活动
支持的支付方式JSAPI、APP、H5、Native、小程序支付、代扣、企业付款到零钱APP、WAP、电脑网站支付、小程序、扫码、口碑、代扣、转账到支付宝账户扫码支付(主扫/被扫)、NFC 支付、二维码收款、无感支付
开发文档质量文档详细,示例丰富,提供多种语言 SDK文档系统完善,接口说明清晰,社区支持好文档相对简洁,更新频率较低
SDK 支持情况提供 Java、PHP、Python、Go 等主流语言 SDK,集成方便提供多语言 SDK,支持 Maven/Gradle 依赖引入提供 Java、C# 等 SDK,但社区资源较少
异步通知机制支持 HTTPS POST 通知,需返回 success 确认支持异步通知,要求返回 success支持通知,机制类似微信和支付宝
对账文件获取支持每日自动下载或手动导出,格式为 CSV,包含交易明细、退款、手续费等提供对账单下载,支持按日/月导出,字段齐全可下载交易明细对账单,格式标准
商户平台功能功能全面,含交易查询、对账单下载、营销工具、风险控制平台功能强大,支持数据分析、发票管理、子商户管理基础交易管理功能完备
审核时效一般 1-3 个工作日一般 1-3 个工作日视具体收单机构而定,通常 3-5 个工作日
公共号/小程序绑定必须绑定同一主体下的公众号或小程序才能使用 JSAPI 和小程序支付支持绑定支付宝小程序,用于小程序内支付不依赖特定 App,任何商户均可接入

3.2 各平台资质要求与接入门槛

平台名称主体类型要求所需资质材料接入门槛说明
微信支付企业、个体工商户、事业单位、社会组织等营业执照、法人身份证、银行账户信息、联系人信息、经营类目个人开发者无法申请;需拥有已认证的公众号或小程序(部分场景除外);审核较严格
支付宝企业、个体工商户、个体经营者、部分特殊行业组织营业执照(个体户可为营业执照或身份证)、法人身份证、银行账户、联系人信息支持”个体经营者”模式,可用身份证注册;小程序需完成实名认证;审核流程自动化程度高
银联云闪付企业、个体工商户营业执照、法人身份证、开户许可证、门店照片、收单协议通常通过第三方收单机构接入(如银联商务、拉卡拉),直接对接门槛较高
行业限制禁止接入赌博、色情、虚拟货币、P2P 理财等违规行业各平台均禁止高风险行业接入,需如实填写经营范围医疗、教育、游戏等行业需额外资质(如 ICP 许可、文网文、医疗执业许可证等)
结算账户要求必须为对公银行账户(个体户可为法人个人账户)账户需与营业执照主体一致,支持主流银行不支持境外银行账户结算(境内版)
技术对接能力需具备基本后端开发能力,能处理 HTTPS 请求、签名验证、异步通知等建议有 Java/PHP/Python 等语言开发经验提供沙箱环境测试,建议先在测试环境完成全流程验证

3.3 费率结构与结算周期分析

平台名称标准费率(线上)线下扫码费率结算周期结算方式注意事项
微信支付0.6%(大部分行业)0.38% 起(根据行业和签约情况浮动)T+1(自然日),节假日顺延自动结算至绑定账户新商户可能有首月优惠;高风险行业费率上浮;提现可能产生额外手续费
支付宝0.6%(标准类)0.38%-0.6%(视签约方案)T+1(工作日),部分可 T+0(需开通)自动结算支持 T+0 快速到账(收取 0.1% 费用);长期交易良好可申请费率优惠
银联云闪付0.38%-0.6%(由收单机构决定)0.25%-0.38%(优惠类商户)T+1 或 D+1(D 为交易日),依收单方而定由收单机构结算费率由第三方收单公司制定,可能存在”秒到”服务(加收费用)
费率减免政策单笔金额极小(如 <0.2 元)可能免收部分行业(如公益、教育)有优惠政策结算时间从支付成功次日开始计算不支持手动发起结算节假日期间交易统一节后首个工作日结算
手续费承担方通常由商户承担同左结算金额 = 订单金额 - 手续费资金不可分账若需分账,需使用各平台提供的”分账”功能(另行收费)
对账参考对账单中明确列出”手续费”字段对账单包含”商户服务费”需与收单机构确认费用明细建议每日对账实际到账金额应等于订单金额减去手续费,差异需及时排查

3.4 国际化支付支持情况(PayPal、Stripe 简要介绍)

支付平台支持国家/地区主要币种支持接入特点适用场景注意事项
PayPal超过 200 个国家和地区USD、EUR、GBP、JPY、CAD、AUD、CNY 等主流货币全球最广泛使用的跨境支付工具;支持信用卡、借记卡、PayPal 余额支付跨境电商、国际订阅服务、数字商品销售收款需绑定海外银行账户或提现至本地银行(有手续费);争议处理机制较复杂
Stripe超过 40 个国家和地区(主要欧美市场)USD、EUR、GBP、JPY、AUD、CAD、SGD 等开发者友好,API 设计现代化,文档优秀;支持 Apple Pay、Google Pay、SEPA 等SaaS 订阅、在线课程、国际 B2B 服务不支持中国大陆主体直接注册;需通过新加坡或美国公司主体接入
支付宝国际版支持港澳台及部分海外国家支持外卡支付(Visa/MasterCard/Discover)帮助境外商户接入支付宝钱包,吸引中国游客消费海外零售、旅游、酒店、跨境电商需与支付宝国际签约,结算以外币形式进行
微信支付境外版支持港澳台、东南亚、日韩、欧美等地支持外卡(Visa/MasterCard/JCB 等)支付境外商户可接入微信支付,服务中国出境用户海外商超、免税店、餐饮、交通需通过当地收单机构或支付服务商接入
结算货币PayPal:支持多币种结算或兑换为本币Stripe:支持多种货币结算,可设置默认结算币种均支持自动汇率转换需关注汇率波动对利润的影响提现可能产生跨境汇兑手续费
合规要求需遵守 FATF 反洗钱规则,提交 KYC 资料需提供公司注册文件、股东信息、业务描述等审核严格,可能需要视频验证建议预留 2-3 周审核时间不可用于赌博、成人内容等受限行业

第四章:微信支付 SDK 接入详解

4.1 公众号/小程序支付接入流程

概念名称说明注意事项
商户号(mch_id)微信支付分配给商户的唯一标识,用于 API 调用身份认证。与 AppID 不同,需在微信支付商户平台查看。
公众号 AppID已认证的服务号或订阅号的唯一 ID,用于公众号支付。必须与商户号绑定在同一主体下,否则无法使用 JSAPI。
小程序 AppID小程序的唯一 ID,用于小程序内调起微信支付。需在微信支付商户平台绑定,且主体一致。
支付目录公众号支付时,用户点击支付的网页 URL 前缀,需在商户平台配置。如:https://pay.example.com/,必须以 / 结尾,支持 https。
授权域名小程序支付需配置 JSAPI 支付授权目录,限制调用权限。需在小程序后台和微信支付商户平台同时配置。
API 密钥(API Key)32 位字符串,用于生成签名和验证通知。必须保密,不可泄露,建议定期更换。
JSAPI 支付流程用户 → 公众号/小程序 → 调用统一下单 → 获取 prepay_id → 发起支付请求 → 支付完成 → 异步通知前端需通过 wx.requestPayment 发起支付,参数由后端生成。
获取用户 openid调用登录接口(如 wx.login)获取 code,后端通过 code 换取 openid。同一用户在不同公众号/小程序下 openid 不同,unionid 可跨应用识别。
统一下单接口提交订单信息到微信,获取预支付交易会话标识(prepay_id)。必须使用 HTTPS 调用,签名方式为 MD5 或 HMAC-SHA256。
支付结果通知微信服务器向 notify_url 发送 POST 请求,告知支付结果。必须校验签名,处理成功后返回纯文本 success
支付安全校验所有接口调用需进行签名验证,防止伪造请求。推荐使用 HMAC-SHA256,安全性高于 MD5。
沙箱环境测试微信提供测试环境,可用于模拟支付流程,不产生真实交易。需单独申请沙箱密钥,部分功能受限。

4.2 APP 支付(拉起微信客户端)

方法/参数名称语法/格式示例用途代码示例(Java 风格)注意事项
统一下单接口(APP)POST https://api.mch.weixin.qq.com/pay/unifiedorder生成预支付订单,获取 prepay_id 用于 APP 调起支付。请求参数包含:appid, mch_id, nonce_str, sign, body, out_trade_no, total_fee, spbill_create_ip, notify_url, trade_type=APPtrade_type 必须为 APP;notify_url 需为 HTTPS;total_fee 单位为分。
prepay_id微信返回的 prepay_id 字段APP SDK 发起支付时必需的参数。response.get("prepay_id")有效期 5 分钟,超时需重新下单。
partnerId商户号(mch_id)标识发起支付的商户。"1900009851"与公众号支付使用的 mch_id 相同。
prepayIdprepay_id预支付交易会话 ID。"wx20141126163950b88a1234567890123456"需原样传入 SDK。
packageValue"Sign=WXPay"固定值,表示支付包类型。"Sign=WXPay"必须为 "Sign=WXPay",不可更改。
nonceStr随机字符串防重放攻击,每次请求不同。"c1d2e3f4g5h6i7j8k9l0"建议 32 位以内,由商户生成。
timeStamp当前时间戳(秒)表示请求时间,用于签名和防重放。String.valueOf(System.currentTimeMillis() / 1000)单位是秒,不是毫秒。
sign签名字符串确保请求和响应未被篡改。通过 API 密钥对所有参数按规则排序后签名生成。必须使用与下单时相同的签名算法(MD5/HMAC-SHA256)。
WXPayObjectnew WXPayObject()微信支付 SDK 对象,用于初始化和调用支付。WXPay wxpay = new WXPay(config);需传入配置类(含 appid, mch_id, key, cert 等)。
sendPayReqwxapi.sendReq(req)向微信客户端发送支付请求,拉起支付界面。api.sendReq(req);需在 Activity 中调用;微信客户端必须安装。
onRespoverride fun onResp(baseResp: BaseResp?)接收微信支付结果回调(成功、失败、取消)。if (baseResp.type == ConstantsAPI.COMMAND_PAY_RESULT) { ... }回调在 UI 线程,不可进行耗时操作;结果非最终依据,仍需以异步通知为准。

4.3 扫码支付(模式一与模式二)

方法/参数名称语法/格式示例用途代码示例(Java 风格)注意事项
trade_typeNATIVE指定为扫码支付类型。"trade_type=NATIVE"统一下单时必须设置。
product_id商户定义的商品 ID 或动态参数用于模式一生成二维码(不调用统一下单)。"123456"用户扫描后微信会回调商户设置的 pay_url,并携带 product_id。
code_url微信返回的 URL(如:weixin://wxpay/s/XXXXX用于生成二维码图片,用户扫描后跳转支付。response.get("code_url")可使用开源库(如 ZXing)生成二维码;有效期默认 2 小时。
pay_url商户设置的回调 URL微信在用户扫描模式一二维码后,向此 URL 发起 GET 请求获取订单信息。https://pay.example.com/wxpay/qr_callback需在商户平台配置;返回 XML 格式的统一下单参数。
out_trade_no商户订单号唯一标识一笔交易。"20251017132000001"必须保证全局唯一,建议包含日期和序列号。
total_fee订单金额(单位:分)支付总金额。100 => 1 元不允许有小数,必须为整数。
body商品描述订单标题,显示在支付界面。"测试商品A"建议简洁明了。
spbill_create_ip客户端 IP发起支付的机器 IP。"192.168.1.1"不能为 0.0.0.0 或内网 IP(测试除外)。
notify_url异步通知地址支付结果通知接收 URL。https://api.example.com/wxpay/notify必须为公网 HTTPS 地址。
统一下单(模式二)POST /pay/unifiedorder直接调用统一下单生成 code_url,推荐使用方式。同 4.2 节统一下单,trade_type=NATIVE简单可靠,无需额外回调。
模式一(被扫)1. 生成带 product_id 的二维码 → 2. 微信回调 pay_url → 3. 返回下单参数 → 4. 用户支付适用于商户动态生成二维码场景。需实现 pay_url 接口,解析 product_id 并返回 XML 格式下单数据。流程复杂,已不推荐使用,建议统一用模式二。
模式二(被扫)1. 调用统一下单 → 2. 获取 code_url → 3. 生成二维码 → 4. 用户扫描支付推荐的扫码支付方式。推荐使用此模式,流程简单,稳定性高。

4.4 H5 支付限制与解决方案

概念名称说明注意事项
H5 支付定义在移动端浏览器(如 Safari、Chrome)中调起微信支付。不能在微信 App 内置浏览器中使用(会被拦截)。
场景限制不允许在微信内(如公众号文章、聊天窗口)直接唤醒 H5 支付。微信会提示”请在手机自带浏览器中打开”。
支付域名配置必须在商户平台配置 H5 支付的授权域名。如:m.example.com,需为顶级域名或二级域名。
场景说明适用于:短信链接、广告跳转、外部 App 内嵌浏览器等非微信环境。不能用于公众号或小程序跳转的 H5 页面。
MWEB_URL统一下单成功后返回的 mweb_url 字段用于重定向用户至微信支付收银台。
redirect_url支付完成后用户跳转的商户页面。可在下单时通过参数指定,用于展示支付结果。
trade_typeMWEB统一下单时指定为 H5 支付类型。
用户标识(如 user_id)建议在场景信息中传递,便于后续识别。可通过 redirect_url 携带参数传递。
替代方案:小程序跳转引导用户从 H5 页面跳转至小程序完成支付。需用户已安装微信,体验较好,但依赖小程序。
替代方案:APP 拉起若用户安装了商户 APP,可通过 URL Scheme 或 Universal Links 拉起 APP 支付。需提前配置相关协议,兼容性需测试。
审核要求H5 支付需单独申请开通,提交业务说明和网站信息。审核通过后方可使用。

4.5 JSAPI 调用统一下单接口

参数名称语法/格式示例用途代码示例(Java 风格)注意事项
appidwxd678efh567hg6787公众号或小程序的 AppID。"wxd678efh567hg6787"必须与发起支付的公众号/小程序一致。
mch_id1234567890微信支付商户号。"1900009851"由微信支付分配。
nonce_str5K8264ILTKCH16CQ2502SI8ZNMTM67VS随机字符串,不长于 32 位。UUID.randomUUID().toString().replace("-", "").substring(0, 32)每次请求必须不同,用于防止重放攻击。
signC380BEC2BFD727A4B6845133519F3AD6签名,用于验证请求合法性。通过 API 密钥对所有非空参数按字典序排序后拼接,再进行 MD5/HMAC-SHA256 计算。签名方法需与商户平台设置一致。
sign_typeHMAC-SHA256MD5签名算法类型。"HMAC-SHA256"推荐使用 HMAC-SHA256,安全性更高。
body腾讯充值中心-游戏充值商品简单描述。"商品A"不可为空,限制 32 个字符内。
detail{..."goods_detail": [...]}商品详细列表(可选)。JSON 格式,包含商品名称、数量、价格等。用于订单详情展示和对账。
out_trade_no20150806125346商户系统内部订单号,要求 32 个字符内,只能是数字、大小写字母以及 `_-*@`。System.currentTimeMillis() + "" + random(1000, 9999)
fee_typeCNY标价币种,默认为 CNY。"CNY"目前只支持人民币。
total_fee888订单总金额,单位为分。100 * amount(元)不能带小数,如 1 元=100 分。
spbill_create_ip192.168.1.1客户端 IP 地址。request.getRemoteAddr()不可为 0.0.0.0 或内网 IP(沙箱环境除外)。
notify_urlhttps://api.example.com/wxpay/notify接收微信支付异步通知的回调地址。"https://yourdomain.com/wxpay/notify"必须为公网可访问的 HTTPS 地址。
trade_typeJSAPI交易类型,公众号/小程序支付固定为 JSAPI。"JSAPI"区分于 APP、NATIVE、MWEB。
openidoUpF8uMuAJO_M2pxb1Q9zNjWeS6o用户在商户 AppID 下的唯一标识。从小程序 wx.login 或公众号网页授权获取。必须与 appid 对应,否则支付失败。
product_id12345商品 ID,仅 trade_type=NATIVE 且为模式一时需要。"1001"其他场景可忽略。
time_start20251017132000订单开始时间,格式 yyyyMMddHHmmss。new SimpleDateFormat("yyyyMMddHHmmss").format(new Date())可选,不填则视为立即生效。
time_expire20251017135000订单失效时间,格式同上。new SimpleDateFormat("yyyyMMddHHmmss").format(expireDate)最长不超过 12 小时。
goods_tagWXG订单优惠标记,可用于后续营销活动。"PROMO2025"可选。
attach用户自定义数据通知回调时会原样返回,可用于携带订单上下文。"{\"orderType\": \"vip\"}"建议使用 JSON 字符串,注意 URL 编码。

4.6 查询订单、关闭订单、撤销订单、申请退款、查询退款

接口名称请求 URL请求方式核心参数说明返回关键字段注意事项
查询订单https://api.mch.weixin.qq.com/pay/orderqueryPOSTout_trade_no 或 transaction_id:二选一,用于定位订单。签名等通用参数。trade_state: 支付状态(SUCCESS/REFUND/CLOSED 等);transaction_id: 微信订单号;total_fee: 订单金额;time_end: 交易结束时间可用于支付结果确认;建议在异步通知未收到时主动查询;频率不宜过高(如 30 秒一次)。
关闭订单https://api.mch.weixin.qq.com/pay/closeorderPOSTout_trade_no:商户系统内部订单号,必须提供。result_code: SUCCESS/FAIL;err_code: 错误码(如 ORDERNOTEXIST)仅在未支付状态下有效(如用户未支付、超时);已支付订单无法关闭;关闭后不可逆转。
撤销订单https://api.mch.weixin.qq.com/secapi/pay/reversePOSTout_trade_no 或 transaction_id:指定要撤销的订单。需使用证书认证。recall: 是否需要重试(Y/N);result_code: 撤销结果仅支持刷卡支付(刷卡、扫码)的当日当批次交易;T+1 及之后交易不可撤销;部分银行不支持;成功率非 100%,需根据 recall 判断是否重试。
申请退款https://api.mch.weixin.qq.com/secapi/pay/refundPOSTout_trade_no / transaction_id: 原订单。out_refund_no: 退款单号。total_fee: 原订单金额。refund_fee: 退款金额。需使用证书认证。refund_id: 微信退款单号;refund_fee: 实际退款金额;refund_status: 退款状态(SUCCESS/FAIL/PROCESSING 等)退款金额 <= 订单金额;一笔订单可多次退款,但总退款额 <= 订单金额;退款资金 1-3 工作日到账;需保存退款单号以便查询。
查询退款https://api.mch.weixin.qq.com/pay/refundqueryPOSTout_refund_no / out_trade_no / transaction_id / refund_id:四选一。refund_status_0: 第一条退款记录状态;refund_fee_0: 退款金额;refund_recv_account_0: 退款入账方可查询单笔订单的所有退款记录;状态包括 SUCCESS, FAIL, PROCESSING, NOTSURE(需对账)等;建议定期对账。

4.7 接收并验证支付结果通知

步骤说明代码逻辑示例(伪代码)注意事项
1. 配置 notify_url在统一下单时指定或在商户平台设置默认通知地址。request.setNotifyUrl("https://yourdomain.com/wxpay/notify");必须为公网可访问的 HTTPS 地址。
2. 接收 POST 请求微信服务器以 POST 方式发送 XML 数据到 notify_url。String xml = request.getBody();请求体为纯 XML,非 JSON。
3. 解析 XML将 XML 转换为 Map 或对象。Map<String, String> resultMap = parseXmlToMap(xml);注意处理 CDATA 内容。
4. 校验签名使用 API 密钥对返回参数重新生成签名,并与 sign 字段比对。String calSign = generateSign(resultMap, apiKey, signType);
if (!calSign.equals(resultMap.get("sign"))) { return "签名错误"; }必须校验,防止伪造通知;算法需与下单时一致(MD5/HMAC-SHA256)。
5. 检查支付状态判断 return_code 和 result_code 是否均为 SUCCESS。if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) { // 支付成功 }return_code 表示通信状态,result_code 表示业务结果,两者都需成功。
6. 验证订单金额对比通知中的 total_fee 与商户系统中该订单的金额是否一致。if (notifyFee != dbOrder.getTotalFee()) { return "金额不一致"; }防止被篡改订单金额。
7. 更新订单状态在商户数据库中标记订单为”已支付”,并执行后续业务逻辑(如发货、开通服务)。orderService.updateStatus(outTradeNo, OrderStatus.PAID);必须保证幂等性,因为通知可能重复发送(如 5 分钟内未收到 success 响应)。
8. 返回响应处理成功后,必须返回纯文本 successresponse.write("success");不能返回 XML 或 JSON,否则微信会认为失败并持续重试(共 8 次,间隔递增)。
9. 异常处理若处理失败(如数据库异常),应记录日志但不返回 success,让微信重试。catch(Exception e) { log.error("处理通知失败", e); };不返回 success,让微信重试重试机制:微信会在失败后按 15s/15s/30s/3m/10m/20m/30m/30m 重试。
10. 幂等性保证使用 out_trade_no 作为唯一键,确保同一订单不会被重复处理。synchronized(lockMap.get(outTradeNo)) { ... } 或数据库唯一约束建议使用分布式锁或数据库乐观锁。

4.8 企业付款到零钱与企业红包接口

接口名称请求 URL核心参数说明返回关键字段注意事项
企业付款到零钱https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transferspartner_trade_no: 商户订单号(唯一)。openid: 用户在商户 appid 下的 openid。amount: 金额(单位:分)。desc: 付款描述。check_name: NO_CHECK/FORCE_CHECK/OPTION_CHECK。re_user_name: 收款用户姓名(需实名验证时)。需使用 API 证书。payment_no: 微信付款单号。payment_time: 付款成功时间。单笔金额 1-20000 元;单日无上限(但有风控);24 小时到账;手续费由商户承担(0.1%);需企业资质;不可撤销;需严格风控防刷。
企业付款到银行卡https://api.mch.weixin.qq.com/mmpaysptrans/pay_bankpartner_trade_no, amount, desc。enc_bank_no: 加密的银行卡号。enc_true_name: 加密的真实姓名。bank_code: 银行编号。需使用 API 证书和公钥加密。payment_no, payment_time需单独签约;需提供银行公钥进行加密;支持主流银行;到账时间依银行而定。
发放普通红包https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpackmch_billno: 商户订单号。send_name: 商户名称。re_openid: 用户 openid。total_amount: 金额(分)。total_num: 红包个数(固定为 1)。wishing: 祝福语。act_name: 活动名称。remark: 备注。需使用 API 证书。send_listid: 红包订单号。send_time: 发送时间。单个红包 ≤200 元;单次 ≤200 个;需企业资质;资金实时到账;不可退。
发放裂变红包https://api.mch.weixin.qq.com/mmpaymkttransfers/sendgroupredpack参数同普通红包,支持设置 total_num > 1,可被多个用户领取。send_listid, send_time适用于群聊场景;需注意活动合规和营销规则。

4.9 微信支付 SDK 集成最佳实践

实践要点详细说明代码/配置示例注意事项
1. 环境隔离严格区分开发、测试(沙箱)、生产环境,避免配置混淆。开发环境:使用沙箱密钥和测试商户号;生产环境:使用正式密钥和商户号;配置文件分离:application-dev.yml, application-prod.yml沙箱环境不产生真实交易,部分功能受限;上线前必须在沙箱充分测试。
2. 配置管理将敏感信息(API Key, 证书路径, 商户号)外置化,避免硬编码。application.yml: wxpay: appid: wxd678efh567hg6787 mch-id: 1900009851 api-key: ${WX_API_KEY} cert-path: /certs/apiclient_cert.p12使用环境变量或配置中心管理 api-key 等敏感信息。
3. 异常处理对网络超时、服务不可用、参数错误等异常进行分类处理和日志记录。try { WXPayResponse response = wxpay.unifiedOrder(reqData); } catch (WXPayNetworkException e) { log.error("网络异常", e); // 重试或降级 } catch (WXPayRequestException e) { log.error("请求异常", e); // 检查参数 }网络异常可考虑重试(建议 3 次,指数退避);业务异常需根据错误码处理。
4. 签名与验签统一使用 HMAC-SHA256 算法,确保所有接口(请求和通知)签名一致。String sign = WXPayUtil.generateSignature(params, apiKey, WXPayConstants.SignType.HMACSHA256);
boolean isValid = WXPayUtil.isSignatureValid(notifyMap, apiKey, WXPayConstants.SignType.HMACSHA256);MD5 已不推荐,HMAC-SHA256 更安全;确保参数不包含 sign 字段参与签名计算。
5. 幂等性设计所有写操作(下单、退款)必须保证幂等,防止重复提交。使用 out_trade_no 作为数据库唯一索引。CREATE UNIQUE INDEX idx_out_trade_no ON orders (out_trade_no); 下单前先查询订单是否存在微信侧不保证下单接口幂等,商户侧必须实现。
6. 日志记录详细记录关键接口的请求和响应(脱敏敏感信息),便于排查问题。log.info("统一下单请求: outTradeNo={}, amount={}, ip={}", outTradeNo, totalFee, ip);
log.info("统一下单响应: returnCode={}, resultCode={}, prepayId={}", resp.get("return_code"), resp.get("result_code"), resp.get("prepay_id"));
log.warn("支付通知金额不匹配: notifyFee={}, orderFee={}", notifyFee, orderFee);记录时间戳、商户订单号、关键状态;避免记录完整的 API Key 或用户敏感信息。
7. 监控与告警对支付成功率、退款率、接口响应时间等关键指标进行监控。使用 Prometheus + Grafana 监控 QPS、延迟、错误率;设置告警规则:支付失败率 > 1% 持续 5 分钟及时发现支付通道异常或系统瓶颈。
8. 定期对账每日下载微信支付官方对账单,与商户系统订单进行核对,确保资金一致。下载路径:https://api.mch.weixin.qq.com/pay/downloadbill;对账字段:订单号、金额、状态、手续费对账是发现”漏单”、“错单”的最后防线;建议自动化对账并生成差异报告。
9. 安全防护防止 CSRF、SQL 注入、数据泄露;HTTPS 强制加密;API 接口访问控制。后端接口校验来源 IP(如微信服务器 IP 段);使用 WAF 防护;敏感数据加密存储微信服务器 IP 需定期更新(可通过 API 获取最新 IP 列表)。
10. 版本升级关注微信支付官方公告,及时升级 SDK 和适配新接口。订阅微信支付商户助手公众号;定期检查微信支付官方文档旧接口可能下线(如 MD5 签名),需提前规划升级。

4.10 常见错误码与排查指南

错误码 (err_code)中文描述可能原因排查步骤
INVALID_REQUEST参数错误必填参数缺失或格式错误(如金额非整数、IP 非法)。1. 核对 API 文档,检查所有必填参数。2. 使用微信提供的签名生成工具验证签名。
ORDERPAID订单已支付该笔交易已成功支付,不能重复发起。1. 查询订单状态确认。2. 前端应避免重复点击支付按钮。
TRADE_ERROR交易错误银行处理失败,或用户余额不足。1. 提示用户更换支付方式或银行卡。2. 记录日志,后续分析。
OUT_TRADE_NO_USED商户订单号重复out_trade_no 在微信系统中已存在。1. 检查订单号生成逻辑,确保全局唯一。2. 查询该订单号是否已存在未支付订单。
LACK_PARAMS缺少参数请求中缺少必要参数。1. 检查请求 XML/JSON 是否完整。2. 确认 appid, mch_id, nonce_str, sign 等基础参数已传。
APPID_NOT_EXISTappId 不存在提供的 appid 未在微信支付系统注册或未绑定商户号。1. 登录微信支付商户平台确认绑定关系。2. 检查 appid 拼写是否正确。
MCHID_NOT_MATCH商户号与 appid 不匹配mch_id 和 appid 不属于同一主体或未关联。1. 在商户平台检查关联的公众号/小程序列表。2. 确认 appid 正确。
SIGNERROR签名错误生成的签名与微信计算的不一致。1. 重点检查:API Key 是否正确。2. 确认所有参数(除 sign)都参与签名,且无空格或特殊字符未处理。3. 确认签名算法(MD5/HMAC-SHA256)与平台设置一致。
XML_FORMAT_ERRORXML 格式错误请求或通知的 XML 格式不正确(如标签未闭合、编码问题)。1. 使用 XML 校验工具检查格式。2. 确保 Content-Type 为 text/xml 或 application/xml。
REQUIRE_POST_METHOD请使用 POST 方法使用了 GET 或其他方法调用 POST 接口。1. 确保 HTTP 请求方法为 POST。
IP_NOT_IN_WHITE此 IP 未在商户平台设置调用接口的服务器 IP 未在微信支付商户平台的”安全设置”中配置。1. 登录商户平台,进入”API 安全” -> “IP 白名单”,添加服务器公网 IP。
NOTSUPPORTEXPORT不支持此交易的对账单下载尝试下载不支持的账单类型或时间范围。1. 确认账单日期格式(yyyyMMdd)。2. 确认账单类型(ALL, SUCCESS, REFUND 等)正确。
FREQ_LIMIT接口调用频率超限调用次数超过微信限制(如 100qps)。1. 降低调用频率,增加延迟。2. 检查是否有死循环或异常重试。
NOAUTH无此接口权限商户未开通该支付产品(如 H5 支付、企业付款)。1. 登录商户平台,检查”产品中心”是否已申请并开通相应权限。
TOTAL_FEE_EXCEED订单总金额超过限额单笔交易金额超过微信或银行限制。1. 拆分大额订单。2. 提示用户使用其他支付方式。

提示: 最全的错误码列表请参考 微信支付官方文档

第五章:支付宝 SDK 接入详解

5.1 APP 支付(移动端集成)

项目说明
适用场景集成在 iOS 或 Android App 内,用户点击支付后调起支付宝 App 完成支付。
核心流程1. 商户服务端调用 alipay.trade.app.pay 接口,生成包含订单信息的支付字符串。
2. 将支付字符串返回给 App 客户端。
3. App 调用支付宝 SDK 的 pay 方法传入支付字符串,拉起支付宝 App。
4. 用户在支付宝内完成支付。
5. 支付宝将结果同步返回给 App,并通过异步通知告知商户服务端。
关键参数out_trade_no: 商户订单号(唯一)
total_amount: 订单总金额(单位:元)
subject: 订单标题
product_code: 固定为 QUICK_MSECURITY_PAY
notify_url: 异步通知地址
SDK 调用 (Android)java\nfinal Runnable payRunnable = new Runnable() {\n @Override\n public void run() {\n PayTask alipay = new PayTask(activity);\n Map<String, String> result = alipay.payV2(payInfo, true);\n Message msg = new Message();\n msg.obj = result;\n mHandler.sendMessage(msg);\n }\n};\nThread payThread = new Thread(payRunnable);\npayThread.start();\n
注意事项- 必须集成支付宝官方 SDK。
- out_trade_no 必须全局唯一。
- 同步返回结果不可靠,仅作参考,最终结果以异步通知为准。
- 需在支付宝开放平台配置应用的 APPID 和回调地址。

5.2 网页支付(即时到账与电脑网站支付)

类型接口说明
电脑网站支付alipay.trade.page.pay用户在 PC 浏览器访问,重定向到支付宝收银台页面完成支付。适用于传统 PC 站点。
手机网站支付alipay.trade.wap.pay用户在手机浏览器访问,跳转到支付宝 H5 收银台。适用于移动端网页。
项目说明
核心流程1. 服务端构造请求参数并签名。
2. 生成一个 form 表单或重定向 URL,包含所有参数。
3. 用户浏览器自动提交表单或跳转到该 URL,进入支付宝支付页面。
4. 支付成功后,支付宝根据 return_url 同步跳转回商户页面(可选),并通过 notify_url 发送异步通知。
关键参数out_trade_no, total_amount, subject, product_code (FAST_INSTANT_TRADE_PAY), notify_url, return_url (可选)
生成表单示例html\n<form name='punchout_form' method='post' action='https://openapi.alipay.com/gateway.do'>\n <input type='hidden' name='app_id' value='2018101800000000'>\n <input type='hidden' name='method' value='alipay.trade.page.pay'>\n <input type='hidden' name='format' value='JSON'>\n <input type='hidden' name='charset' value='utf-8'>\n <input type='hidden' name='sign_type' value='RSA2'>\n <input type='hidden' name='timestamp' value='2025-10-17 13:30:00'>\n <input type='hidden' name='version' value='1.0'>\n <input type='hidden' name='notify_url' value='https://yourdomain.com/alipay/notify'>\n <input type='hidden' name='return_url' value='https://yourdomain.com/alipay/return'>\n <input type='hidden' name='biz_content' value='{"out_trade_no":"202510170001","total_amount":"9.90","subject":"测试商品","product_code":"FAST_INSTANT_TRADE_PAY"}'>\n <input type='hidden' name='sign' value='生成的签名'>\n <input type='submit' value='立即支付' style='display:none'>\n</form>\n<script>document.forms[0].submit();</script>\n
注意事项- return_url 仅用于页面跳转展示,不可用于业务逻辑。
- notify_url 是最终结果通知的唯一可靠途径。
- 需配置支付网关的域名白名单。

5.3 小程序支付接入

项目说明
适用场景在支付宝小程序内发起支付。
核心流程1. 小程序前端调用服务端接口获取支付参数。
2. 服务端调用 alipay.trade.createalipay.trade.precreate 生成预付订单,或直接调用 alipay.trade.app.pay 获取 orderString。
3. 服务端将支付参数(orderString)返回给小程序。
4. 小程序前端调用支付宝 my.requestPayment API 发起支付。
5. 用户确认支付,结果通过异步通知返回服务端。
关键参数out_trade_no, total_amount, subject, buyer_id (可选,指定用户), timeout_express (超时时间)
小程序前端调用javascript\nmy.requestPayment({\n tradeNo: orderString, // 服务端返回的orderString\n success: (res) => {\n console.log('支付成功', res); // 同步结果,仅参考\n },\n fail: (err) => {\n console.log('支付失败', err);\n },\n});\n
注意事项- 小程序必须是支付宝小程序。
- 需在支付宝开放平台配置小程序 APPID。
- 支付权限需在小程序 app.json 中声明。

5.4 扫码支付(主扫与被扫)

类型接口说明
被扫支付(主扫)alipay.trade.pay用户打开支付宝”付款码”,商户扫描用户付款码完成扣款。适用于线下收银。
主扫支付(被扫)alipay.trade.precreate商户生成一个二维码(包含订单信息),用户用支付宝 App 扫描该二维码完成支付。
项目说明
被扫关键参数out_trade_no, total_amount, subject, auth_code (用户的付款码)
主扫关键参数out_trade_no, total_amount, subject
主扫流程1. 服务端调用 alipay.trade.precreate
2. 支付宝返回一个 qr_code 字符串。
3. 商户系统将 qr_code 生成二维码图片展示给用户。
4. 用户扫码支付。
注意事项- 被扫:auth_code 有效期短(通常 1 分钟),需快速调用接口;需处理 AUTH_CODE_INVALID 等错误。
- 主扫:二维码可重复扫描,需保证 out_trade_no 幂等性。
- 两种方式均需处理支付结果的异步通知。

5.5 统一收单交易创建与查询

接口说明关键参数返回关键字段
创建订单 alipay.trade.create创建交易订单,不立即支付。可用于后续的扫码或刷卡支付。out_trade_no, total_amount, subject, buyer_id (可选)trade_no: 支付宝交易号
out_trade_no: 商户订单号
查询订单 alipay.trade.query根据 out_trade_notrade_no 查询订单状态。out_trade_notrade_notrade_status: 交易状态 (WAIT_BUYER_PAY, TRADE_SUCCESS, TRADE_FINISHED, TRADE_CLOSED)
send_pay_date: 付款时间
buyer_user_id: 买家支付宝ID
关闭订单 alipay.trade.close关闭未支付的交易。out_trade_notrade_notrade_no, out_trade_no, msg (Success/Closed)
撤销订单 alipay.trade.cancel取消交易(与关闭类似,但语义更广)。out_trade_notrade_notrade_no, out_trade_no, action (close/refund/nothing)
项目说明
注意事项- query 接口可用于对账和结果确认。
- closecancel 在未支付状态下效果类似,建议优先使用 close
- 已支付的订单无法关闭,只能退款。

5.6 退款操作与退款查询

接口说明关键参数返回关键字段注意事项
申请退款 alipay.trade.refund对已支付的订单进行退款。out_trade_no / trade_no
out_request_no: 本次退款请求号(幂等)
refund_amount: 退款金额
refund_reason: 退款原因
trade_no
out_trade_no
refund_fee
gmt_refund_pay: 退款时间
- 退款金额 <= 订单金额。
- 一笔订单可多次退款,总和 <= 订单金额。
- out_request_no 必须保证本次退款请求唯一,防止重复退款。
- 退款即时到账(通常秒级)。
查询退款 alipay.trade.fastpay.refund.query查询单笔退款的处理结果。out_trade_no / trade_no
out_request_no: 退款请求号
refund_amount
refund_status: (SUCCESS, FAILED)
- 用于确认退款是否成功。
- 退款状态为 SUCCESS 才代表资金已退回。

5.7 支付宝异步通知处理

步骤说明代码逻辑示例(伪代码)注意事项
1. 配置 notify_url在支付请求中指定或在开放平台设置。request.setNotifyUrl("https://yourdomain.com/alipay/notify");必须为公网可访问的 HTTPS 地址。
2. 接收 POST 请求支付宝服务器以 POST 方式发送 application/x-www-form-urlencoded 数据。Map<String, String> params = request.getParameterMap();请求体是表单格式,非 JSON 或 XML。
3. 验签使用支付宝公钥验证通知的签名,确保来源可信。java\nboolean isValid = AlipaySignature.rsaCheckV1(params, alipayPublicKey, "UTF-8", "RSA2");\nif (!isValid) {\n log.error("支付宝异步通知验签失败");\n return "failure"; // 返回failure让支付宝重试\n}\n必须验签!防止伪造通知。公钥需从支付宝开放平台获取。
4. 检查 trade_status判断交易状态是否为 TRADE_SUCCESS 或 TRADE_FINISHED。```java\nString tradeStatus = params.get(“trade_status”);\nif (“TRADE_SUCCESS”.equals(tradeStatus)
5. 验证 app_id 和 seller_id确认通知是发给本商户的。```java\nif (!params.get(“app_id”).equals(ourAppId)
6. 验证订单金额对比通知中的 total_amount 与商户系统订单金额。if (Double.parseDouble(params.get("total_amount")) != order.getAmount()) { return "failure"; }防止金额被篡改。
7. 更新订单状态在数据库中标记订单为已支付。orderService.paySuccess(outTradeNo);必须保证幂等性!同一通知可能多次发送。
8. 返回响应处理成功后,必须返回纯文本 successresponse.getWriter().write("success");不能返回其他任何内容,否则支付宝会认为失败并持续重试(最多 4 次)。
9. 异常处理若处理中发生异常(如数据库故障),应捕获异常但不返回 successjava\ntry {\n // 处理逻辑\n} catch (Exception e) {\n log.error("处理支付宝通知失败", e);\n // 不返回success,让支付宝重试\n}\n重试机制保障了可靠性。
10. 幂等性保证使用 out_trade_no + 状态机或数据库唯一约束。UPDATE orders SET status = 'PAID' WHERE out_trade_no = ? AND status = 'UNPAID';确保同一订单不会被重复处理成功。

5.8 单笔转账到支付宝账户接口

项目说明
接口alipay.fund.trans.toaccount.transfer
适用场景商户向用户的支付宝账户(alipay_user_idpayee_account)打款,如提现、佣金发放、退款等。
核心流程1. 服务端构造转账请求,包含收款方、金额、单号等。
2. 调用接口发起转账。
3. 支付宝处理转账,结果通过异步通知(可选)告知商户。
关键参数out_biz_no: 商户转账唯一订单号(幂等)
payee_type: ALIPAY_USERID 或 ALIPAY_LOGONID
payee_account: 收款方支付宝账号(ID 或手机号/邮箱)
amount: 转账金额(单位:元)
payer_show_name: 付款方显示名称
payee_real_name: 收款方真实姓名(可选,加强风控)
remark: 转账备注
返回关键字段out_biz_no
order_id: 支付宝转账单据号
pay_date: 支付宝处理成功时间
status: SUCCESS / FAIL / PROCESSING
注意事项- 必须使用应用私钥签名,且需在支付宝开放平台配置应用。
- 单笔限额:通常 1 分 - 5 万元。
- 手续费:目前免费,但可能调整。
- 风控:大额或高频转账可能被拦截,需实名认证。
- 结果确认:接口返回 SUCCESS 仅表示支付宝受理成功,最终是否到账需查询 alipay.fund.trans.order.query 接口或等待异步通知。
- 异步通知:需配置 transfer_notify_url 接收最终结果。
- 资金来源:从商户支付宝账户余额扣除。

5.9 支付宝 SDK 集成最佳实践

实践要点详细说明代码/配置示例注意事项
1. 环境与配置管理严格区分开发(沙箱)、生产环境,使用不同的 APPID 和密钥。沙箱环境 APPID: 2021000000000000;生产环境 APPID: 2021000000000001
配置文件分离:alipay.propertiesapplication-alipay.yml
沙箱环境用于测试,无真实资金流动;上线前必须切换为生产配置。
2. 密钥安全私钥绝对保密,严禁硬编码或提交到代码仓库;公钥正确配置在开放平台。将私钥存储在服务器安全目录或密钥管理服务(KMS)。
private_key = "MIIEvQIBADANBgkqhkiG..."; // 从文件或环境变量读取
支付宝公钥用于验签,应用私钥用于请求签名。
3. 异常处理与重试对网络超时、系统错误进行捕获,对幂等操作可安全重试。java\ntry {\n AlipayTradeQueryResponse response = alipayClient.execute(request);\n} catch (AlipayApiException e) {\n if (isNetworkError(e)) {\n // 重试最多3次\n retry();\n } else {\n log.error("支付宝API调用失败", e);\n // 记录失败,人工介入\n }\n}\n非幂等接口(如 trade.pay)避免重试,防止重复扣款。
4. 幂等性保证所有创建类请求(支付、退款、转账)必须使用唯一业务单号(out_ 开头)。支付:out_trade_no = ORD202510171345001
退款:out_request_no = RFD202510171345001_1
转账:out_biz_no = TRF202510171345001
业务单号是支付宝侧保证幂等的关键,商户必须确保其全局唯一。
5. 日志记录详细记录关键接口的请求参数(脱敏)和响应结果。java\nlog.info("支付宝支付请求: outTradeNo={}, amount={}, subject={}", outTradeNo, amount, subject);\nlog.info("支付宝支付响应: isSuccess={}, tradeStatus={}, tradeNo={}", response.isSuccess(), response.getTradeStatus(), response.getTradeNo());\nlog.warn("支付宝退款金额超限: refundAmount={}, totalAmount={}", refundAmount, totalAmount);\n记录时间戳、业务单号、关键状态;避免记录完整私钥或用户敏感信息。
6. 监控与告警监控支付成功率、退款率、接口延迟、错误码分布。使用 APM 工具(如 SkyWalking)监控接口性能。
设置告警:支付失败率 > 2% 持续 10 分钟。
及时发现支付通道问题或系统异常。
7. 定期对账每日下载支付宝官方账单,与商户系统订单核对。下载路径:支付宝商家中心 -> 对账中心 -> 下载账单。
对账字段:交易号、订单号、金额、手续费、交易状态。
对账是确保资金准确的最后防线;建议自动化脚本每日执行。
8. 安全防护防止 CSRF、SQL 注入;HTTPS 强制加密;IP 白名单(可选)。后端接口校验 app_idseller_id
敏感操作(如转账)增加管理员审批或二次确认。
支付宝服务器 IP 段较广,通常不强制 IP 白名单,但可作为辅助风控。
9. 版本与兼容性使用最新稳定版 SDK,关注支付宝开放平台公告。定期检查支付宝开发者文档更新。
SDK 升级前在沙箱充分测试。
旧接口可能废弃,需及时升级适配。
10. 用户体验支付前后给予清晰反馈;支付失败提供明确原因。支付中:显示加载动画。
支付成功:跳转成功页,显示订单信息。
支付失败:提示”余额不足”、“网络异常”等具体原因。
良好的体验可降低支付流失率。

5.10 常见错误码与排查指南

错误码 (sub_code)中文描述可能原因排查步骤
ACQ.TRADE_NOT_EXIST交易不存在out_trade_notrade_no 在支付宝系统中找不到。1. 核对订单号是否正确。
2. 确认该订单是否已成功创建。
ACQ.EXIST_FORBIDDEN_WORD订单信息中包含违禁词subjectbody 中包含敏感词汇。1. 检查订单标题和描述内容。
2. 修改为合规的描述。
ACQ.PAYMENT_AUTH_CODE_INVALID付款码无效用户的付款码已过期或被使用。1. 提示用户刷新付款码。
2. 确保扫码后立即调用接口。
ACQ.TOTAL_FEE_EXCEED订单总金额超过限额单笔交易金额超过支付宝或银行限制。1. 拆分大额订单。
2. 提示用户使用其他支付方式。
ACQ.BUYER_BALANCE_NOT_ENOUGH买家余额不足用户支付宝余额或绑定的银行卡余额不足。1. 提示用户充值或更换支付方式。
ACQ.INVALID_PARAMETER参数无效请求参数格式错误或缺失必填项。1. 仔细核对 API 文档中的参数要求。
2. 检查 biz_content JSON 格式是否正确。
ACQ.CONTEXT_INCONSISTENT交易信息不一致重复使用同一个 out_trade_no 发起支付。1. 确保 out_trade_no 全局唯一。
2. 下单前查询订单是否存在。
PERMISSION_DENIED权限不足应用未开通该接口权限或账户受限。1. 登录支付宝开放平台,检查应用是否已签约并开通”电脑网站支付”等产品权限。
ILLEGAL_SIGN签名错误生成的签名与支付宝计算的不一致。1. 重点检查:应用私钥是否正确。
2. 确认签名算法(RSA2)一致。
3. 参数是否按字母序排序后签名,sign 参数不参与签名。
ACCESS_FORBIDDEN访问受限应用 APPID 被冻结或接口调用频率超限。1. 检查应用状态是否正常。
2. 降低调用频率,检查是否有异常调用。
SYSTEM_ERROR系统异常支付宝服务器内部错误。1. 记录日志,稍后重试。
2. 联系支付宝技术支持。
RETRYABLE可重试的系统异常网络抖动或临时故障导致。1. 实现指数退避重试机制(如 1s, 2s, 4s)。
NOT_ENOUGH_PAYMENT_AMOUNT可用余额不足商户支付宝账户余额不足以支付订单或转账。1. 登录商家中心充值。
INVALID_TEMPLATE_ID模板 ID 无效发送消息时使用的模板 ID 不存在或未审核通过。1. 在支付宝开放平台检查消息模板。

提示: 最全的错误码列表请参考支付宝开放平台文档。建议在代码中建立错误码映射,向用户展示友好的错误提示。

第六章:银联支付接入(简要)

6.1 银联全渠道支付(UnionPay)概述

银联全渠道支付是中国银联为商户提供的综合性支付解决方案,支持银行卡(借记卡、信用卡)、银联二维码、手机闪付(Apple Pay、Huawei Pay 等)等多种支付方式。其核心特点如下:

  • 多渠道覆盖:支持线上(APP、网页、小程序)和线下(POS、扫码)全场景支付。
  • 统一接口:通过一套标准化的 API 接口,接入多种银联支付产品。
  • 安全合规:严格遵循国家金融行业标准,采用高强度加密和证书认证机制,保障交易安全。
  • 清算高效:资金通过银联系统清算,结算周期清晰(通常为 T+1)。
  • 适用场景:广泛应用于电商、零售、交通、教育、医疗等需要受理银行卡支付的行业。

核心交易模式:

  • 消费:用户支付商品或服务费用。
  • 查询:查询交易状态。
  • 退货:对已支付的交易进行退款。
  • 撤销:取消未完成清算的当日交易。

6.2 前后台交易接口说明(消费、查询、退货等)

银联接口分为前台交易和后台交易:

  • 前台交易:需要用户在浏览器或 App 中进行交互(如跳转、输入密码)。结果通常是同步返回的,但最终状态仍需以后台通知或查询为准。
  • 后台交易:在商户服务器与银联服务器之间进行,无需用户直接参与。结果通过异步通知或主动查询获取。
交易类型接口名称(功能码)交易方向说明
消费consume (01)前台/后台创建支付订单。前台交易会跳转到银联支付页面;后台交易用于无感支付等。
消费撤销consumeUndo (31)后台撤销一笔当日成功的消费交易(通常要求在当日且未清算前)。
预授权auth (01)前台/后台冻结用户卡内部分额度(如酒店押金)。
预授权撤销authUndo (31)后台撤销预授权,解冻额度。
预授权完成capture (02)后台将预授权的冻结金额转为实际扣款。
预授权完成撤销captureUndo (41)后台撤销一笔预授权完成的交易。
退货refund (04)后台对已清算的交易进行退款。可部分或全额退货。
查询query (00)后台根据订单号查询交易的最终状态(成功、失败、处理中)。是确认交易结果的最可靠方式。
文件传输fileTransfer后台用于下载对账单、差错文件等。

关键参数(以消费为例):

  • merId: 商户号(由银联分配)
  • orderId: 商户订单号(必须全局唯一)
  • txnTime: 订单发送时间(格式:YYYYMMDDhhmmss)
  • txnAmt: 交易金额(单位:分)
  • currencyCode: 货币代码(人民币为 156)
  • frontUrl: 前台通知地址(前台交易后,银联跳转回商户页面的 URL)
  • backUrl: 后台通知地址(银联服务器异步通知交易结果的 URL)
  • signMethod: 签名方法(通常为 01,表示证书签名)
  • version: 接口版本号(如 5.1.0)

6.3 测试环境与生产环境配置

环境说明配置要点
测试环境用于开发和联调,不产生真实资金流动。- 服务器地址:通常为 https://gateway.test.95516.comhttps://mgate.unionpay.com(具体以银联文档为准)。
- 商户号 merId:银联提供的测试商户号。
- 证书:使用银联颁发的测试环境证书(.pfx 或 .jks 文件)。
- 签名密钥:测试证书的密码。
- 注意事项:在测试环境充分验证所有交易流程和异常处理。
生产环境正式上线,处理真实交易。- 服务器地址:银联生产网关地址,如 https://gateway.95516.com
- 商户号 merId:银联审核通过后分配的正式商户号。
- 证书:必须使用银联颁发的生产环境证书,并严格保管。
- IP 白名单:将商户服务器的公网 IP 添加到银联商户平台的白名单中,确保服务器可访问生产网关。
- 注意事项:上线前必须完成生产环境的联调测试(通常称为”生产联调”),并获得银联确认。

配置管理建议:

  • 使用不同的配置文件(如 upay-test.properties, upay-prod.properties)隔离环境。
  • 敏感信息(证书路径、密码)通过环境变量或配置中心管理,避免硬编码。

6.4 签名与加密机制(证书方式)

银联支付采用数字证书进行签名和加密,是其安全体系的核心。

证书申请:

  • 商户向银联或其收单机构提交申请。
  • 银联审核通过后,会为商户生成一对公钥/私钥。
  • 商户获得一个包含私钥和公钥的 PKCS#12 (.pfx) 或 JKS 格式的证书文件,以及一个证书序列号 (certId)。
  • 商户将公钥证书提交给银联,银联将使用该公钥来验证商户请求的签名。
  • 银联也会提供一个银联根证书和银联中级证书给商户,商户使用这些证书来验证来自银联的通知和响应的签名。

签名过程(商户 → 银联):

  • 商户将请求报文中的关键字段(如 merId, orderId, txnTime, txnAmt 等)按银联规定的格式和顺序拼接成一个字符串。
  • 使用商户的私钥对该字符串进行 SHA-1 或 SHA-256 算法的签名,生成一个二进制签名值。
  • 将二进制签名值进行 Base64 编码,得到最终的 signature 字符串。
  • signaturecertId(证书序列号)作为请求参数发送给银联。
  • 银联验签:银联收到请求后,使用商户的公钥(通过 certId 查找)对 signature 进行解密,并与自己按同样规则拼接计算的字符串进行比对,一致则验签通过。

加密过程(可选,用于敏感信息):

  • 对于卡号、CVN2 等极度敏感的信息,银联可能要求使用银联的公钥进行加密。
  • 加密后的内容再进行 Base64 编码传输。
  • 银联使用自己的私钥解密。

验签过程(银联 → 商户):

  • 银联在发送后台通知或查询响应时,也会使用其私钥对报文进行签名。
  • 商户收到通知后,必须使用银联提供的根证书和中级证书来验证 signature 的有效性,确保消息确实来自银联且未被篡改。

关键代码逻辑(伪代码):

// 1. 构造待签名字符串(以消费为例)
String signData = "merId=" + merId +
                  "&orderId=" + orderId +
                  "&txnTime=" + txnTime +
                  "&txnAmt=" + txnAmt +
                  "&currencyCode=" + currencyCode +
                  "..."; // 按银联文档规定的字段和顺序

// 2. 使用商户私钥进行签名
byte[] signatureBytes = SignUtil.signByCert(signData, "SHA256", merchantPrivateKey);
String signature = Base64.encode(signatureBytes);

// 3. 构造请求参数
Map<String, String> requestParams = new HashMap<>();
requestParams.put("merId", merId);
requestParams.put("orderId", orderId);
requestParams.put("txnTime", txnTime);
requestParams.put("signature", signature);
requestParams.put("certId", certId); // 商户证书序列号

// 4. 发送 HTTPS 请求到银联网关
HttpResponse response = HttpClient.post(gatewayUrl, requestParams);

// 5. 接收并验证银联通知(异步)
Map<String, String> notifyParams = parseNotify(request);
// 使用银联证书验证签名
boolean isValid = SignUtil.validateSignature(notifyParams, acquirerCert);
if (isValid) {
    // 处理业务逻辑
    processBusiness(notifyParams);
    return "success"; // 返回success
} else {
    log.error("银联通知验签失败");
    return "fail";
}

注意事项:

  • 证书保管:商户的私钥证书是最高机密,必须存储在安全位置(如服务器安全目录、HSM 硬件),严禁泄露。
  • 证书更新:数字证书有有效期(通常 1-2 年),需在到期前及时更新,否则会导致交易失败。
  • 严格遵循文档:拼接签名字符串的字段、顺序、编码(通常为 UTF-8)必须与银联官方文档完全一致,否则验签必失败。
  • HTTPS:所有与银联的通信必须通过 HTTPS 协议进行加密传输。

第七章:支付安全体系构建

7.1 敏感信息加密传输(AES、RSA)

在支付系统中,用户银行卡号、CVV2、身份证号、手机号、支付密码等均属于敏感信息,必须在传输过程中进行加密保护。

加密方式原理适用场景优点缺点实践示例
对称加密 (AES)加密和解密使用同一个密钥。- 商户系统内部数据传输(如服务间调用)。
- 加密数据库中的敏感字段。
- 加密缓存中的用户信息。
速度快,适合加密大量数据。密钥分发和管理困难,一旦密钥泄露,所有数据不安全。java\n// 使用AES-256-CBC加密银行卡号\nString encryptedCardNo = AESUtil.encrypt(cardNo, "merchantSecretKey256bits");\n// 存储或传输 encryptedCardNo\n
非对称加密 (RSA)使用公钥加密,私钥解密。- 前端 → 后端传输敏感信息(如支付表单提交)。
- 商户 → 支付平台传输数据(如银联、部分网关)。
公钥可公开,私钥保密,解决了密钥分发问题。速度慢,不适合加密大段数据。javascript\n// 前端使用从后端获取的RSA公钥加密\nconst encryptedData = JSEncrypt(publicKey, sensitiveInfo);\nfetch('/api/pay', { body: JSON.stringify({ encryptedData }) });\n
java\n// 后端使用私钥解密\nString sensitiveInfo = RSAUtil.decrypt(encryptedData, privateKey);\n
混合加密结合两者优势。- HTTPS 的核心机制。
- 安全的 API 通信。
安全且高效。实现稍复杂。1. 随机生成一个 AES 会话密钥。
2. 用接收方的 RSA 公钥加密这个 AES 密钥。
3. 用 AES 密钥加密实际的敏感数据。
4. 将加密后的 AES 密钥和加密后的数据一起发送。
5. 接收方用 RSA 私钥解密出 AES 密钥,再用 AES 密钥解密数据。

最佳实践:

  • HTTPS 强制使用:所有涉及支付的接口必须通过 HTTPS (TLS 1.2+) 访问,这是最基本的安全保障。
  • 最小化传输:前端应避免直接传输完整的银行卡号、CVV2 等信息。可使用支付组件(如微信/支付宝的 SDK)在客户端完成加密或直接调起支付应用。
  • 密钥管理:使用密钥管理服务(KMS)或 HSM 硬件安全模块来存储和管理主密钥,避免密钥硬编码。

7.2 防重放攻击与请求时效性控制

重放攻击指攻击者截获一次合法的请求(如支付请求),然后重复发送该请求,以达到非法目的(如重复扣款)。

防护机制说明实现方式注意事项
时间戳 (Timestamp)要求请求中包含一个时间戳,服务端验证其是否在有效期内。- 请求参数包含 timestamp(如 1634456789000)。
- 服务端计算 current_time - timestamp
- 如果差值 > 阈值(如 5 分钟),则拒绝请求。
- 依赖双方时间同步(使用 NTP 协议)。
- 阈值不宜过长或过短。
随机数 (Nonce)要求每次请求携带一个唯一的、不可预测的随机数。- 服务端维护一个 Nonce 缓存(如 Redis),存储最近 N 分钟内使用过的 Nonce。
- 收到请求时,检查 Nonce 是否已存在。
- 不存在则处理请求,并将 Nonce 存入缓存;存在则拒绝。
- 缓存过期时间必须大于请求有效期。
- 缓存空间需考虑,可使用 LRU 策略。
- Nonce 长度要足够(如 16 位随机字符串)。
序列号 (Sequence Number)为每个客户端维护一个递增的序列号。- 客户端在请求中包含 seq
- 服务端记录该客户端的最新 seq
- 收到请求时,检查 seq > 最新 seq,否则拒绝。
- 适用于长连接或状态化客户端。
- 客户端重启后序列号管理复杂。
签名 (Signature)timestampnonce 纳入签名计算范围。- 签名原文包含 timestamp, nonce, params 等。
- 服务端收到请求后,先验证 timestamp 有效性,再验证 nonce 唯一性,最后验证 signature
这是最推荐的方式,结合了时效性和唯一性验证。

综合方案示例:

// 请求参数
{
  "params": { "orderId": "123", "amount": 99.9 },
  "timestamp": 1634456789000,
  "nonce": "aB3kL9mN2pQx",
  "signature": "生成的签名"
}
// 服务端校验逻辑
if (Math.abs(System.currentTimeMillis() - timestamp) > 300_000) { // 5分钟
    throw new SecurityException("请求已过期");
}
if (redis.exists("nonce:" + nonce)) {
    throw new SecurityException("重复请求");
}
// 验证签名
boolean isValid = verifySignature(params, timestamp, nonce, signature, secretKey);
if (!isValid) {
    throw new SecurityException("签名无效");
}
// 校验通过,处理业务,并将nonce存入Redis(过期时间6分钟)
redis.setex("nonce:" + nonce, 360, "1");

7.3 IP 白名单与调用频率限制

防护机制说明实现方式注意事项
IP 白名单只允许来自特定 IP 地址或 IP 段的请求访问关键接口。- 支付平台 → 商户:微信/支付宝/银联的异步通知服务器 IP 通常是固定的,商户应配置白名单,只允许这些 IP 访问 notify_url
- 商户 → 支付平台:部分支付平台(如银联)要求商户服务器 IP 在其平台备案。
- 内部服务:核心支付服务只对内部网关或特定服务开放。
- 需定期更新白名单(如微信支付会公布其服务器 IP 段)。
- 云环境 IP 可能变动,需与云服务商确认。
调用频率限制 (Rate Limiting)限制单位时间内对某个接口的调用次数,防止 DDoS 或恶意刷单。- 基于 IP:同一 IP 每秒/分钟最多调用 N 次。
- 基于用户/设备:同一用户/设备 ID 每天最多支付 M 次。
- 基于接口:/api/pay 接口每秒最多 100 次。
- 实现:使用 Redis 记录计数,如 INCR api:pay:ip:${ip},配合 EXPIRE 设置过期时间。
- 合理设置阈值,避免误伤正常用户。
- 区分普通接口和核心支付接口的限流策略。
- 记录被限流的请求用于分析。

7.4 支付令牌(Token)机制设计

支付令牌是一种用临时、唯一的标识符(Token)代替真实敏感信息(如银行卡号)的技术,常用于”免密支付”或”快捷支付”。

设计目标:

  • 用户首次绑卡后,后续支付无需重复输入卡信息。
  • 提高支付体验,降低支付流失率。
  • 减少商户端存储和传输敏感信息的风险。

核心流程:

1. 绑卡 (Tokenization):

  • 用户在商户 App/网页输入银行卡信息。
  • 商户将卡信息通过安全通道(HTTPS + 加密)发送给支付网关或持卡人信息绑定平台(如银行、银联、第三方支付机构)。
  • 平台验证卡信息有效性,生成一个唯一的 payment_token(支付令牌)和 token_expires_at(过期时间)。
  • 平台将 payment_token 返回给商户。
  • 商户将 payment_token 与用户 ID 关联,安全存储在数据库中(建议加密)。

2. 支付 (Using Token):

  • 用户选择使用已绑定的银行卡支付。
  • 商户服务端根据用户 ID 查找对应的 payment_token
  • 商户调用支付接口,将 payment_token 作为参数传给支付平台。
  • 支付平台根据 payment_token 查找对应的银行卡信息,完成扣款。
  • 支付结果返回商户。

3. 令牌管理:

  • 有效期:设置合理的过期时间(如 1-2 年),过期后需用户重新绑卡。
  • 状态管理:支持 active(可用)、inactive(停用)、expired(过期)、revoked(用户主动解绑)。
  • 安全性
    • payment_token 应为高强度随机字符串(如 UUID v4)。
    • 传输和存储过程必须加密。
    • 支付平台侧需有严格的令牌验证和风控机制。

7.5 防刷单与风控策略初步设计

防刷单旨在防止恶意用户通过自动化脚本、代理 IP 等手段大量创建虚假订单,消耗系统资源或套取优惠。

风控维度具体策略说明
设备指纹采集设备信息(如浏览器 UA、屏幕分辨率、时区、插件、Canvas 指纹)生成唯一标识。同一设备短时间内大量请求,判定为可疑。
行为分析监控用户操作行为(如鼠标移动轨迹、点击间隔、页面停留时间)。机器行为通常过于规律或极快。
IP 分析- 检查 IP 是否为代理、VPN、数据中心 IP。
- 同一 IP 关联大量不同账户或订单。
- IP 地域与用户常用地域不符。
使用 IP 情报库进行识别。
账户分析- 新注册账户、未实名认证账户。
- 账户关联的手机号、邮箱、银行卡是否为新注册或高风险。
- 账户历史行为(如退款率、投诉率)。
建立用户风险画像。
交易特征- 订单金额异常(如整数、特定优惠券金额)。
- 下单时间异常(如凌晨集中下单)。
- 商品组合异常(如只买高价值优惠商品)。
- 退货/退款频率过高。
基于规则或机器学习模型识别。
频率限制- 同一用户/设备/IP 每小时下单次数限制。
- 同一优惠券使用次数限制。
最基础的防护。
人机验证在可疑请求时,弹出滑块、点选等验证码。平衡安全与体验,避免对所有用户强制验证。

初步实施建议:

  • 日志埋点:全面记录用户行为、设备、网络、交易日志。
  • 建立规则引擎:实现简单的规则,如”同一 IP 10 分钟内下单 > 5 次”则触发风控。
  • 引入第三方服务:使用专业的反欺诈服务(如阿里云 RiskDNA、腾讯天御)。
  • 人工审核队列:对高风险订单进入人工审核流程。

7.6 数据库敏感字段存储规范

绝对禁止以明文形式存储用户的敏感信息。

敏感字段存储规范说明
银行卡号 (Card Number)脱敏存储 + 加密- 显示给用户时:6222 **** **** 1234(保留前后4位,中间用 * 号代替)。
- 数据库存储:使用 AES 等对称加密算法加密完整卡号。密钥由 KMS 管理。
CVV2 / CVC2禁止存储根据 PCI DSS 安全标准,绝对不允许在任何系统中存储 CVV2/CVC2 码。
银行卡有效期 (Expiry Date)可存储,建议加密可以存储,但建议与卡号一样进行加密存储。
身份证号 (ID Number)脱敏显示 + 加密存储- 显示:110101********1234
- 存储:使用 AES 加密。
手机号 (Phone Number)脱敏显示 + 可选择加密- 显示:138****1234
- 存储:可选择加密,或使用哈希(如 SHA-256)用于索引查询(但无法还原)。
支付密码 (Pay Password)哈希存储(加盐)- 必须使用强哈希算法(如 bcrypt, scrypt, PBKDF2),并加随机盐(salt)。
- 绝对禁止使用 MD5、SHA-1 等弱哈希或明文存储。
用户密码 (Login Password)哈希存储(加盐)同支付密码,使用 bcrypt 等强哈希算法。
邮箱 (Email)可明文或哈希存储通常可明文存储,但若需隐私保护,也可使用哈希(用于登录验证)或加密。

第八章:支付对账系统设计与实现

8.1 对账文件获取方式(自动下载 vs 手动导出)

方式说明优缺点实现建议
自动下载(推荐)通过支付平台提供的 API 接口,由商户系统定时(如每日凌晨)自动下载对账单文件。优点:高效(无需人工干预,自动化程度高)、及时(可第一时间获取文件,缩短对账周期)、可靠(减少人为失误)。
缺点:需开发和维护下载逻辑、依赖支付平台 API 的稳定性和可用性。
- 微信支付:调用 downloadbill 接口,传入 bill_datebill_type
- 支付宝:调用 alipay.data.dataservice.bill.downloadurl.query 接口获取账单下载地址。
- 实现:使用定时任务(如 Quartz, XXL-JOB)触发下载任务,通过 HTTP 客户端下载文件并存储到服务器或对象存储(如 OSS, S3)。
- 重试机制:网络失败时自动重试(指数退避)。
手动导出登录支付平台的商家后台,手动选择日期并点击”导出”按钮,将文件下载到本地,再上传到商户系统。优点:简单直接,无需开发、适用于初期或对账量小的场景。
缺点:低效(占用人力,易遗漏或延迟)、易出错(可能选错日期或文件类型)、不及时(无法实现自动化处理)。
- 仅作为自动下载的备用方案或临时应急手段。
- 应制定严格的操作流程和交接记录。

最佳实践:

  • 优先采用自动下载,实现对账流程的自动化。
  • 文件存储:下载的对账单应按日期分类存储,并保留至少 6 个月以备查。
  • 完整性校验:下载后校验文件大小或 MD5 值(如果平台提供),确保文件完整。

8.2 微信、支付宝对账单格式解析

微信支付对账单

  • 格式:UTF-8 编码的 CSV 文件。
  • 文件名*_*_*_*.csv(如 1900009191_20251017135400_1599123456.csv
  • 关键字段
    • 交易时间:订单支付成功时间。
    • 公众账号 ID / 商户号:标识商户。
    • 微信订单号:微信侧的唯一订单号。
    • 商户订单号:商户系统提交的 out_trade_no
    • 交易类型:JSAPI(小程序/公众号)、APP、NATIVE(扫码)等。
    • 交易状态:SUCCESS、REFUND、NOTPAY 等。
    • 银行类型:支付银行。
    • 订单金额:订单总金额(单位:分)。
    • 应结订单金额:商户实际应收到的金额(扣除优惠后)。
    • 手续费:微信收取的服务费。
    • 费率:服务费率。
    • 退款金额:该笔交易的退款金额(如果有)。
  • 解析要点
    • 第一行为标题,第二行为数据,最后一行为汇总。
    • 金额字段为整数(分),需转换为元(除以 100)。
    • 处理转义字符(如 ")。

支付宝对账单

  • 格式:UTF-8 编码的 CSV 文件。
  • 文件名*_*_*_*_*.csv(如 2088101111111111_20251017_account.csv
  • 关键字段
    • 业务类型:交易、退款、充值等。
    • 订单号:支付宝交易号(trade_no)。
    • 商家订单号:商户订单号(out_trade_no)。
    • 商品名称。
    • 金额:交易金额(收入为正,支出为负)。
    • 收/支:收入或支出。
    • 交易状态:成功、失败。
    • 服务费:支付宝收取的费用。
    • 服务费率。
    • 交易时间。
    • 备注:可能包含退款原因等。
  • 解析要点
    • 字段间以英文逗号 , 分隔。
    • 字符串字段可能被双引号 " 包围。
    • 金额字段需根据收/支判断正负。
    • 注意区分”交易”和”退款”记录。

解析工具建议:

  • 使用成熟的 CSV 解析库(如 Java 的 OpenCSV、Python 的 csv 模块)。
  • 编写通用的解析器,通过配置映射不同平台的字段。

8.3 对账核心逻辑:交易流水匹配与差异识别

对账的核心是将商户系统交易流水与支付平台对账单流水进行匹配,识别差异。

核心步骤:

  1. 数据准备:

    • 从数据库读取指定日期(T 日)的所有支付成功和退款成功的订单流水。
    • 从对账单文件中解析出 T 日的所有交易和退款记录。
  2. 流水匹配:

    • 主键匹配:最可靠的方式是使用商户订单号(out_trade_no)作为主键进行匹配。在商户系统和对账单中查找 out_trade_no 相同的记录。
    • 辅助匹配:如果 out_trade_no 不一致(如系统问题),可尝试用交易时间 + 金额 + 用户 ID 组合进行模糊匹配(需谨慎,可能误匹配)。
  3. 差异识别:

    • 长款(Long):支付平台有记录,但商户系统无记录。
      • 可能原因:商户系统下单失败但支付成功、漏单、out_trade_no 生成错误。
      • 风险:用户已付款但未发货,需紧急处理。
    • 短款(Short):商户系统有记录,但支付平台无记录。
      • 可能原因:对账单下载失败、支付平台数据延迟、订单未真正支付成功(状态为”待支付”)。
      • 风险:可能已发货但未收款,需核查。
    • 金额差异out_trade_no 相同,但金额不一致。
      • 可能原因:订单修改价格后未正确更新、优惠券计算错误。
    • 状态差异:商户系统状态为”成功”,但对账单状态为”退款”或反之。
      • 可能原因:退款未同步、系统状态更新失败。
  4. 结果输出:生成三张表:

    • 匹配成功表:所有核对无误的记录。
    • 差异表:包含长款、短款、金额差异、状态差异的记录,标记差异类型。
    • 待处理表:需要人工介入核查的记录。

8.4 自动对账任务调度设计(定时任务 + 消息队列)

为实现高可用、解耦和可扩展的对账系统,推荐采用定时任务 + 消息队列的架构。

组件说明:

  • Scheduler(定时任务):如 XXL-JOB、Quartz,每日固定时间(如 01:00)触发对账流程。
  • Producer(生产者):将”下载”、“解析”、“对账”等任务作为消息发布到消息队列。
  • 消息队列:如 Kafka、RabbitMQ,作为任务的缓冲和解耦层。即使下游服务暂时不可用,任务也不会丢失。
  • Consumer(消费者):独立的服务进程,从队列中消费任务并执行。
    • 下载消费者:负责调用 API 下载对账单。
    • 解析消费者:负责解析 CSV 文件,将流水数据存入临时表或缓存。
    • 对账消费者:执行 8.3 节的核心对账逻辑。

优点:

  • 解耦:各环节独立,易于维护和扩展。
  • 异步:提高整体效率,避免阻塞。
  • 可靠:消息队列保证任务不丢失,支持失败重试。
  • 可扩展:可通过增加消费者实例来提高处理能力。

8.5 差错处理流程与人工干预机制

自动对账无法解决所有问题,必须设计完善的差错处理流程。

自动化处理:

  • 轻微差异:如金额差异极小(几分钱),可设置容差阈值,自动标记为”可忽略”。
  • 已知问题:对于已知的系统 Bug 导致的差异,可配置规则自动归类。

人工干预机制:

  • 告警通知:对账完成后,如果存在长款或重大差异,立即通过邮件、短信、钉钉机器人等方式通知财务和运维人员。
  • 差异管理后台
    • 提供 Web 界面,展示所有差异记录。
    • 支持按类型、日期、订单号筛选。
    • 显示商户系统和支付平台的详细记录。
  • 处理流程
    • 核查:财务人员登录支付平台后台,手动核对争议订单的详细信息。
    • 判断:确认为长款(立即联系用户或补发货)、确认为短款(检查订单状态,如已发货需联系支付平台申诉)。
    • 处理:在后台标记差异处理结果(如”已补单”、“已申诉”、“忽略”),记录处理人和处理时间。
  • 申诉通道:对于支付平台记录有误的情况(如用户未付款但平台记为成功),通过支付平台的”差错平台”或客服渠道提交申诉材料。

8.6 对账报表生成与可视化展示

对账结果需要以清晰的报表形式呈现,供管理层和财务部门审阅。

报表类型:

  • 对账汇总日报
    • 统计日期、商户号。
    • 交易总笔数、总金额。
    • 退款总笔数、总金额。
    • 手续费总额。
    • 差异笔数、差异总金额。
    • 对账状态(成功/失败)。
  • 差异明细表
    • 列出所有差异记录。
    • 字段:订单号、交易时间、金额、差异类型、平台记录、商户记录、处理状态、备注。
  • 手续费分析表
    • 按支付方式(微信、支付宝)统计手续费。
    • 按费率档位分析。
  • 趋势分析图(可视化)
    • 折线图:展示每日交易金额、差异金额的趋势。
    • 柱状图:对比不同支付渠道的交易量和手续费。
    • 饼图:展示交易类型(支付、退款)的占比。

实现方式:

  • 使用 BI 工具(如 Superset、Metabase、帆软)连接对账结果数据库。
  • 开发定制化的管理后台,集成报表和图表。
  • 支持报表导出为 Excel 或 PDF。

第九章:异常处理与日志监控

在支付系统中,网络波动、第三方服务不可用、系统故障等异常是常态。建立完善的异常处理与监控体系,是保障系统稳定、资金安全和用户体验的关键。

9.1 支付超时、失败、重复通知的处理策略

异常类型处理策略详细说明
支付超时主动查询 + 幂等设计- 前端超时:用户点击支付后长时间无响应,前端应提示”支付结果未知,请勿重复支付”,并引导用户到”订单详情”页查看状态。
- 后端调用超时:调用支付平台 API 时网络超时,不能简单认为失败。必须:
1. 记录日志并告警。
2. 立即调用查询接口(query)确认订单最终状态。
3. 根据查询结果更新本地订单状态(成功则发货,失败则解锁库存)。
- 核心原则:“宁可重复查,不可盲目重试”,避免因重试导致重复扣款。
支付失败明确原因 + 友好提示- 解析失败原因:根据支付平台返回的 sub_codesub_msg(如 ACQ.BUYER_BALANCE_NOT_ENOUGH)判断具体原因。
- 分类处理:
  - 用户侧问题(余额不足、银行卡限额):提示用户更换支付方式或充值。
  - 商户侧问题(参数错误、权限不足):记录详细日志,开发介入修复。
  - 平台侧问题(系统异常):提示”系统繁忙,请稍后重试”,并自动重试(见 9.2)。
- 状态更新:将订单状态更新为”支付失败”,解锁库存。
重复通知幂等性 + 状态机支付平台(尤其是微信、支付宝)的异步通知(notify_url)可能因网络问题重复发送(最多 5 次)。
- 幂等处理:
1. 收到通知后,首先验证签名,确保来源可信。
2. 根据通知中的 out_trade_no 查询本地订单的当前状态。
3. 如果订单状态已是”已支付”,则直接返回 success,不再处理。
4. 如果订单状态是”待支付”,则执行支付成功逻辑(更新状态、发货等),并返回 success
- 状态机设计:订单状态流转必须是单向的(如 待支付 → 已支付 → 已退款),避免状态回滚导致资金风险。

9.2 分布式事务与最终一致性保障

支付通常涉及多个服务(订单服务、库存服务、账户服务、消息服务),在微服务架构下,需保障跨服务操作的最终一致性。

常用方案:

方案原理适用场景优缺点
基于消息队列的最终一致性1. A 服务(订单)在本地事务中完成操作并发送”支付成功”消息到 MQ。
2. B 服务(库存)消费消息,扣减库存。若失败,MQ 会重试。
3. 通过”事务消息”确保 A 服务的操作与消息发送的原子性。
最常用于支付成功后的业务处理(如扣库存、发券)。优点:性能高,解耦。
缺点:存在短暂不一致,需处理消息重复消费。
TCC(Try-Confirm-Cancel)1. Try:锁定资源(如冻结库存、预占额度)。
2. Confirm:确认操作(扣减库存、扣款)。
3. Cancel:取消操作(释放冻结的资源)。
适用于对一致性要求高、资源需要预占的场景(如秒杀)。优点:强一致性保证。
缺点:实现复杂,需业务逻辑拆分,存在”悬挂”和”空回滚”风险。
Saga 模式将长事务拆分为一系列本地事务。每个事务有对应的补偿事务。正向流程:创建订单 → 扣库存 → 调用支付。补偿流程:取消订单 ← 回滚库存 ←(支付失败)。适用于流程长、涉及服务多的业务。优点:避免长时间锁资源。
缺点:补偿逻辑复杂,不保证隔离性(中间状态可见)。

支付场景实践:

  • 下单:使用 TCC 或 Saga,确保订单创建和库存预占的原子性。
  • 支付回调:采用”本地事务 + 消息队列”。
    • 在支付成功回调中,开启数据库事务:
    • 更新订单状态为”已支付”。
    • 向 MQ 发送”支付成功”事件。
    • 事务提交后,MQ 将事件投递给库存、积分、物流等服务。
    • 各服务消费事件,完成后续操作。失败则重试。
  • 补偿机制:对于关键操作(如发货),需有对账系统兜底,发现”已支付未发货”时触发补偿。

9.3 关键操作日志记录规范

日志是排查问题、审计追溯的唯一依据。必须规范记录关键操作。

记录原则:

  • 完整性:包含时间、操作、用户、上下文、结果。
  • 可追溯性:能通过日志还原操作过程。
  • 安全性:敏感信息必须脱敏。
  • 结构化:使用 JSON 格式,便于搜索和分析。

关键操作日志示例:

// 1. 支付请求日志
{
  "timestamp": "2025-10-17T14:05:00.123Z",
  "level": "INFO",
  "thread": "http-nio-8080-exec-5",
  "logger": "PaymentService",
  "message": "发起微信支付请求",
  "traceId": "a1b2c3d4-5678-90ef-ghij-klmnopqrstuv",
  "spanId": "wxyz1234",
  "userId": "U10001",
  "orderId": "ORD202510171404001",
  "outTradeNo": "WXP202510171404001",
  "amount": 9990,
  "subject": "商品A",
  "clientIp": "192.168.1.100",
  "userAgent": "Mozilla/5.0...",
  "paymentMethod": "JSAPI"
}

// 2. 支付回调日志
{
  "timestamp": "2025-10-17T14:06:30.456Z",
  "level": "INFO",
  "logger": "NotifyController",
  "message": "收到微信支付异步通知",
  "traceId": "a1b2c3d4-5678-90ef-ghij-klmnopqrstuv",
  "outTradeNo": "WXP202510171404001",
  "transactionId": "1234567890",
  "totalFee": 9990,
  "cashFee": 9990,
  "bankType": "CFT",
  "timeEnd": "20251017140625",
  "notifyId": "N20251017140630"
}

// 3. 支付成功处理日志
{
  "timestamp": "2025-10-17T14:06:31.000Z",
  "level": "INFO",
  "logger": "OrderService",
  "message": "支付成功,更新订单状态并发送消息",
  "traceId": "a1b2c3d4-5678-90ef-ghij-klmnopqrstuv",
  "orderId": "ORD202510171404001",
  "statusBefore": "待支付",
  "statusAfter": "已支付",
  "eventSent": "PAYMENT_SUCCESS"
}

// 4. 异常日志
{
  "timestamp": "2025-10-17T14:07:15.789Z",
  "level": "ERROR",
  "logger": "AlipayService",
  "message": "调用支付宝查询接口失败",
  "traceId": "e5f6g7h8-9012-34ij-klmn-opqrstuvwxy",
  "outTradeNo": "ALI202510171407001",
  "error": "AlipayApiException",
  "subCode": "SYSTEM_ERROR",
  "subMsg": "系统繁忙",
  "stackTrace": "..."
}

日志管理:

  • 使用 ELK(Elasticsearch, Logstash, Kibana)或 Loki 等日志系统集中收集和分析。
  • 设置日志保留策略(如 180 天)。

9.4 监控告警机制(钉钉、邮件、短信)

实时监控系统状态,异常时及时通知相关人员。

监控维度:

监控类型指标告警方式触发条件
应用性能 (APM)- 接口平均耗时 > 1s
- 错误率 > 1%
- JVM 内存使用率 > 80%
钉钉群机器人、邮件持续 5 分钟超过阈值
业务指标- 支付成功率 < 95%
- 退款率 > 5%
- 对账差异金额 > 1000 元
钉钉、短信(严重时)单日或单小时统计
基础设施- 服务器 CPU > 80%
- 磁盘空间 < 20%
- 网络延迟高
邮件、短信实时触发
第三方服务- 调用微信/支付宝 API 失败率 > 10%
- 对账单下载失败
钉钉、邮件连续失败 3 次
定时任务- 对账任务未完成
- 报表生成失败
钉钉、邮件任务超时或执行失败

实现工具:

  • Prometheus + Grafana + Alertmanager:开源监控告警组合。
  • Zabbix:传统企业级监控。
  • 商业 APM:如阿里云 ARMS、腾讯云 APM。
  • 自研告警服务:集成钉钉 Webhook、邮件服务、短信网关。

9.5 核心接口健康检查方案

确保核心支付接口的可用性,是系统稳定的第一道防线。

检查方式:

  • 主动探测(Active Probing):
    • HTTP Ping:定时(如每 30 秒)访问 /health/actuator/health 接口,检查返回 200 和 {"status": "UP"}
    • 业务探活:模拟真实业务调用,如:
      • 调用 query 接口查询一个已知存在的测试订单。
      • 调用 refund 接口发起一笔小额退款(需在测试环境)。
    • 依赖检查:健康检查接口内部检查数据库、Redis、MQ 等关键依赖的连接状态。
  • 被动监控:通过 APM 工具监控接口的实时 QPS、延迟、错误率。

健康检查接口示例(Spring Boot Actuator):

// GET /actuator/health
{
  "status": "UP",
  "components": {
    "db": {
      "status": "UP",
      "details": {
        "database": "MySQL",
        "validationQuery": "isValid()"
      }
    },
    "redis": {
      "status": "UP",
      "details": {
        "connectedClients": 10,
        "usedMemory": "1024KB"
      }
    },
    "payService": {
      "status": "UP",
      "details": {
        "wechat": "AVAILABLE",
        "alipay": "AVAILABLE"
      }
    }
  }
}

高可用设计:

  • 负载均衡:在 Nginx/LVS 层配置健康检查,自动剔除异常节点。
  • 多活部署:核心服务在多可用区部署,避免单点故障。
  • 熔断降级:使用 Hystrix 或 Sentinel,在第三方支付接口不可用时,降级为”暂不支持该支付方式”,避免雪崩。

第十章:高可用与性能优化

支付系统作为核心业务系统,必须具备高可用性(99.99%+)和高性能(低延迟、高吞吐)。本章将探讨关键的高可用与性能优化策略。

10.1 支付网关层设计(路由、降级、熔断)

支付网关是所有支付请求的统一入口,承担着流量管控、协议转换、安全校验和容错处理的重任。

核心功能设计:

功能说明实现方式
统一接入对接多种支付方式(微信、支付宝、银联、Apple Pay 等),对外提供统一的 API。- 定义标准化的请求/响应格式。
- 网关负责将标准请求转换为各支付平台的特定协议。
智能路由根据策略将请求路由到最优的支付服务商。- 权重路由:按预设权重分配流量(如微信 60%,支付宝 40%)。
- 性能路由:根据各服务商的实时响应时间、成功率动态调整路由。
- 地域路由:根据用户 IP 地域选择最优服务商(如支付宝在华东地区更快)。
- 成本路由:优先选择费率更低的渠道。
降级(Degradation)当某个服务不可用时,自动切换到备用方案,保证核心功能可用。- 服务降级:微信支付不可用时,自动引导用户使用支付宝或银联。
- 功能降级:在极端情况下,关闭非核心功能(如优惠券、分期),保证基础支付流程畅通。
- 返回兜底数据:查询接口失败时,返回缓存中的历史数据或默认值。
熔断(Circuit Breaker)防止一个服务的故障蔓延到整个系统。- 使用 Hystrix 或 Sentinel 等框架。
- 配置规则:当对某个支付服务商的调用错误率 > 50% 或超时次数 > 10 次/分钟时,触发熔断。
- 熔断后,所有对该服务商的请求直接失败,不再发起调用,等待一段时间后自动半开(尝试放行少量请求)进行恢复探测。
限流(Rate Limiting)防止突发流量压垮系统。- 基于 IP、用户 ID、App ID 进行限流。
- 使用令牌桶或漏桶算法。
- 结合 Redis 实现分布式限流。

10.2 多服务商容灾切换策略

依赖单一支付服务商存在巨大风险(如服务商故障、费率调整、政策限制)。多服务商接入是实现高可用的必要手段。

容灾切换策略:

策略说明实施要点
主备模式设置一个主服务商和一个或多个备用服务商。主服务商故障时,流量全部切到备用。- 简单易实现。
- 备用服务商可能因长期不用而出现问题(如证书过期、接口变更)。
- 切换时流量突增可能导致备用服务商压力过大。
多活模式(推荐)多个服务商同时在线,按策略分担流量。任一服务商故障,其流量自动分配给其他正常服务商。- 高可用性:无单点故障。
- 负载均衡:充分利用各服务商的资源。
- 实现:通过网关的智能路由实现。需维护各服务商的健康状态。
灰度切换新接入或切换服务商时,先对小部分用户开放,验证稳定后再全量。- 降低切换风险。
- 可对比不同服务商的性能、成功率、用户体验。

切换触发条件:

  • 自动触发:支付服务商 API 连续超时或错误、熔断器被触发、对账系统发现该服务商有大量长款/短款。
  • 手动触发:运维人员通过管理后台手动切换、接到服务商维护通知。

关键保障:

  • 统一抽象层:定义统一的 PaymentService 接口,各服务商实现该接口,便于切换。
  • 状态同步:确保订单状态在不同服务商间能正确同步(如通过异步通知)。
  • 对账统一:无论使用哪个服务商,对账逻辑应能兼容所有渠道。

10.3 接口幂等性设计实践

幂等性指同一操作发起一次或多次请求,对系统产生的业务影响是相同的。在支付场景中至关重要,防止重复扣款。

常见场景:

  • 用户重复点击支付按钮。
  • 网络超时,客户端重试。
  • 支付平台重复发送异步通知。

实现方案:

方案原理适用场景注意事项
数据库唯一索引利用数据库的唯一约束防止重复插入。最常用、最可靠。- 在订单表创建 out_trade_no 字段的唯一索引。
- 插入订单时,如果 out_trade_no 已存在,则抛出唯一键冲突异常,返回”订单已存在”。
分布式锁(Redis)在执行关键操作前,先获取一个基于业务键的锁。适用于非数据库操作或复杂流程。- 使用 SET key value NX EX seconds 命令。
- key 为 lock:pay:{out_trade_no}
- 注意锁的过期时间和释放,避免死锁。
Token 机制(防重 Token)1. 用户进入支付页时,服务端生成一个一次性 Token 并返回前端。
2. 提交支付时,前端携带此 Token。
3. 服务端校验 Token 有效性,校验通过后立即将其删除或标记为已使用。
有效防止前端重复提交。- Token 可存储在 Redis,设置较短过期时间(如 5 分钟)。
- 需处理 Token 获取失败的情况。
状态机 + 条件更新更新订单状态时,使用条件语句确保状态流转的合法性。配合其他方案使用,增强安全性。sql\nUPDATE orders SET status = 'paid', paid_time = NOW()\nWHERE order_id = ? AND status = 'unpaid';\n
如果返回影响行数为 0,说明订单已支付或状态异常。

最佳实践:

  • 组合使用:通常采用”唯一索引 + 状态机”作为核心保障,“防重 Token”作为前端防护。
  • 幂等范围:明确哪些接口需要幂等(如 createOrder, refund),并在接口文档中注明。

10.4 缓存机制在支付中的应用(如预下单缓存)

合理使用缓存可以显著提升性能,降低数据库和第三方服务的压力。

应用场景:

场景缓存策略说明
预下单结果缓存- 用户点击”去支付”后,服务端调用支付平台 unifiedorder 接口生成预支付交易会话标识(如 prepay_id)。
- 将 prepay_id、支付链接等结果缓存到 Redis,key=prepay:{out_trade_no},过期时间=2 小时。
- 前端再次请求时,直接返回缓存结果,避免重复调用支付平台 API。
- 支付平台 API 有调用频率限制。
- 缓存可减少网络开销和支付平台压力。
- 注意:需确保 out_trade_no 的唯一性,避免缓存污染。
支付配置缓存将支付渠道的开关、费率、权重等配置信息缓存到 Redis 或本地缓存(如 Caffeine)。- 避免每次支付都查询数据库。
- 配置变更时,通过消息队列通知所有节点更新缓存。
商户信息缓存缓存商户的公钥、证书序列号、API 密钥等。- 减少数据库查询。
- 提高签名/验签速度。
对账单缓存将解析后的对账单流水缓存,供对账任务使用。- 避免重复解析大文件。
- 加速对账过程。

缓存注意事项:

  • 一致性:缓存与数据库数据需保持一致,设置合理的过期时间或通过消息机制主动失效。
  • 雪崩:避免大量缓存同时过期,可设置随机过期时间。
  • 穿透:查询不存在的数据时,可缓存一个空值(null)并设置较短过期时间。
  • 击穿:热点 key 失效时,用互斥锁(Mutex Lock)重建缓存。

10.5 并发支付请求处理优化

在秒杀、促销等高并发场景下,需优化系统以应对瞬时流量洪峰。

优化策略:

策略说明实现方式
异步化将非核心、耗时操作异步执行。- 支付成功后,发送通知、更新积分、记录日志等操作通过消息队列异步处理。
- 提高主支付流程的响应速度。
批量处理合并多个小操作为批量操作,减少 I/O 次数。- 对账任务中,将数据库更新操作批量提交。
- 向消息队列批量发送消息。
数据库优化提升数据库在高并发下的性能。- 分库分表:按用户 ID 或订单 ID 对订单表进行水平拆分。
- 读写分离:写请求走主库,查询请求走从库。
- 索引优化:为 out_trade_no, user_id, status 等常用查询字段建立复合索引。
连接池优化合理配置数据库和 HTTP 客户端连接池。- HikariCP:配置合适的最小/最大连接数、超时时间。
- HTTP Client:复用连接,避免频繁创建销毁。
无锁设计减少锁竞争。- 使用 Redis INCR 原子操作生成分布式自增 ID。
- 利用数据库的乐观锁(version 字段)更新订单状态。
资源隔离避免不同业务相互影响。- 为支付核心链路分配独立的服务器、数据库实例、MQ 集群。
- 使用线程池隔离不同类型的请求。

性能测试:

  • 上线前必须进行压力测试(如使用 JMeter、Gatling),模拟高并发支付场景。
  • 监控关键指标:TPS(每秒事务数)、平均响应时间、错误率、系统资源(CPU、内存、IO)。

第十一章:实战项目:搭建一个可扩展的支付中台

支付中台的目标是将复杂的支付能力进行抽象、整合和标准化,为多个业务系统(如电商、零售、SaaS 平台)提供统一、稳定、可扩展的支付服务,避免重复建设和维护。

11.1 架构设计:统一接入层 + 路由中心 + 安全中心

采用分层架构,实现高内聚、低耦合。

核心组件说明:

  • 统一接入层(Unified API Gateway):

    • 功能:所有业务系统的支付请求统一入口。
    • 职责:
      • 协议转换(HTTP/HTTPS, JSON/XML)。
      • 请求路由到内部服务。
      • 基础安全校验(IP 白名单、基础签名)。
      • 限流、熔断、降级。
      • 统一的错误码和响应格式。
    • 技术选型:Spring Cloud Gateway, Nginx + Lua, Kong。
  • 路由中心(Routing Center):

    • 功能:根据业务规则决定使用哪个支付服务商。
    • 职责:
      • 接收来自接入层的标准化支付请求。
      • 查询配置中心获取当前路由策略。
      • 结合服务商健康状态(来自健康检查)、性能指标(延迟、成功率)、成本、用户偏好等,选择最优的支付适配器。
      • 调用选定的适配器执行支付。
    • 关键:实现灵活的路由决策引擎。
  • 安全中心(Security Center):

    • 功能:集中处理所有与安全相关的逻辑。
    • 职责:
      • 签名/验签:为请求到支付平台的数据生成签名,验证来自支付平台的异步通知。
      • 敏感信息加解密:使用 KMS 管理密钥,对银行卡号、Token 等进行加密存储和传输。
      • 防重放攻击:验证请求中的 timestampnonce
      • IP 白名单校验:验证异步通知来源。
    • 优势:将安全逻辑从各业务和适配器中剥离,统一管理,降低风险。
  • 配置管理中心(见 11.3):独立模块,管理所有动态配置。

11.2 抽象支付接口与多服务商适配器模式

采用适配器模式(Adapter Pattern)和策略模式(Strategy Pattern),实现对多服务商的灵活扩展。

核心抽象:

// 1. 定义统一的支付服务接口
public interface PaymentService {
    // 创建支付订单(预下单)
    PayResponse createOrder(PayRequest request) throws PaymentException;

    // 查询订单状态
    QueryResponse queryOrder(QueryRequest request) throws PaymentException;

    // 申请退款
    RefundResponse refund(RefundRequest request) throws PaymentException;

    // 处理异步通知
    NotifyResponse handleNotify(Map<String, String> params) throws PaymentException;
}

// 2. 标准化的请求/响应对象
@Data
public class PayRequest {
    private String outTradeNo;     // 商户订单号
    private BigDecimal amount;     // 金额
    private String subject;        // 商品标题
    private String payMethod;      // 支付方式 (WECHAT, ALIPAY, UNIONPAY)
    private String clientIp;       // 客户端IP
    // ... 其他通用字段
}

@Data
public class PayResponse {
    private String code;           // 业务码 (SUCCESS, FAILED)
    private String msg;            // 描述
    private String tradeNo;        // 支付平台交易号
    private Map<String, String> payInfo; // 支付所需信息 (如 prepay_id, 支付链接)
}

// 3. 具体的适配器实现
@Component
@Primary
public class WeChatPaymentAdapter implements PaymentService {
    @Override
    public PayResponse createOrder(PayRequest request) {
        // 1. 转换 PayRequest 为微信支付所需的参数
        Map<String, String> wxParams = convertToWxParams(request);
        // 2. 调用微信统一下单API
        Map<String, String> wxResponse = wxApi.unifiedOrder(wxParams);
        // 3. 验签
        if (!securityCenter.verifySignature(wxResponse, "wechat")) {
            throw new PaymentException("微信签名验证失败");
        }
        // 4. 转换微信响应为 PayResponse
        return convertWxResponseToPayResponse(wxResponse);
    }

    @Override
    public NotifyResponse handleNotify(Map<String, String> params) {
        // 1. 验签
        if (!securityCenter.verifySignature(params, "wechat")) {
            return NotifyResponse.fail("签名失败");
        }
        // 2. 解析通知
        // 3. 更新订单状态 (通过事件或直接调用)
        return NotifyResponse.success();
    }
    // ... 实现其他方法
}

@Component
public class AliPayPaymentAdapter implements PaymentService {
    // 实现支付宝的 createOrder, query, refund, handleNotify
    // 逻辑与微信类似,但调用支付宝API
}

优势:

  • 可扩展性:新增支付服务商(如 PayPal、Stripe)时,只需实现 PaymentService 接口并注册为 Spring Bean,无需修改核心逻辑。
  • 可维护性:各服务商的实现相互隔离,便于维护和升级。
  • 一致性:对外提供统一的 API,业务系统无需关心底层差异。

11.3 配置管理中心设计

配置管理中心是支付中台实现动态化、可运营的核心。

管理内容:

配置类型说明动态性
服务商开关是否启用某个支付方式(微信、支付宝)。高(可实时开关)
路由策略各服务商的权重、优先级、成本系数。中(可动态调整)
费率配置不同渠道、不同金额区间的费率。低(通常按合同约定)
API 密钥/证书各服务商的 app_id, api_key, 证书路径和密码。低(变更需谨慎)
回调地址notify_url, return_url 的模板或固定值。
熔断降级规则触发熔断的错误率阈值、降级目标。
对账配置对账文件下载时间、路径、解析规则。

技术实现:

  • 配置中心:使用 Nacos、Apollo 或 Consul。
  • 动态生效:当配置变更时,配置中心推送事件到支付中台,中台实时更新内存中的配置。
  • 版本管理:支持配置的历史版本和回滚。
  • 权限控制:不同角色(开发、运维、财务)拥有不同的配置修改权限。
  • 灰度发布:新配置可先对部分业务系统或用户生效。

示例(Nacos 配置):

# dataId: payment-service-router.yaml
router:
  strategy: weighted   # 路由策略: weighted, performance, cost
  weights:
    wechat: 60
    alipay: 40
    unionpay: 0        # 当前关闭银联

# dataId: payment-service-wechat.yaml
wechat:
  enabled: true
  app_id: wx1234567890
  mch_id: 1900000001
  api_key: your_api_key_here
  cert_path: /certs/wechat.p12
  notify_url: https://api.yourdomain.com/pay/notify/wechat

11.4 提供内部 API 供业务系统调用

支付中台通过 RESTful API 或 RPC(如 gRPC, Dubbo)向业务系统暴露能力。

核心 API 设计(RESTful 示例):

# 1. 创建支付订单
POST /api/v1/payments
{
  "out_trade_no": "ORD202510171420001",
  "amount": 99.90,
  "subject": "iPhone 15",
  "pay_method": "WECHAT_JSAPI",  # 指定支付方式,或留空由路由中心决定
  "client_ip": "192.168.1.100",
  "openid": "oABC123..."         # JSAPI 支付需要
}

# 成功响应
HTTP 200
{
  "code": "SUCCESS",
  "msg": "OK",
  "data": {
    "trade_no": "420000000012345678",
    "pay_info": {
      "appId": "wx1234567890",
      "timeStamp": "1634456789",
      "nonceStr": "aB3kL9mN2pQx",
      "package": "prepay_id=wx1234567890",
      "signType": "RSA",
      "paySign": "..."
    }
  }
}

# 2. 查询订单
GET /api/v1/payments/{out_trade_no}

# 响应
{
  "code": "SUCCESS",
  "data": {
    "outTradeNo": "ORD202510171420001",
    "tradeNo": "420000000012345678",
    "amount": 99.90,
    "status": "SUCCESS",  # PAID, REFUNDED, CLOSED, FAILED
    "payTime": "2025-10-17T14:21:30Z"
  }
}

# 3. 申请退款
POST /api/v1/refunds
{
  "out_trade_no": "ORD202510171420001",
  "out_refund_no": "RFD202510171430001",
  "refund_amount": 99.90,
  "reason": "用户取消订单"
}

API 管理:

  • API 文档:使用 Swagger(OpenAPI)自动生成和发布文档。
  • 认证授权:使用 API Key + Secret 或 OAuth 2.0 进行调用方身份认证。
  • 版本控制:通过 URL 路径(/v1/, /v2/)或 Header 进行版本管理。
  • 流量控制:对接入的业务系统进行 QPS 限制。

11.5 日志追踪与链路监控集成

在分布式架构下,一次支付请求可能经过网关、路由、适配器等多个服务,必须实现全链路追踪。

集成方案:

  • 分布式追踪(Distributed Tracing):

    • 技术:集成 Sleuth + Zipkin 或 SkyWalking。
    • 实现:
      • 在统一接入层生成唯一的 traceId,并注入到请求头中。
      • 后续所有内部服务(路由中心、适配器)在日志和调用中传递 traceIdspanId
      • 将追踪数据上报到 Zipkin 或 SkyWalking Server。
      • 价值:可在 UI 中查看一次支付请求的完整调用链路、各环节耗时、异常点。
  • 统一日志收集:

    • 技术:ELK Stack(Elasticsearch, Logstash, Kibana)或 EFK(Fluentd)。
    • 实现:
      • 所有服务将结构化日志(JSON 格式)发送到 Kafka 或直接到 Logstash。
      • Logstash 进行过滤和解析后存入 Elasticsearch。
      • 通过 Kibana 进行搜索、分析和可视化。
      • 价值:基于 traceId 快速定位问题,分析错误日志。
  • 监控告警(见第九章):

    • 将支付中台的关键指标(TPS、成功率、延迟、错误率)接入 Prometheus。
    • 使用 Grafana 展示监控大盘。
    • 通过 Alertmanager 配置告警规则,通知到钉钉、邮件。

最终效果:

  • 运维:通过 Grafana 大盘实时监控系统健康状况。
  • 开发:通过 Kibana 和 Zipkin 快速定位线上问题,排查性能瓶颈。
  • 财务:通过日志和监控数据,确保每一笔交易都有迹可循。