支付宝支付平台如何对接本市多家银行,接入是否收费?
明确结论
这个场景原则上可以实现,但仅接入支付宝支付接口,无法满足全部需求。整个系统至少包含三部分:
- 支付宝线上收款:通过支付宝开放平台完成支付、查询、退款和异步通知等操作。
- 银行数据交互和票据打印:通常要分别接入各家银行的银企直连、柜面系统或当地统一缴款平台。支付宝不能代替银行打印专有格式的票据。
- 资金流向和清分结算:需要收单机构、开户银行和相关职能部门共同确认。业务系统不能通过修改接口参数,随意改变资金去向。
费用方面没有统一标准。支付宝支付服务一般根据签约产品、商户类型、交易金额和结算方案收取服务费。银行接口、专线、数字证书、账户管理和定制开发也可能收费,具体以支付宝、银行和系统服务商签订的合同及报价为准。
为什么需要分别对接
支付宝主要负责支付受理和交易处理。付款人完成支付宝付款后,平台会向业务系统返回交易状态、支付宝交易号等信息,但这并不意味着银行专用票据已经打印,也不代表多家银行之间的数据交换已经完成。
银行打印专有格式票据时,通常需要交款单号、缴款人、金额、项目编码、执收单位、财政或业务分类等字段。字段格式、传输方式和打印规则由银行或当地职能部门确定,常见接入渠道包括:
- 银企直连接口;
- 银行开放平台;
- 政务缴费或非税收入统一平台;
- SFTP 文件交换;
- 专线报文;
- 银行柜面系统的定制接口。
如果业务属于财政非税、公共服务收费或其他受监管场景,还可能涉及缴款码、执收项目编码、财政专户、电子票据和对账规范。遇到这类业务,应先确认当地主管部门是否已经建成统一支付或票据平台,以免重复对接多家银行。
推荐的系统架构
可以使用统一支付网关,将支付宝、银行和业务系统隔离开:
业务系统
│
├── 创建交款单
│
统一支付网关
├── 支付宝:下单、查询、退款、异步通知
├── 银行A:交款单传输、票据状态查询
├── 银行B:交款单传输、票据状态查询
└── 对账与清分模块
│
├── 交易流水
├── 银行回单
├── 支付宝账单
└── 票据状态
支付订单、银行交款单和票据需要分别保存编号,不能混用:
out_trade_no:商户支付订单号;- 支付宝交易号:支付宝返回的交易流水号;
payment_notice_no:业务交款单号;bank_receipt_no:银行回单或票据编号。
建立这些编号之间的关联后,系统才能完整追踪支付、入账、票据和退款流程。
具体接入步骤
1. 先确认业务和资金模型
开发前,职能部门、财务部门和银行应共同书面确认以下事项:
- 收款主体是谁;
- 支付宝商户号由谁申请;
- 资金结算到普通账户、专户还是监管账户;
- 一笔资金是否需要分账;
- 是否允许跨主体结算;
- 票据由银行打印,还是由财政电子票据系统开具;
- 退款由谁审核和发起;
- 每家银行负责哪些业务,以及采用什么路由规则。
“按照职能部门要求设置资金流向”需要落实为经过审核的账户、商户、分账关系或支付路由配置。涉及不同法人主体时,不能通过普通支付参数自行划转资金。
2. 申请支付宝商户接入
通常需要完成商户签约、应用创建、密钥或证书配置、产品开通和回调地址配置。根据实际使用场景选择相应产品,例如:
- 电脑网站支付:
alipay.trade.page.pay - 手机网站支付:
alipay.trade.wap.pay - 当面付扫码:
alipay.trade.precreate - 交易查询:
alipay.trade.query - 退款:
alipay.trade.refund
以上是支付宝开放平台常见的接口名称。实际可用接口、准入条件和请求参数,要以签约时的官方文档及控制台配置为准。
3. 分别确认银行接口
需要向每家银行索取正式接口规范和测试条件,至少确认以下内容:
- 通信协议和网络接入方式;
- 报文格式及字段字典;
- 签名、加密和数字证书要求;
- 交款单受理及撤销流程;
- 重复报文处理规则;
- 票据打印条件和打印状态查询方式;
- 日终对账文件格式;
- 测试环境、生产环境及上线验收流程。
如果各家银行的报文差异较大,可以在支付网关内部为每家银行开发独立适配器,业务系统只需调用统一接口。
4. 建立支付与票据状态机
状态机至少可以包含以下状态:
CREATED
PAYING
PAID
BANK_ACCEPTED
TICKET_PRINTED
REFUNDING
REFUNDED
CLOSED
收到支付宝“支付成功”通知时,只能把订单更新为 PAID。银行明确接收交款单后,才能更新为 BANK_ACCEPTED。收到银行票据编号或打印结果后,才能更新为 TICKET_PRINTED。
5. 完成联调、对账和异常补偿
上线前需要覆盖以下场景:
- 支付成功,但业务系统没有收到通知;
- 同一通知被重复发送;
- 支付成功,但银行暂时不可用;
- 银行已经接收交款单,但票据打印失败;
- 交款单金额与支付金额不一致;
- 跨日到账;
- 原路退款后票据作废;
- 支付宝账单、银行到账和业务订单三方不一致。
支付接口示例
下面是简化后的服务端处理逻辑,只用于说明各部分的职责,不代表任何银行或支付宝产品的完整报文。正式开发时应使用官方 SDK,并以当前签约产品的文档为准。
public PaymentResult createPayment(PaymentOrder order) {
// 1. 服务端重新读取订单,不能直接信任前端金额
PaymentOrder stored = orderRepository.findByOrderNo(order.getOrderNo());
if (stored == null || stored.getStatus() != OrderStatus.CREATED) {
throw new IllegalStateException("订单不存在或状态不允许支付");
}
// 2. 创建支付宝支付请求
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setNotifyUrl("https://example.com/api/alipay/notify");
request.setReturnUrl("https://example.com/payment/result");
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo(stored.getOrderNo());
model.setTotalAmount(stored.getAmount().toPlainString());
model.setSubject(stored.getSubject());
model.setProductCode("FAST_INSTANT_TRADE_PAY");
request.setBizModel(model);
// 3. alipayClient 应在应用启动时完成密钥或证书配置
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
"支付下单失败:" + response.getSubCode() + " " + response.getSubMsg()
);
}
return new PaymentResult(response.getBody());
}
处理异步通知时,必须先验证签名,再根据商户订单号查询本地订单:
@PostMapping("/api/alipay/notify")
public String handleNotify(HttpServletRequest request) {
Map<String, String> params = extractParameters(request);
boolean signatureValid = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!signatureValid) {
return "failure";
}
PaymentOrder order = orderRepository.findByOrderNo(
params.get("out_trade_no")
);
if (order == null) {
return "failure";
}
// 同时校验商户身份、应用身份、金额和币种等关键数据
if (!order.getAmount().toPlainString().equals(params.get("total_amount"))) {
return "failure";
}
if ("TRADE_SUCCESS".equals(params.get("trade_status"))) {
// 使用唯一索引或事务锁保证幂等
paymentService.markPaid(
order.getOrderNo(),
params.get("trade_no")
);
// 建议写入可靠消息或任务表,再异步向银行发送交款单
bankDeliveryService.enqueue(order.getOrderNo());
}
return "success";
}
向不同银行传输交款单时,可以定义统一接口:
public interface BankPaymentAdapter {
BankAcceptResult submitPaymentNotice(PaymentNotice notice);
TicketStatus queryTicket(String paymentNoticeNo);
}
每家银行的适配器负责转换对应报文:
public class BankAAdapter implements BankPaymentAdapter {
@Override
public BankAcceptResult submitPaymentNotice(PaymentNotice notice) {
BankARequest request = new BankARequest();
request.setNoticeNo(notice.getNoticeNo());
request.setPayerName(notice.getPayerName());
request.setAmount(notice.getAmount());
request.setItemCode(notice.getItemCode());
// 签名、加密、发送和验签方式以银行正式规范为准
return bankAClient.submit(request);
}
@Override
public TicketStatus queryTicket(String paymentNoticeNo) {
return bankAClient.queryTicket(paymentNoticeNo);
}
}
资金路由如何处理
资金路由通常有三种处理方式:
- 不同业务使用不同支付宝商户或收款账户:配置相对简单,但需要管理更多商户和账户。
- 先收款,再通过合规产品分账:需要开通相应的分账能力,接收方、分账比例和额度都受产品规则限制。
- 由当地统一缴费平台清分:适用于多部门、多银行或财政类业务。支付系统负责受理交易,统一平台负责清分和对账。
普通支付接口不能接收任意银行卡号或银行账户作为参数,并要求支付宝将资金直接结算到该账户。结算账户变更、分账和多主体收款通常都需要经过签约、审核和风控配置。
是否会产生费用
可能产生的费用包括:
- 支付宝交易服务费;
- 特定产品或增值能力费用;
- 银行银企直连、专线或账户服务费;
- CA 证书、密码设备或签名验签设备费用;
- 银行定制接口及联调费用;
- 票据系统或财政平台接入费用;
- 系统开发、运维、对账和安全测评费用。
具体费率不能只根据接口名称判断。同一种支付产品,也可能因为行业、商户资质、交易类型、活动政策和合作模式不同而采用不同费率。商务签约阶段应取得书面报价。
需要特别注意的事项
- 收到支付成功通知后,必须验签,并核对订单号、金额、商户身份和应用身份。
- 异步通知可能重复发送,所有状态更新都要支持幂等处理。
- 应用私钥不能放在前端、移动端或公开代码仓库中。
- 银行交款单传输失败时,应将任务放入重试队列,不能因此回滚已经成功的支付。
- 票据开具、作废和退款之间需要建立对应关系,避免退款后票据仍然有效。
- 处理姓名、证件号、账号等信息时,应尽量减少采集范围,并采用加密传输和访问审计。
- 对账不能只依靠实时回调。系统应定期下载交易账单,并与银行到账记录和业务订单核对。
- 如果业务涉及非税收入、财政票据或其他专户,应先取得主管部门确认,再确定技术方案。
比较稳妥的实施顺序是:先由主管部门明确账户、票据和资金清分规则,再与支付宝签约线上支付产品,同时向各家银行取得接口规范,最后通过统一支付网关处理支付、交款单传输、票据状态和三方对账。
备注:内容仅供参考。
