服务商支付模式sdk与普通商户支付sdk是否相同?
结论
服务商支付模式和普通商户支付模式通常可以共用一套前端、后端 SDK,无需因模式切换重新安装或完整重做 SDK 集成。
需要调整的主要是后端,包括身份配置、下单接口参数、签名和证书配置、回调处理及账务归属。前端是否需要修改,取决于支付平台是否改变了调起支付所需的参数结构。多数情况下,前端仍然只使用后端返回的支付参数,改动不大。
不同支付平台、支付产品和 SDK 版本可能有不同要求,具体应以对应平台的服务商模式接入文档为准。
为什么 SDK 通常可以复用
SDK 主要负责封装请求、签名、验签、序列化和网络通信。普通商户模式与服务商模式一般使用相同的通信协议,因此往往不需要更换 SDK 软件包。
两种模式的主要区别是交易主体:
- 普通商户模式:商户使用自己的商户身份直接发起交易。
- 服务商模式:服务商代表特约商户发起交易,请求中需要同时携带服务商和特约商户的身份信息。
- 交易资金、账单、退款和回调通常归属于指定的特约商户。
- 服务商需要使用平台分配的服务商凭证完成签名或鉴权。
所以,SDK 可以共用,并不代表代码和配置都能原样保留。
后端需要调整的内容
1. 更换服务商身份配置
将普通商户配置切换为服务商配置,例如服务商商户号、应用标识、私钥、证书或 API 密钥。具体字段名称由支付平台决定。
建议保留原有配置,并按支付模式分别管理:
payment:
mode: service_provider
direct:
merchant_id: ${DIRECT_MERCHANT_ID}
app_id: ${DIRECT_APP_ID}
service_provider:
provider_id: ${PROVIDER_ID}
app_id: ${PROVIDER_APP_ID}
private_key_path: ${PROVIDER_PRIVATE_KEY_PATH}
私钥、证书密码和 API 密钥不应写入代码仓库。
2. 改用服务商下单流程
服务商下单通常需要额外传入特约商户标识。下面的代码只用于说明组织方式,其中的字段名不能直接当作某个平台的真实 API 参数:
PaymentRequest request = new PaymentRequest();
request.setOrderNo(order.getOrderNo());
request.setAmount(order.getAmount());
request.setDescription(order.getDescription());
if (paymentMode == PaymentMode.SERVICE_PROVIDER) {
request.setProviderId(config.getProviderId());
request.setSubMerchantId(order.getSubMerchantId());
} else {
request.setMerchantId(config.getMerchantId());
}
PaymentResponse response = paymentClient.createPayment(request);
需要逐项确认:
- 服务商模式应使用哪个下单接口。
- 是否必须传入特约商户号或子商户号。
- 使用服务商应用标识还是特约商户应用标识。
- 用户标识是否必须与指定应用身份匹配。
- 使用服务商凭证还是特约商户凭证生成签名。
这些规则不能根据普通商户代码直接推断。
3. 调整回调验签与订单归属校验
切换模式后,回调验签使用的证书或密钥可能不同。验证签名后,还要检查回调中的商户身份是否与订单记录一致,防止不同特约商户之间串单。
public void handlePaymentNotify(String body, Map<String, String> headers) {
PaymentNotification notification =
paymentClient.verifyAndParseNotification(body, headers);
Order order = orderRepository.findByOrderNo(notification.getOrderNo());
if (order == null) {
throw new IllegalArgumentException("Order not found");
}
if (!order.getSubMerchantId().equals(notification.getSubMerchantId())) {
throw new SecurityException("Sub-merchant mismatch");
}
if (notification.isPaid()) {
orderService.markPaidIdempotently(
order.getOrderNo(),
notification.getTransactionId()
);
}
}
示例中的类名和方法名仅表示代码结构,实际实现应使用目标支付平台 SDK 提供的接口。
4. 同步修改退款、查询和关单逻辑
改造范围不能只覆盖支付下单。以下操作通常也要使用相同的特约商户身份:
- 查询订单
- 关闭订单
- 发起退款
- 查询退款
- 下载账单
- 处理支付和退款通知
建议在本地订单中保存支付模式、服务商标识、特约商户标识和平台交易号。后续操作统一从订单记录中读取这些信息,不要依赖当前的全局配置。
前端是否需要重新集成
多数情况下,前端无需重新安装 SDK。前端仍然向业务后端请求支付参数,再调用原有的支付方法:
async function pay(orderNo) {
const response = await fetch(`/api/payment/prepare?orderNo=${orderNo}`);
const paymentParams = await response.json();
return paymentSdk.pay(paymentParams);
}
服务商号、特约商户号和签名都应由后端处理,前端不应自行拼接或生成。
以下情况可能需要修改前端:
- 服务商模式改用了不同的支付产品或调起接口。
- 后端返回的支付参数结构发生变化。
- 应用标识或用户授权主体发生变化。
- 前端 SDK 版本过旧,不支持目标接口。
- 支付场景从网页切换到小程序、App 或其他形式。
即使前端调用代码没有变化,也要验证后端生成的签名参数是否与当前应用、特约商户和用户身份匹配。
推荐切换步骤
- 确认服务商和特约商户已经完成平台要求的签约、授权或绑定。
- 检查现有 SDK 版本是否支持服务商模式使用的接口。
- 单独配置服务商凭证,不要与普通商户凭证混用。
- 修改下单、查询、关单、退款和账单接口中的商户身份参数。
- 更新回调验签配置,并校验特约商户归属。
- 保持前端只使用后端返回的支付参数。
- 在测试环境验证支付成功、取消支付、重复回调、退款和跨商户串单等场景。
- 上线时保留普通商户模式的回滚配置,不要一次性删除原有逻辑。
注意事项
- 使用相同的 SDK 包,不代表普通商户接口可以原样用于服务商模式。
- 服务商号和特约商户号不能互换。
- 应用标识、用户标识和商户身份通常存在绑定关系,组合错误可能导致下单或调起支付失败。
- 支付回调必须完成验签、金额校验、商户归属校验和幂等处理。
- 多个特约商户共用一套系统时,应根据订单选择对应的商户配置,避免使用单一的全局商户号。
- 是否需要升级 SDK,取决于当前版本是否包含服务商模式所需的 API,与 SDK 名称是否相同无关。
简而言之,切换到服务商支付模式通常可以复用 SDK,但需要调整业务接入。改造重点在后端,前端多数时候只需配合验证。是否完全不需要修改,仍要根据具体的支付平台和支付场景确认。
备注:内容仅供参考。
