手把手教你如何采用服务商模式实现微信支付
作者:互联网
背景
小程序盛行时代,一般的企业中都都会包含多个小程序,而大部分的小程序通常都需要实现支付功能,本文将针对服务商模式进行微信支付进行详细讲解。
微信支付的模式
一般企业选用是服务商或者为渠道商模式,但是成为渠道商需要相关流量支撑才能申请,本文以服务商模式进行讲解。
前期准备
注册服务商
服务商申请需要通过已做过公司认证的公众号,登录公司的微信服务号,在【微信支付】>【服务商申请】,直接跟着官方引导一步步操作即可,具体申请流程如下:
说明:所以企业需要申请公共号,才能申请注册服务商。
服务商入驻
通过服务商来开发的系统来帮助商户微信支付,首先需要完成商户号在服务商号中的入驻过程。服务商注册成功后,进入微信支付平台,登录服务商,进行商户入驻。一般商户入驻有两种,具体如下:
- 页面入驻
- 调用API方式入驻
页面入驻
入驻可以看作是商户生成商户号的同时与服务商形成绑定关系。具体可以参考微信公众号中按流程指引一步步操作就行。
说明:商户入驻完成后,此商户才能用于微信支付。
申请证书
商户号入驻成功后,需要申请API证书。
说明:按照官方文档申请证书,设置密钥,设置好密钥后一定要在安全的前提下记住,之后只能重置不能查看。
重要参数说明
- appid:服务商Appid
- mchId:服务商的商户id
- mchKey:证书的序列号
- subAppId:子商户小程序Appid
- subOpenId:子商户小程序用户的openId
- subMchId:子商户的商户id
子商户支付流程
以下是子商户APP中调用支付的交互时序图
流程说明:
- 用户在商户APP中选择商品,选择微信支付,提交订单,如图中步骤1-3所示。
- 调用服务商提供的下单接口,服务商后台收到下单请求,会返回签好名的订单数据,用于商户APP里面调起微信支付,如图中步骤3-5所示。
- 用户确认支付,输入密码,支付完成,如图中步骤6-8所示。
- 支付完成后,微信返回商户APP,回调APP实现的回调函数,此时需要根据单号调用服务商提供的查询结果,查询后台实际支付结果,再作用户页面展示和发货操作。如图步骤9-13.
实现方案
服务商模式的微信支付的具体实现方案,本文采用的是Spring Boot集成weixin-java-pay来实现微信支付。
实现步骤
jar包引入
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.1.0</version>
</dependency>
复制代码
支付配置
# 微信支付配置
wx:
pay:
#服务商id
mchId: 12
#证书
keyPath: apiclient_cert.p12
#服务商appid
appId: wxe12233
#支付回调通知地址
notifyUrl: https://localhost:8080/pay/resWxPay
#服务商key的密钥
mchKey: 123444
复制代码
相关配置类
@Component
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayProperties {
/**
* 小程序appid
*/
private String appId;
/**
* 微信支付商户号
*/
private String mchId;
/**
* 微信支付商户密钥
*/
private String mchKey;
/**
* 服务商模式下的子商户公众账号ID
*/
private String subAppId;
/**
* 服务商模式下的子商户号
*/
private String subMchId;
/**
* apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定
*/
private String keyPath;
/**
* 支付回调
*/
private String notifyUrl;
//省略get、set方法
}
复制代码
说明:读取微信支付的配置信息
/**
* 微信支付配置类
*/
@Configuration
public class CustWxPayConfig
{
@Autowired
private WxPayProperties properties;
@Bean
@ConditionalOnMissingBean
public WxPayService wxService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
payConfig.setNotifyUrl(StringUtils.trimToNull(this.properties.getNotifyUrl()));
//设置微信支付参数
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
复制代码
说明:将微信支付的相关参数设置到wxjava中的cofig中。
业务实现类
@Service
@Transactional(rollbackFor =Exception.class)
public class CustWxPayServiceImpl implements CustWxPayService
{
private Logger logger = LoggerFactory.getLogger(CustWxPayService.class);
@Autowired
private WxPayService wxService;
public void createWxPayOrder(WxPayObject payObject)
{
logger.info("Create wx pay order.");
//并发控制
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
request.setTradeType(payObject.getTradeType());
int payMoney = payObject.getPayMoney()
.multiply(new BigDecimal(100)).intValue();
// totalFee单位为(分)
request.setTotalFee(payMoney);
//第三方订单号
request.setOutTradeNo(payObject.getOrderNo());
//用户OpenID(支付小程序)
request.setSubOpenid(payObject.getSubOpenid());
//支付小程序APPID
request.setSubAppId(payObject.getSubAppId());
request.setNonceStr(getRandomString(32));
request.setBody(payObject.getBody());
request.setFeeType(payObject.getFeeType());
HttpServletRequest httpRequest = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
//IP地址
request.setSpbillCreateIp(IpUtil.getIpAddr(httpRequest));
//配置回调地址
request.setNotifyUrl(wxService.getConfig().getNotifyUrl());
//收款子商户
String subMchId = wxService.getConfig().getSubAppId();
request.setSubMchId(subMchId);
//DeviceID
request.setDeviceInfo(payObject.getDeviceInfo());
//支付类型:
request.setTradeType(payObject.getTradeType());
//创建建订单
WxPayMpOrderResult wxPayMpOrderResult =null;
try
{
wxPayMpOrderResult = wxService.createOrder(request);
if (wxPayMpOrderResult == null)
{
logger.info("Create Wx Order Success. Result is null.");
}
else
{
logger.info("Create Wx Order Success. Result:{}",
JSON.toJSONString(wxPayMpOrderResult));
}
}
catch (WxPayException e)
{
logger.error("createWxPayOrder error",e);
throw new SysException("create wx order error",e);
}
}
@Override
public void resWxPay(String xmlData)
{
logger.info("Wx Pay Response, param[{}]", xmlData);
WxPayOrderNotifyResult result =null;
try
{
result = wxService
.parseOrderNotifyResult(xmlData);
//支付失败
if ("FAIL".equalsIgnoreCase(result.getResultCode()) || "FAIL".equalsIgnoreCase(result.getReturnCode()))
{
//记录支付失败错误日志
throw new SysException(result.getErrCodeDes());
}
//获取订单号
String outTradeNo = result.getOutTradeNo();
if (StringUtils.contains(outTradeNo, "_"))
{
outTradeNo = outTradeNo.substring(0, outTradeNo.indexOf("_"));
}
//执行具体业务逻辑
//记录流水
//更新订单状态
}
catch (WxPayException e)
{
throw new SysException("wx pay error",e);
}
}
@Override
public WxPayOrderQueryResult queryOrder(String outTradeNo)
{
WxPayOrderQueryResult wxPayOrderQueryResult =null;
try
{
wxPayOrderQueryResult= wxService.queryOrder(null, outTradeNo);
}
catch (WxPayException e)
{
throw new SysException("query wx order error",e);
}
return wxPayOrderQueryResult;
}
public static String getRandomString(int length)
{
String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
}
复制代码
说明:实现微信创建订单、支付回调、查询订单接口。
测试类
@RestController
@RequestMapping("/pay/wxpay/")
public class WxPayController
{
private Logger logger =LoggerFactory.getLogger(WxPayController.class);
@Autowired
private CustWxPayService custWxPayService;
/**
*
* @param subAppId 小程序AppId
* @param subOpenid 用户在小程序的openid
* @param payMoney 支付金额
* @param orderNo 第三方订单号
* @return
*/
@PostMapping("/reqWxPay")
public Result reqWxPay(@RequestBody WxPayObject wxPayObject)
{
//支付参数验证
logger.info("Req Wx Pay,param:{}",JSON.toJSONString(wxPayObject));
Result result =new Result();
try
{
//创建订单
custWxPayService.createWxPayOrder(wxPayObject);
}
catch(Exception e)
{
logger.error("Req Wx Pay Error.", e);
return Result.error("Req Wx Pay error"+e);
}
return Result.success(result);
}
/**
* 异步回调
* @param xmlData
* @return
*/
@PostMapping("/resWxPay")
public String resWxPay(@RequestBody String xmlData)
{
logger.info("Wx Pay Response, Data:[{}]", xmlData);
try
{
custWxPayService.resWxPay(xmlData);
}
catch(Exception e)
{
logger.error("Wx Pay Response Process Error.", e);
return WxPayNotifyResponse.fail("订单支付失败.");
}
logger.info("Wx Pay Response Process Success.");
return WxPayNotifyResponse.success("付款成功");
}
@GetMapping("/queryOrder")
public Result queryOrder(String outTradeNo)
{
WxPayOrderQueryResult wxPayOrderQueryResult = custWxPayService.queryOrder(outTradeNo);
logger.info(wxPayOrderQueryResult.toString());
return Result.success(wxPayOrderQueryResult);
}
}
复制代码
相关测试
创建支付订单
请求参数
说明:
- subAppId:子商户的小程序id
- subOpenid:小程序用户的openid
响应结果
说明:returnCode为success,则说明创建微信支付订单成功。
查询订单
订单创建成功后,需要查询订单状态。
请求参数
响应结果
微信异步回调
关于微信支付回调,需要绑定外网域名,在内网需要测试的话,需要进行内网穿透,微信异步回调可能由于网络原因出现延迟,所以在业务上需要提供主动查询和异步回调结合的方式,关于具体实现将在后续的文章中进行讲解。
总结
本文服务商模式实现微信支付进行详细的讲解,实现过程还是比较复杂,订单支付中涉及到重复提交、幂等性验证、同步+异步的轮询处理等问题将在后续的文章中进行讲解。
作者:剑圣无痕
链接:https://juejin.cn/post/7134982531786473480
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
标签:手把手,String,商户,微信,服务商,request,支付 来源: https://www.cnblogs.com/jiangshengwuhen/p/16618926.html