通道对接

适合技术人员阅读,帮助快速对接上游支付通道与代付通道

上游通道分为支付通道代付通道两类。支付通道负责接收商户收款请求并对接上游;代付通道负责调用上游代付/转账接口完成出款。

瓯海支付-pay LocalDynamicChannelService 内置通道 / 动态通道

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
重要提示

转账判断是否成功很关键,一定要在明确成功或失败时,才可以设置代付最终业务结果。

对接上游通道代付接口时,判断代付结果一般有三种方式:

  1. 同步响应结果

    调代付接口直接同步响应结果,明确告诉代付已经成功,该种最简单,直接处理业务即可。

  2. 主动查询代付结果

    调代付接口时,上游通道同步返回申请成功,需要主动查询代付结果。在系统中可通过 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

  3. 异步回调确认

    调代付接口时,上游通道同步返回申请成功,需要接收上游通道的异步回调,确认代付结果。需要实现回调处理类,名称格式为:通道名称 + TransNotifyService,重写 doNotify 方法。可参考项目中双乾代付的代码:SqpayTransNotifyService

如果还是搞不定,可以联系售后技术支持!

查看售后支持说明
已复制到剪贴板