上游通道分为支付通道和代付通道两类。支付通道负责接收商户收款请求并对接上游;代付通道负责调用上游代付/转账接口完成出款。
1. 支付通道
(1)支付流程
商户下单后,瓯海支付-pay 项目中的 PayOrderController 类的 payOrder 方法负责接收商户下单请求。该方法会根据商户的下单请求参数,得到具体的支付通道名称,然后调用该通道的 pay 方法完成上游通道的下单操作,核心代码如下:
payOrderId = payOrder.getPayOrderId();
String channelId = payOrder.getChannelId();
String channelName = channelId.substring(0, channelId.indexOf("_"));
AbstractRes res = localDynamicChannelService.callPaymentMethod(channelName, payOrder, true);
通过 localDynamicChannelService.callPaymentMethod(channelName, payOrder, true) 可以从 Spring 容器中得到该通道的实例,然后调用具体的下单方法。
命名通道类名时,格式须为:通道名称 + PaymentService,如 AlipayPaymentService 为支付宝通道。
在瓯海支付系统中,已支持内置支付通道和动态通道(可通过接口商店安装),具体代码逻辑参考类 LocalDynamicChannelService 中的业务逻辑。
内置支付通道
在 瓯海支付-pay 项目中硬编码实现,开发完成后需部署 class 文件并重启项目。
动态支付通道
动态开发的支付接口,打包成 jar 文件,在运营平台自动导入即可,项目可不用重启,直接可使用。
(2)内置支付通道实现
在 瓯海支付-pay 项目中的 channel 目录下,须为通道创建一个独立的目录。
比如以威富通为例,威富通就是一个具体的上游通道,给它定义名称为 swiftpay,一般名字的定义来自通道的品牌名称。
然后在该目录下创建具体的支付实现类,名字为:SwiftpayPaymentService,类的名字必须是通道名称 + PaymentService,首字母大写。然后该类继承自 BasePayment,重写 pay 方法即可。
一般一个通道会对应多种支付方式,比如威富通会有微信扫码支付、支付宝扫码支付、统一条码支付等。那么每种支付方式需要对应一个支付接口,命名规则为:通道名称 + _ + 支付方式,如威富通微信扫码支付定义为:swiftpay_wxpay_native。
在 pay 方法中,会根据商户选择的支付方式,对应到上游通道的实现方法中,可以参考威富通的支付对接实现。具体的每种支付方式,需要参考上游通道的接口和 demo 来实现。
- 通道目录名:
swiftpay - 支付实现类:
SwiftpayPaymentService - 支付接口代码:
swiftpay_wxpay_native
(3)通道支付回调
一般正规的支付流程,都是在用户支付成功后,上游通道会回调接口中上传的回调地址,那么我们需要处理上游过来的回调请求。
同样的,也是在支付通道目录下,创建一个支付回调的实现类,类的名字为:通道名称 + PayNotifyService,如威富通支付的回调类名为 SwiftpayPayNotifyService,该类继承自 BasePayNotify,重写 doNotify 方法即可。
在调用上游通道支付接口时,我们会指定回调地址,系统通过统一的方式获取支付回调地址,具体代码如下:
// 前端页面跳转通知地址
paramMap.put("returnUrl", super.getReturnUrl(getChannelName(), dbConfig));
// 后台异步回调通知地址
paramMap.put("notifyUrl", super.getNotifyUrl(getChannelName(), dbConfig));
notify 地址格式为:http://支付系统地址/notify/通道名称/notify_res.htm,如威富通的回调地址为:http://pay.xx.com/notify/swiftpay/notify_res.htm,每个通道获取到的通知地址,通道名称是对应自己的。
通知的入口类为 NotifyPayController,实现代码为:
_log.info("====== 开始接收{}支付回调通知 ======", channel);
// 验证回调是否在白名单
String notifyIp = IPUtility.getClientIp(request);
String checkResult = notifyCheck(channel, notifyIp);
if (checkResult != null) return checkResult;
// 获取通道对象实例
Object instance = localDynamicChannelService.getPayNotifyInterface(channel.toLowerCase());
if(instance instanceof PayNotifyInterface){
PayNotifyInterface payNotifyInterface = (PayNotifyInterface)instance;
if(payNotifyInterface == null){
return ApiBuilder.bizError("支付渠道类型[channel="+channel+"]实例化异常").toJSONString();
}
JSONObject retObj = payNotifyInterface.doNotify(request);
String notifyRes = retObj.getString(PayConstant.RESPONSE_RESULT);
_log.info("响应给{}:{}", channel, notifyRes);
_log.info("====== 完成接收{}支付回调通知 ======", channel);
return notifyRes;
}
原理同支付下单流程类似,也是得到具体的通道,然后从 Spring 容器中得到具体的通知实例,调用 doNotify 方法。
(4)通道参数定义
一般每个支付通道都会对应一些配置,比如商户 ID、私钥、网关地址等信息。我们需要根据上游通道的接口文档,抽象出具体的配置字段,然后定义配置类。
一般类的名称命名为通道名称 + Config,比如威富通的配置类为 SwiftpayConfig。
威富通的配置包括:商户 ID、商户 key、网关请求地址,代码示例如下:
public class SwiftpayConfig {
// 商户ID
private String mchId;
// 商户Key
private String key;
// 请求地址
private String reqUrl;
public SwiftpayConfig(){}
public SwiftpayConfig(String payParam) {
Assert.notNull(payParam, "init swiftpay config error");
JSONObject object = JSONObject.parseObject(payParam);
this.mchId = object.getString("mchId");
this.key = object.getString("key");
this.reqUrl = object.getString("reqUrl");
}
public String getMchId() { return mchId; }
public void setMchId(String mchId) { this.mchId = mchId; }
public String getKey() { return key; }
public void setKey(String key) { this.key = key; }
public String getReqUrl() { return reqUrl; }
public void setReqUrl(String reqUrl) { this.reqUrl = reqUrl; }
}
在调用上游通道接口时,当需要使用配置参数时,可以这样使用:
SwiftpayConfig swiftpayConfig = new SwiftpayConfig(getPayParam(payOrder));
map.put("mch_id", swiftpayConfig.getMchId());
(5)通道接口配置
以上支付通道的支付接口、回调接口都已经实现,这时需要在运营平台创建通道接口配置,才可使用。
创建支付接口类型
进入:运营平台 > 支付配置 > 支付接口类型 > 新增接口类型
| 配置项 | 说明 |
|---|---|
| 接口类型代码 | 与支付通道的通道名称一致,如:swiftpay |
| 接口类型名称 | 对应上游支付通道名称,如:威富通支付 |
| 配置定义描述 | 与通道配置类对应,JSON 格式,描述生成该通道配置账户界面的表单内容。可使用官方工具生成:jeequan.com/dev/tool.html |
| 回调 IP 白名单 | 如果配置了 IP,那么只有该 IP 下的回调才会通过 |
| 订单超时时间 | 如果配置了,过了超时时间,系统会自动将订单关闭 |
创建支付接口
进入:运营平台 > 支付配置 > 支付接口 > 新增支付接口
| 配置项 | 说明 |
|---|---|
| 接口代码 | 对应每个支付通道定义的支付接口,如威富通微信扫码:swiftpay_wxpay_native |
| 接口类型 | 选择对应的通道名称 |
| 支付类型 | 根据具体的支付场景,选择对应的支付类型 |
通道账号配置
进入:运营平台 > 支付配置 > 支付通道 > 子账户
账户配置的表单,来自上面配置的支付接口类型的「配置定义描述」,也对应通道配置类中的属性。
2. 代付通道
代付通道的实现参考支付通道即可,这里调用的是上游的代付接口。下面给出几个核心逻辑类:
| 类型 | 类名格式 / 入口 | 说明 |
|---|---|---|
| 代付下单入口 | AgentpayController | 接收代付请求 |
| 转账实现类 | 通道名称 + TransService | 重写 trans 方法 |
| 转账回调类 | 通道名称 + TransNotifyService | 重写 doNotify 方法,参考 SqpayTransNotifyService |
转账判断是否成功很关键,一定要在明确成功或失败时,才可以设置代付最终业务结果。
对接上游通道代付接口时,判断代付结果一般有三种方式:
-
同步响应结果
调代付接口直接同步响应结果,明确告诉代付已经成功,该种最简单,直接处理业务即可。
-
主动查询代付结果
调代付接口时,上游通道同步返回申请成功,需要主动查询代付结果。在系统中可通过 MQ 延迟查询实现:
// 交易处理中 _log.info("{} >>> 转账处理中", logPrefix); JSONObject msgObj = new JSONObject(); msgObj.put("count", 1); msgObj.put("transOrderId", transOrderId); msgObj.put("channelName", getChannelName()); mq4TransQuery.send(msgObj.toJSONString(), 10 * 1000); // 10秒后查询 return QueryRetMsg.waiting();通过 MQ 方式设置延迟查询,直到查询到成功或失败。可参考项目中杉德代付的代码:
SandpayTransService。 -
异步回调确认
调代付接口时,上游通道同步返回申请成功,需要接收上游通道的异步回调,确认代付结果。需要实现回调处理类,名称格式为:通道名称 +
TransNotifyService,重写doNotify方法。可参考项目中双乾代付的代码:SqpayTransNotifyService。
如果还是搞不定,可以联系售后技术支持!
查看售后支持说明