ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

手把手教你如何采用服务商模式实现微信支付

2022-08-24 10:31:51  阅读:196  来源: 互联网

标签:手把手 String 商户 微信 服务商 request 支付


背景

小程序盛行时代,一般的企业中都都会包含多个小程序,而大部分的小程序通常都需要实现支付功能,本文将针对服务商模式进行微信支付进行详细讲解。

微信支付的模式

图片.png

一般企业选用是服务商或者为渠道商模式,但是成为渠道商需要相关流量支撑才能申请,本文以服务商模式进行讲解。

前期准备

注册服务商

服务商申请需要通过已做过公司认证的公众号,登录公司的微信服务号,在【微信支付】>【服务商申请】,直接跟着官方引导一步步操作即可,具体申请流程如下:

图片.png

说明:所以企业需要申请公共号,才能申请注册服务商。

服务商入驻

通过服务商来开发的系统来帮助商户微信支付,首先需要完成商户号在服务商号中的入驻过程。服务商注册成功后,进入微信支付平台,登录服务商,进行商户入驻。一般商户入驻有两种,具体如下:

  • 页面入驻
  • 调用API方式入驻

页面入驻

图片.png

入驻可以看作是商户生成商户号的同时与服务商形成绑定关系。具体可以参考微信公众号中按流程指引一步步操作就行。

图片.png

说明:商户入驻完成后,此商户才能用于微信支付。

申请证书

商户号入驻成功后,需要申请API证书。

图片.png

说明:按照官方文档申请证书,设置密钥,设置好密钥后一定要在安全的前提下记住,之后只能重置不能查看。

重要参数说明

  • appid:服务商Appid
  • mchId:服务商的商户id
  • mchKey:证书的序列号
  • subAppId:子商户小程序Appid
  • subOpenId:子商户小程序用户的openId
  • subMchId:子商户的商户id

子商户支付流程

以下是子商户APP中调用支付的交互时序图

图片.png

流程说明:

  • 用户在商户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);
    }
}
复制代码

相关测试

创建支付订单

请求参数

图片.png

说明:

  • subAppId:子商户的小程序id
  • subOpenid:小程序用户的openid

响应结果

图片.png

说明:returnCode为success,则说明创建微信支付订单成功。

查询订单

订单创建成功后,需要查询订单状态。

请求参数

图片.png

响应结果

图片.png

微信异步回调

关于微信支付回调,需要绑定外网域名,在内网需要测试的话,需要进行内网穿透,微信异步回调可能由于网络原因出现延迟,所以在业务上需要提供主动查询和异步回调结合的方式,关于具体实现将在后续的文章中进行讲解。

总结

本文服务商模式实现微信支付进行详细的讲解,实现过程还是比较复杂,订单支付中涉及到重复提交、幂等性验证、同步+异步的轮询处理等问题将在后续的文章中进行讲解。


作者:剑圣无痕
链接:https://juejin.cn/post/7134982531786473480
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

标签:手把手,String,商户,微信,服务商,request,支付
来源: https://www.cnblogs.com/jiangshengwuhen/p/16618926.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有