紫玉坏小子

导航

微信小程序支付 未完成

微信小程序支付

在进行微信支付开发之前,深刻理解微信支付的账号关系非常有助于使用微信以及微信支付的能力。微信支付对商户开放的所有面对用户使用的api,都是由appid和mchid成对使用的。微信支付开放的能力主要分2大类,普通模式和服务商模式

 

普通模式

最常规的普通模式,适用于有自己开发团队或外包开发商的直连商户收款。开发者申请自己的appid和mchid,两者需具备绑定关系,以此来使用微信支付提供的开放接口,对用户提供服务

服务商模式

第三方服务商申请自己的服务号appid,并通过该服务号appid申请服务商mchid,以此获得微信支付服务商能力。 再通过服务商mchid为所服务的特约商户申请创建微信支付sub_mchid,创建好的sub_mchid默认和服务商的mchid建立父子授权关系。 以此来使用微信支付提供的开放接口,对特约商户及用户提供服务。 同时,微信支付为服务商模式下的每一条“mchid-sub_mchid父子授权关系”上,都开放了一些开发配置能力供服务商配置,包括不限于支付授权目录、推荐关注的appid、sub_appid等。 以小程序支付举例,服务商订单由哪个小程序调用js拉起支付,则需要在特约商户开发配置中将该小程序appid配置成sub_appid。 每条父子关系上的sub_appid可以为多,用以满足不同的场景需求,但每笔交易只能使用1个。

接入前准备
    1. 选择接入模式

      商户/服务商在接入前首先要判断自己公司注册区域适用的接入模式,微信支付目前提供两种接入方式:直连模式和服务商模式

      1.1 直连模式

      信息、资金流:微信支付—>直连商户

      直连模式,商户自行申请入驻微信支付,无需服务商协助。(商户平台申请)成为普通商户

      1.2 服务商模式

      服务商模式,商户申请成为微信支付服务商,服务商自身无法作为一个普通商户直接发起交易,其发起交易必 须传入相关特约商户商户号的参数信息。(服务商平台申请)成为普通服务商

    2. 参数申请

      商户自行申请入驻微信支付,无服务商协助。(商户平台申请)成为普通商户

      2.1 申请appId

      2.2 申请mchId

      2.3 绑定appId及mchId

    3. 配置API Key

      API v3密钥主要用于平台证书解密、回调信息解密 (具体看文档)

    4. 下载并配置商户证书

      商户API证书具体使用说明可参见接口规则文档中私钥和证书章节

      商户可登录微信商户平台,在【账户中心】->【API安全】->【API证书】目录下载证书(具体看文档)

    5. 配置应用

      5.1 申请小程序开发者账号,进行微信认证,获取appid登录《微信公众平台》,注册一个小程序的开发者账号。小程序账号申请指引

      5.2 小程序开通微信支付,即申请或复用微信支付商户号,申请完小程序后,登录小程序后台。点击左侧导航栏的微信支付,在页面中进行开通。

    备注
      1. 证书的下载

        登陆商户平台–>账户中心–>api安全–>API安全 然后生成证书,最终会生成3个文件

        ***_cert.p12       ***_cert.prm     ***_key.pem
      1. 设置api秘钥和apiv3秘钥

        登陆商户平台–>账户中心–>api安全–>设置api秘钥/设置apiv3秘钥 保存好,后面要用到

      1. 获取平台证书 platformCert.pem

        启动服务,本地访问接口: localhost/v3/get 这里会请求腾讯接口,拿到平台证书

      1. 具体的字段

      public class WxPayV3Bean {
         // 应用appId
         private String appId;
         // v3 秘钥
         private String keyPath;
         // CA证书 格式.pem
         private String certPath;
         // CA证书 格式.p12
         private String certP12Path;
         // 平台证书路径
         private String platformCertPath;
         // 商户id
         private String mchId;
         // 自定义 api秘钥
         private String apiKey;
         // 自定义 apiv3秘钥
         private String apiKey3;
         // 项目域名
         private String domain;
      }
    •  

  • 统一下单

    商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付

  • 接口说明

    1. 请求url:https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi

    2. 请求方式:post

  • 请求参数

    应用IDappid直连商户申请的公众号或移动应用appid
    直连商户号 mchid 直连商户的商户号,由微信支付生成并下发
    商品描述 description 商品描述
    商户订单号 out_trade_no 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一 示例值:1217752501201407033233368018
    交易结束时间 time_expire 订单失效时间 具体方法看文档 这个参数不是必须的
    通知地址 notify_url 通知URL必须为直接可访问的URL,不允许携带查询串,要求必须为https地址。 格式:URL
    订单金额    
      总金额(total) 订单总金额,单位为分。
      货币类型(currency) CNY:人民币,境内商户号仅支持人民币。 示例值:CNY
    支付者    
      用户标识(openId) 用户在直连商户appid下的唯一标识。 示例值:oUpF8uMuAJO_M2pxb1Q9zNjWeS6o
  • 请求实例

    {
    "time_expire": "2018-06-08T10:34:56+08:00",
    "amount": {
    "total": 100,
    "currency": "CNY"
    },
    "mchid": "1230000109",
    "description": "Image形象店-深圳腾大-QQ公仔",
    "notify_url": "https://www.weixin.qq.com/wxpay/pay.php",
    "payer": {
    "openid": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"
    },
    "out_trade_no": "1217752501201407033233368018",
    "goods_tag": "WXG",
    "appid": "wxd678efh567hg6787",
    "attach": "自定义数据说明",
    "detail": {
    "invoice_id": "wx123",
    "goods_detail": [{
    "goods_name": "iPhoneX 256G",
    "wechatpay_goods_id": "1001",
    "quantity": 1,
    "merchant_goods_id": "商品编码",
    "unit_price": 828800
    }, {
    "goods_name": "iPhoneX 256G",
    "wechatpay_goods_id": "1001",
    "quantity": 1,
    "merchant_goods_id": "商品编码",
    "unit_price": 828800
    }],
    "cost_price": 608800
    },
    "scene_info": {
    "store_info": {
    "address": "广东省深圳市南山区科技中一道10000号",
    "area_code": "440305",
    "name": "腾讯大厦分店",
    "id": "0001"
    },
    "device_id": "013467007045764",
    "payer_client_ip": "14.23.150.211"
    }
    }
  • 代码(这里使用的WxPayApi是IJPay-WxPay中的 不是IJPay-All中的)

    /**
        * 功能描述:
        * 〈微信小程序支付统一下单〉
        * @param openId 1
        * @return : java.lang.String
        * @author : hxz
        * @date : 2021/2/5 19:05
        */
       @RequestMapping("/mini")
       public String jsApiPay(@RequestParam(value = "openId", required = false, defaultValue = "o-_-itxuXeGW3O1cxJ7FXNmq8Wf8") String openId) {
          try {
              //设置交易结束时间
              String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
              UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                      .setAppid(wxPayV3Bean.getAppId())
                      .setMchid(wxPayV3Bean.getMchId())
                      .setDescription("IJPay 让支付触手可及")
                      .setOut_trade_no(PayKit.generateStr())
                      .setTime_expire(timeExpire)
                      .setAttach("微信系开发脚手架 https://gitee.com/javen205/TNWX")
                      .setNotify_url("回调路径")
                      .setAmount(new Amount().setTotal(1))
                      .setPayer(new Payer().setOpenid(openId));
              log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
              IJPayHttpResponse response = WxPayApi.v3(
                      RequestMethod.POST,
                      WxDomain.CHINA.toString(),
                      WxApiType.JS_API_PAY.toString(),
                      wxPayV3Bean.getMchId(),
                      getSerialNumber(),
                      null,
                      wxPayV3Bean.getKeyPath(),
                      JSONUtil.toJsonStr(unifiedOrderModel)
              );
              // 根据证书序列号查询对应的证书来验证签名结果
              boolean verifySignature = com.ijpay.core.kit.WxPayKit.verifySignature(response, wxPayV3Bean.getPlatformCertPath());
              log.info("verifySignature: {}", verifySignature);
              log.info("统一下单响应 {}", response);

              if (verifySignature) {
                  String body = response.getBody();
                  JSONObject jsonObject = JSONUtil.parseObj(body);
                  String prepayId = jsonObject.getStr("prepay_id");
                  Map<String, String> map = WxPayKit.jsApiCreateSign(wxPayV3Bean.getAppId(), prepayId, wxPayV3Bean.getKeyPath());
                  log.info("唤起支付参数:{}", map);
                  return JSONUtil.toJsonStr(map);
              }
              return response.getBody();
          }catch (Exception e){

          }
           return null;
      }

    其中getSerialNumber()方法

     String serialNo;
    private String getSerialNumber() {
           if (StrUtil.isEmpty(serialNo)) {
               // 获取证书序列号
               X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayV3Bean.getCertPath()));
               serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
    //           System.out.println("输出证书信息:\n" + certificate.toString());
    //           // 输出关键信息,截取部分并进行标记
    //           System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
    //           System.out.println("版本号:" + certificate.getVersion());
    //           System.out.println("签发者:" + certificate.getIssuerDN());
    //           System.out.println("有效起始日期:" + certificate.getNotBefore());
    //           System.out.println("有效终止日期:" + certificate.getNotAfter());
    //           System.out.println("主体名:" + certificate.getSubjectDN());
    //           System.out.println("签名算法:" + certificate.getSigAlgName());
    //           System.out.println("签名:" + certificate.getSignature().toString());
          }
           System.out.println("serialNo:" + serialNo);
           return serialNo;
      }

    回调方法

     @RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
       @ResponseBody
       public void payNotify(HttpServletRequest request, HttpServletResponse response) {
           Map<String, String> map = new HashMap<>(12);
           try {
               String timestamp = request.getHeader("Wechatpay-Timestamp");
               String nonce = request.getHeader("Wechatpay-Nonce");
               String serialNo = request.getHeader("Wechatpay-Serial");
               String signature = request.getHeader("Wechatpay-Signature");

               log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
               String result = HttpKit.readData(request);
               log.info("支付通知密文 {}", result);

               // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
               String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,wxPayV3Bean.getApiKey3(), wxPayV3Bean.getPlatformCertPath());

               log.info("支付通知明文 {}", plainText);

               if (StrUtil.isNotEmpty(plainText)) {
                   response.setStatus(200);
                   map.put("code", "SUCCESS");
                   map.put("message", "SUCCESS");
              } else {
                   response.setStatus(500);
                   map.put("code", "ERROR");
                   map.put("message", "签名错误");
              }
               response.setHeader("Content-type", ContentType.JSON.toString());
               response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
               response.flushBuffer();
          } catch (Exception e) {
               e.printStackTrace();
          }
      }

     

  •  

posted on 2021-02-05 21:44  紫玉坏小子  阅读(954)  评论(0编辑  收藏  举报