微信支付之扫码、APP、小程序支付接入详解

做电商平台的小伙伴都知道,支付服务是必不可少的一部分,今天我们开始就说说支付服务的接入及实现。目前在国内,几乎90%中小公司的支付系统都离不开微信支付和支付宝支付。那么大家要思考了,为什么微信支付和支付宝支付能作为大多数公司接入的首选呢?其实这个问题大多小伙伴应该是很清楚的,说白了就是人家有庞大的用户流量,目前微信在国内的用户已突破10亿,支付宝也接近8亿左右,如此庞大的用户群体,你还会选择其他的第三方支付(微博钱包、财付通、快钱等)吗,作为普通客户,大家都希望能方便快捷,谁会为了在一个平台买点东西下载或开通其他服务呢,除非你给他有诱惑性的好处。今天我们先说说微信支付的接入及实现。

image

微信支付接入

首选我们去微信支付的官网,先看看官方提供的开发文档。链接地址:https://pay.weixin.qq.com/wiki/doc/api/index.html

image

我们先看看微信支付目前提供的支付方式(如上图),本次只讲原生支付(扫码支付)、App支付及小程序支付三种。一,准备工作

在开发前,需要先申请一个商家版的微信公众号或微信小程序(目前微信支付只有商家版公众号可开通),然后开通微信支付功能,并做相应的配置。

image

申请开通微信公众号和开通微信支付(商户)需要等待审核,一般都5个工作日左右。开通成功后,需要获取配置信息:

wx.pay.appid=***
wx.pay.mchid=***
wx.pay.key=***
wx.pay.secret=***

注:appid是公众号ID,mchid是支付的商户ID,其中appid和secret可以在公众平台找着,mchid和key则在商户平台找到,特别是key(即API_KEY)要在商户平台设置好。本项目中这些配置通过properties文件放在-payment-service工程的resource根路径下。
在编码之前,还需要登录微信商户平台配置支付回调URL,此配置作为支付成功后回调接口的域名。如果配置的URL为:http://www.abc.com/, 你的支付回调路径则可设置为:http://www.abc.com/api/payment/notify。二,编码阶段
*在开始编码前,我们必须先了解清楚微信支付的对接及支付的业务流程。

  • 扫码支付的业务流程:

image

  • App支付的业务流程:

image

  • 小程序支付的业务流程:

image

从官方提供的业务流程图我们可以大致总结对接流程如下:

1,在发起支付前,先在自己的商户后台下单,生成商户订单信息;

2,根据对应支付方式的参数需求,封装对应所需参数,并调用微信官方提供的统一下单Api接口下单;

3,统一下单成功,微信后台返回对应的响应数据。返回数据类型如下:

  • 扫码支付统一下单后会返回生成二维码图片的链接code_url;
  • app和小程序支付统一下单后会返回预支付id,即:prepay_id;

4,如果扫码支付,你要用code_url生成一个二维码展示在前端页面供客户扫码付款;如果是app和小程序支付,后端只需将prepay_id及需要的参数传给app和小程序端。app会通过调用SDK、小程序会通过调用微信的JS发起支付。5,客户付款成功后,客户的微信端会展示付款结果信息,同时微信后台会异步调用商户后台的回调接口(回调的api接口在统一下单作为下单参数),更新商户系统的支付单状态。

看到这里,大家会发现这三种方式的基本业务流程都差不多,只是由于不同支付方式调起微信应用支付功能的方式不同,所以统一下单成功后返回的参数有所不同。

Controller接口层:


@RestController
@RequestMapping(value = "/api/payment/")
public class PaymentController {

  private static Logger logger = LoggerFactory.getLogger(PaymentController.class);
  @Autowired
  private PaymentService paymentService;

  /**
   * App支付接口
   * 微信和支付宝统一下单入口
   *
   * @param request
   * @return
   * @throws Exception
   */
  @ResponseBody
  @RequestMapping(value="toPay", method=RequestMethod.POST, produces = {"application/json;charset=UTF-8"})
  public JSONObject toPay(HttpServletRequest request) throws Exception {
    String requestStr = RequestStr.getRequestStr(request);
    if (StringUtils.isEmpty(requestStr)) {
      throw new ParamException();
    }
    JSONObject jsonObj = JSONObject.parseObject(requestStr);
    if(StringUtils.isEmpty(jsonObj.getString("orderNo")) || StringUtils.isEmpty(jsonObj.getString("payAmount"))){
          throw new ParamException();
    }
    //验证订单是否存在
    String orderNo = jsonObj.getString("orderNo");
      double payAmount = jsonObj.getDouble("payAmount");
      if(payAmount < 0.01){
          return AjaxUtil.renderFailMsg("订单有误,请确认!");
      } else {
         //微信支付
         Map<String, String> resMap = paymentService.wxAppPayment(orderInfo.getOrderNo(),orderInfo.getPayPrice(),null);
         //判断微信统一下单是否成功
         if("SUCCESS".equals(resMap.get("returnCode")) && "OK".equals(resMap.get("returnMsg"))){
           //统一下单成功
           resMap.remove("returnCode");
           resMap.remove("returnMsg");
           logger.info("【App支付服务】微信支付下单成功!");
           return AjaxUtil.renderSuccessMsg(resMap);
         }else{
           logger.info("【App支付服务】微信支付下单失败!原因:"+resMap.get("returnMsg"));
           return AjaxUtil.renderFailMsg(resMap.get("returnMsg"));
         }
      }
}

PaymentService接口方法:

image

PaymentService实现类部分代码(微信App支付):

@Service(value = "paymentService")
public class PaymentServiceImpl implements PaymentService {

  private static Logger LOGGER = LoggerFactory.getLogger(PaymentServiceImpl.class);
  @Value("${spring.profiles.active}")
  private String PROJECT_ENV;
  @Value("${hcc.pay.domain}")
  private String payDomain;
  @Autowired
  private PaymentRecordMapper paymentRecordMapper;

  @Override
  @Transactional(readOnly=false,rollbackFor={Exception.class})
  public Map<String,String> wxAppPayment(String orderId, double money,Long customerId) throws Exception {
    LOGGER.info("【微信App支付】 统一下单开始, 订单编号="+orderId);
    SortedMap<String, String> resultMap = new TreeMap<String, String>();
    //生成支付金额
    double payAmount = PayUtils.getPayAmountByEnv(PROJECT_ENV, money);
    //TODO 操作数据库,添加或更新支付记录
    //this.addOrUpdatePaymentRecord(......);
    //微信统一下单
    Map<String,String> resMap = this.wxUnifieldOrder(orderId, PayConfig.TRADE_TYPE_APP, payAmount, null);
    if(PayConstant.SUCCESS.equals(resMap.get("return_code")) && PayConstant.OK.equals(resMap.get("return_msg"))){
        //封装参数返回
        resultMap.put("appid", PayConfig.WX_APP_ID);
        resultMap.put("partnerid", PayConfig.WX_MCH_ID);
        resultMap.put("prepayid", resMap.get("prepay_id"));
        resultMap.put("package", "Sign=WXPay");
        resultMap.put("noncestr", PayUtils.makeUUID(32));
        resultMap.put("timestamp", PayUtils.getCurrentTimeStamp());
        resultMap.put("sign", PayUtils.createSign(resultMap,PayConfig.WX_KEY));
        resultMap.put("returnCode", "SUCCESS");
        resultMap.put("returnMsg", "OK");
        LOGGER.info("【微信App支付】统一下单成功,返回参数:"+resultMap);
    }else{
        resultMap.put("returnCode", resMap.get("return_code"));
        resultMap.put("returnMsg", resMap.get("return_msg"));
        LOGGER.info("【微信App支付】统一下单失败,失败原因:"+resMap.get("return_msg"));
    }
    return resultMap;
}

统一下单方法(在PaymentService实现类里):


/**
     * <p>微信支付统一下单</p>
     *
     * @param orderId 订单编号
     * @param tradeType 支付类型
     * @param payAmount 支付金额
     * @param openid
     * @return
     * @throws Exception
     */
    private Map<String,String> wxUnifieldOrder(String orderId, String tradeType, double payAmount, String openid) throws Exception{
        //封装参数
        SortedMap<String,String> paramMap = new TreeMap<String,String>();
        String appid = PayConfig.WX_APP_ID;
        String mchid = PayConfig.WX_MCH_ID;
        if(PayConstant.WX_TRADE_TYPE_JSAPI.equals(tradeType)){
            appid = PayConfig.XCX_APP_ID;
            mchid = PayConfig.XCX_MCH_ID;
        }
        paramMap.put("appid", appid);
        paramMap.put("mch_id", mchid);
        paramMap.put("nonce_str", PayUtils.makeUUID(32));
        paramMap.put("body", BaseConstants.PLATFORM_COMPANY_NAME);
        paramMap.put("out_trade_no", orderId);
        paramMap.put("total_fee", PayUtils.moneyToIntegerStr(payAmount));
        paramMap.put("spbill_create_ip", PayUtils.getLocalIp());
        paramMap.put("notify_url", this.getNotifyUrl(PayConstant.PAY_TYPE_WX));
        paramMap.put("trade_type", tradeType);
        if(PayConstant.WX_TRADE_TYPE_JSAPI.equals(tradeType)){
            paramMap.put("openid",openid);
        }
        paramMap.put("sign", PayUtils.createSign(paramMap,PayConfig.WX_KEY));
        //转换为xml
        String xmlData = PayUtils.mapToXml(paramMap);
        //请求微信后台
        String resXml = HttpUtils.postData(PayConfig.WX_PAY_UNIFIED_ORDER, xmlData);
        LOGGER.info("【微信支付】 统一下单响应:\n"+resXml);
        return PayUtils.xmlStrToMap(resXml);
    }

统一下单完成,微信后台将相应的参数以xml的形式返回,统一下单成功后返回xml示例:

<xml>
   <return_code><![CDATA[SUCCESS]]></return_code>
   <return_msg><![CDATA[OK]]></return_msg>
   <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
   <mch_id><![CDATA[10000100]]></mch_id>
   <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
   <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
   <result_code><![CDATA[SUCCESS]]></result_code>
   <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
   <trade_type><![CDATA[APP]]></trade_type>
</xml>

因此我们需要将统一下单后的xml解析成map(上面的统一下单方法里已经转换成map),并判断下单状态。如果返回的return_code为SUCCESS并return_msg为OK,那么表示统一下单成功,然后封装对应的参数返回给前端。前端根据下单成功后Java后端返回的参数,进行相应的处理并唤起微信应用的支付服务。注意,扫码支付是用统一下单成功后微信后台返回的code_url生成二维码展示给客户。二维码的生成可以前端也可Java后端生成然后以输出流的形式输出到网页上(坚决不建议Java端生成二维码图片保存到文件服务器然后再展示)。

客户在手机调起微信支付服务并输入密码成功付款后,客户手机的微信里会收到支付成功的付款信息,同时微信后台也在异步调用商户的后台接口。这个回调地址就是在统一下单方法里我们传的notify_url字段的参数值。

下面是回调接口代码:

/**
   * 微信支付完成回调Api
   *
   * @param request
   * @param response
   * @throws Exception
   */
    @RequestMapping(value="notify")
  public void wxNotify(HttpServletRequest request,HttpServletResponse response) throws Exception {
     InputStream inputStream =  request.getInputStream();
     //获取请求输入流
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len=inputStream.read(buffer))!=-1){
            outputStream.write(buffer,0,len);
        }
        outputStream.close();
        inputStream.close();
        Map<String,Object> map = BeanToMap.getMapFromXML(new String(outputStream.toByteArray(),"utf-8"));
        logger.info("【微信支付回调】 回调数据:\n"+map);
        String resXml = "";
        String returnCode = (String) map.get("return_code");
        if ("SUCCESS".equalsIgnoreCase(returnCode)) {
            String returnmsg = (String) map.get("result_code");
            if("SUCCESS".equals(returnmsg)){
              //更新支付单状态信息
              int result = paymentService.wxNotify(map);
              if(result > 0){
                //支付成功
                  resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                          + "<return_msg><![CDATA[OK]]></return_msg>"+"</xml>";
              }
            }else{
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]></return_msg>" + "</xml>";
                logger.info("支付失败:"+resXml);
            }
        }else{
            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[报文为空]></return_msg>" + "</xml>";
            logger.info("【订单支付失败】");
        }
        logger.info("【微信支付回调响应】 响应内容:\n"+resXml);
        //做出响应
        response.getWriter().print(resXml);
  }

到此为止,所有的编码工作已完成。

三,测试(用扫码支付)

选择要购买的商品,然后下单,再去发起支付。

image

单击“去支付”按钮,跳转到二维码支付页面:

image

扫码支付完成后,显示二维码的页面会跳转到支付成功页面(带微信支付成功logo),并有3s的倒计时,然后跳转到“订单详情”页。

image

本文章只是给大家讲解原理和接入思路及实现,所以文章里只放了核心业务代码。还有好多工具类及配置等代码,全放到这里来肯定也不现实,如果有需要完整代码的可关注公众号获取。

获取方式

扫码关注公众号;找到“关于我”>>>"联系我" ;添加我个人微信获取;

原文地址:https://www.cnblogs.com/lyn20141231/p/11465124.html

时间: 2024-10-12 17:13:55

微信支付之扫码、APP、小程序支付接入详解的相关文章

当在微信扫一扫进入小程序 并获取到二维码的参数 从而实现扫码进入小程序

第一步:  登录你的小程序账号,找到设置 ,然后选择开发设置 点击添加后进去页面: 填写相应的一些二维码规则,就是你生成这个扫码的规则,还有根据项目情况填写下面对应的要求  保存之后可以返回到之前的页面 这样就实现了在微信打开扫一扫就进入小程序了 更深入一些: 当需要扫码进入之后需要获取二维码的参数进行下一步操作的话 ,可以再onload函数中进行判断 逻辑代码如下 if(e.q){ var scene = decodeURIComponent(e.q)  // 使用decodeURICompo

微信小程序 授权登录详解(附完整源码)

一.前言 由于微信官方修改了 getUserInfo 接口,所以现在无法实现一进入微信小程序就弹出授权窗口,只能通过 button 去触发. 官方连接:https://developers.weixin.qq.com/community/develop/doc/0000a26e1aca6012e896a517556c01 二.实现思路 自己写一个微信授权登录页面让用户实现点击的功能,也就是实现了通过 button 组件去触发 getUserInof 接口.在用户进入微信小程序的时候,判断用户是否

微信小程序的配置详解

1.配置详解: 使用app.json文件来对微信小程序进行全局配置,决定页面文件的路径.窗口表现.设置网络超时时间.设置多 tab 等. 1>pages 接受一个数组,每一项都是字符串,来指定小程序由哪些页面组成.每一项代表对应页面的[路径+文件名]信息,数组的第一项代表小程序的初始页面.小程序中新增/减少页面,都需要对 pages 数组进行修改. 文件名不需要写文件后缀,因为框架会自动去寻找路径.json,.js,.wxml,.wxss的四个文件进行整合. 2>window 用于设置小程序的

微信小程序页面传值详解

我们知道,在微信小程序中,从一个页面转到另一个页面,一般情况下可以通过navigate或redirect时候的url来携带参数,然后在目标页面的onLoad函数参数中获取这些url参数.例如: // 源页面A相关代码   wx.navigateTo({   url: "/pages/mypage/mypage?a=1&b=2"   })     // 目标页面B相关代码   Page({   onLoad: function (options) {   var a = opti

微信小程序要调数据 微信小程序 for 循环详解

现在要完成这样的效果: 我的代码是: <view class="l-setlist clr" > <template name="listab"> <image src="{{pic}}" class="fl setpic"></image> <view class="fr"> <view class="listbox"&

微信支付之扫码支付、公众号支付、H5支付、小程序支付相关业务流程分析总结

前言 很久以来,一直想写一篇微信支付有关的总结文档:一方面是总结自己的一些心得,另一方面也可以帮助别人,但是因种种原因未能完全理解透彻微信支付的几大支付方式,今天有幸做一些总结上的文章,也趁此机会,将一年多以来的相关经验分享一下. 概述 1. 扫码支付 商户在pc端展示一个支付二维码,用户使用微信扫一扫功能,扫码后实现付款的支付方式. 2. 公众号支付 商户在微信APP内(微信浏览器)打开H5网页,通过微信支付实现付款的支付方式. 3. H5支付 商户在微信浏览器以外的手机浏览器打开H5网页,通

8. PHP接入微信的三种支付:APP支付、公众号支付、扫码支付

微信的支付逻辑与支付宝的支付有一些差别.为了让客户端忽略这些差别,统一调用.本sdk做了对应处理. # SDK调用 微信支付不同接口需要的参数会有差别.请大家在使用接口时,仔细查看文档. use Payment\ChargeContext; use Payment\Config; use Payment\Common\PayException; // 微信支付,必须设置时区,否则发生错误 date_default_timezone_set('Asia/Shanghai'); // 生成订单号 便

微信小程序支付代码asp源码下载

昨天晚上给朋友写了一个asp的微信小程序支付接口,我这朋友的小程序服务器端用的asp做的,以前没有支付,非让我给写一个支付,因为必须是asp来写,他不会,所以我就帮他写了,顺便还写了一个asp获取用户资料头像和微信名入库的代码,也是asp源码,数据库用的是mdb的格式,还可以比较好用,微信小程序用asp做服务器端估计很少,应该有朋友需要,下载这个代码:ly.ftphn.com/wxpay.rar ============================================= 互相学习

微信小程序支付

关于微信小程序支付功能,他的官方文档真的写的很清楚了!但是整理吸收一下还是不错的 官网呈上:https://developers.weixin.qq.com/miniprogram/dev/api/wx.requestPayment.html 核心流程:下面的图和交互流程描述写的真是不能再清晰了 商户系统和微信支付系统主要交互: 1.小程序内调用登录接口,获取到用户的openid,api参见公共api[小程序登录API] 2.商户server调用支付统一下单,api参见公共api[统一下单API