该文档适合技术人员阅读,可以帮助技术人员快速对接上游支付通道。
支付流程
商户下单后,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使用。
进入:服务商系统 > 商户管理 > 通道配置,选择一条支付通道进行配置。
如盛付通的子商户配置。
以上为支付通道的对接。如果还是搞不定,可以联系售后技术支持哦!