该文档适合技术人员阅读,可以帮助技术人员快速对接上游支付通道。

支付流程

商户下单后,xxpay-pay项目中的PayOrderController类中的payOrder方法负责接收商户下单请求,该方法中会根据商户的下单请求参数,得到具体的支付通道名称,然后调用该通道的pay方法完成上游通道的下单操作,具体代码提下如下。

paymentInterface = (PaymentInterface) SpringUtil.getBean(channelName.toLowerCase() +  "PaymentService");

AbstractRes res = paymentInterface.pay(payOrder);

通过 (PaymentInterface) SpringUtil.getBean(channelName.toLowerCase() + "PaymentService"); 可以从spring容器中得到该通道的实例,然后调用具体的下单方法paymentInterface.pay(payOrder)完成支付。

在命名通道类名的时候,格式须为:通道名称+PaymentService,如:AlipayPaymentService 为支付宝通道。

支付通道实现

在xxpay-pay项目中的channel目录下,须为通道创建一个独立的目录。

比如这里以盛付通为例,盛付通就是一个具体的上游通道,给它定义名称为shengpay,一般名字的定义来自通道的品牌名称。

然后在该目录下创建具体的支付实现类,名字为:ShengpayPaymentService,类的名字必须是通道名称+PaymentService,首字母大写。然后该类继承自BasePayment,重写pay方法即可。

一般一个通道会对应多种支付方式,比如盛付通会有微信公众号支付,微信条码支付,支付宝生活号支付,支付宝条码支付等。那么每种支付方式需要对应一个支付接口,我们可以这样命名:通道名称+_+支付方式,如盛付通微信条码支付我们定义为:shengpay_wx_bar

在pay方法中,我们会根据商户选择的支付方式,对应到上游通道的实现方法中,可以参考盛付通的支付对接实现。具体的每种支付方式,需要参考上游通道的接口和demo来实现。

通道支付回调

一般的支付流程,都是在用户支付成功后,上游通道会回调接口中上传的回调地址,那么我们需要处理上游过来的回调请求。

同样的,也是在支付通道目录下,创建一个支付回调的实现类,类的名字为:通道名称+PayNotifyService,如盛付通支付的回调类名为:ShengpayPayNotifyService,该类继承自BasePayNotify,重写doNotify方法即可。

在调用上游通道支付接口时,我们会指定回调地址,那么在我们系统中,通过统一的方式获取支付回调地址,具体代码如下。

// 前端页面跳转通知地址
payConfig.getReturnUrl(getChannelName())
// 后台异步回调通知地址
payConfig.getNotifyUrl(getChannelName())

notify地址格式为:http://支付系统地址/notify/通道名称/notify_res.htm,如盛付通的回调地址为:http://pay.xx.com/notify/shengpay/notify_res.htm,每个通道获取到的通知地址,通道名称是对应自己的。

通知的入口类为:NotifyPayController,实现代码为:

    @RequestMapping(value= {"/notify/{channel}/notify_res.htm", "/notify/{channel}/{payOrderId}/notify_res.htm"})
    @ResponseBody
    public ResponseEntity payNotifyRes(HttpServletRequest request, @PathVariable("channel") String channel, @PathVariable(value = "payOrderId", required = false) String urlOrderId) throws ServletException, IOException {
        _log.info("====== 开始接收{}支付回调通知:urlOrderId:[{}] ======", channel, StringUtils.defaultString(urlOrderId, ""));
        try {
            payNotifyInterface = (PayNotifyInterface) SpringUtil.getBean(channel.toLowerCase() +  "PayNotifyService");
        }catch (BeansException e) {
            _log.error(e, "");
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.TEXT_HTML);
            return  ResponseEntity.badRequest().body(XXPayUtil.makeRetFail(XXPayUtil.makeRetMap(PayConstant.RETURN_VALUE_FAIL, "支付渠道类型[channel="+channel+"]实例化异常", null, null)));
        }

        try {
            JSONObject retObj;
            if (StringUtils.isNotBlank(urlOrderId)) {
                retObj = payNotifyInterface.doNotify(request, urlOrderId);
            }else {
                retObj = payNotifyInterface.doNotify(request);
            }
            Object notifyRes = retObj.get(PayConstant.RESPONSE_RESULT);
            if (notifyRes instanceof ResponseEntity) {
                return (ResponseEntity) notifyRes;
            }else {
                _log.info("响应给{}:{}", channel, notifyRes);
                _log.info("====== 完成接收{}支付回调通知 ======", channel);
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.setContentType(MediaType.TEXT_HTML);
                return  new ResponseEntity(notifyRes, httpHeaders, HttpStatus.OK);
            }
        }catch (Exception e) {
            _log.error(e, "");
            return ResponseEntity.badRequest().body(e.getMessage());
        }
    }

原理同支付下单流程类似,也是得到具体的通道,然后从spring容器中得到具体的通知实例,调用doNotify方法。

通道参数定义

一般每个支付通道都会对应一些配置,比如商户ID,私钥,网关地址等信息。我们需要根据上游通道的接口文档,抽象出具体的配置字段,然后定义配置类。

一般类的名称命名为通道名称+Config,比如盛付通的配置类为ShengpayConfig

威富通的配置包括:商户号,商户私钥,盛付通公钥,子商户号,那么我们的代码是这样的。

    public class ShengpayConfig {

    //测试地址:  http://mchapitest.shengpay.com
    //正式地址:  http://mchapi.shengpay.com
    public static final String REQ_URL = "http://mchapi.shengpay.com";

    public static final String CNY ="CNY";
    public static final String DEFAULT_CHARSET = "UTF-8";

    /**
     * 商户号
     */
    public String mchId;
    /**
     * 商户私钥
     */
    public String privateKey;
    /**
     * 盛付通公钥
     */
    public String publicKey;
    /**
     * 子商户号
     */
    public String subMchId;

    public String getMchId() {
        return mchId;
    }

    public void setMchId(String mchId) {
        this.mchId = mchId;
    }

    public String getPrivateKey() {
        return privateKey;
    }

    public void setPrivateKey(String privateKey) {
        this.privateKey = privateKey;
    }

    public String getPublicKey() {
        return publicKey;
    }

    public void setPublicKey(String publicKey) {
        this.publicKey = publicKey;
    }

    public String getSubMchId() {
        return subMchId;
    }

    public void setSubMchId(String subMchId) {
        this.subMchId = subMchId;
    }

    public ShengpayConfig() {
    }

    public ShengpayConfig(JSONObject mainParam) {
        this.mchId = mainParam.getString("mchId");
        this.privateKey = mainParam.getString("privateKey");
        this.publicKey = mainParam.getString("publicKey");
    }

    public ShengpayConfig(JSONObject mainParam, JSONObject subMchParam) {
        this.mchId = mainParam.getString("mchId");
        this.privateKey = mainParam.getString("privateKey");
        this.publicKey = mainParam.getString("publicKey");
        this.subMchId = subMchParam.getString("subMchId");
    }

}

在调用上游通道接口时,当需要使用配置参数时,我们可以这样使用。

JSONObject mainParam = getMainPayParam(payOrder);
JSONObject subMchParam = getSubMchPayParam(payOrder);
ShengpayConfig shengpayConfig = new ShengpayConfig(mainParam, subMchParam);
// 服务商商户号
String mcnId = shengpayConfig.getMchId();
// 子商户商户号
String subMchId = shengpayConfig.getSubMchId();

通道接口配置

以上支付通道的支付接口,回调接口都已经实现,这时需要在运营平台创建接口类型和支付接口配置,才可使用。

创建支付接口类型

进入:运营平台 > 支付配置 > 接口类型 > 新增接口类型

  • 接口类型代码:这个和支付通道的通道名称是一致的,如:shengpay。
  • 接口类型名称:对应上游支付通道名称,如:盛付通。
  • [服务商]配置定义描述:这里是服务商配置支付参数的json描述。
  • [服务商子商户]配置定义描述:这里是服务商子商户配置支付参数的json描述。
  • [私有商户]配置定义描述:暂时不支持,留空即可。

配置定义描述内容为json格式,描述了生成该通道配置账户界面的表单内容。可使用官方工具生成:https://www.jeequan.com/dev/tool.html

创建支付接口

进入:运营平台 > 支付配置 > 支付接口 > 新增支付接口

  • 接口代码:这里对应我们我们为每个支付通道定义的支付接口,如盛付通的微信条码支付,名字为:shengpay_wx_bar。
  • 接口类型:选择对应的通道名称。
  • 支付类型:根据具体的支付场景,选择对应的支付类型。

通道参数配置

进入:运营平台 > 服务商管理 > 接口配置,选择一条支付通道进行配置。

如盛付通的服务商配置,其中多出来的微信和支付宝配参数,是为了跳转获取微信openid和支付宝userid使用。

进入:服务商系统 > 商户管理 > 通道配置,选择一条支付通道进行配置。

如盛付通的子商户配置。

以上为支付通道的对接。如果还是搞不定,可以联系售后技术支持哦!

文档更新时间: 2021-10-23 23:46   作者:大森林