1. 程式人生 > >微信小程式支付功能,完成整個交易的思路和程式碼

微信小程式支付功能,完成整個交易的思路和程式碼

開發工具:微信開發者工具,Intellij idea 2018

框架:spring boot 

交易流程圖:

下面直接上程式碼:

1.在wxml新增一個支付按鈕,點選監聽payMethod方法

<view><button bindtap='payMethod'>支付</button></view>

2.payMethod方法種呼叫wx.login微信介面,獲取code。呼叫getOpenId方法

/**
   *  支付例子
   * const config = require('../../config')獲取全域性配置
   * const appInstance = getApp();獲取全域性上下文例項
   */
  payMethod(){
    let _this = this ;//當前上下文
    //獲取登入code  
    wx.login({
      success: result =>{
        console.info(result.code)
        //獲取openid 
        _this.getOpenId(result.code) 
      }
    }); 
  },

3.將code作為引數,請求Java服務,獲取openId

 /**
   * 用code作為引數請求Java服務,像微信服務換取openId,access_token
   * 此處用到openId,access_token暫時無用,為安全起見,此欄位不返回客戶端
   */
  getOpenId(code){
    let _this = this;//當前上下文
    wx.request({
      url: config.localServer + 'api/wc/jscode2session.wc',
      data: { code: code},
      method: 'POST',
      success: result =>{
        console.info('返回openId')
        console.info(result.data)
        _this.generateOrder(result.data.data.openid)
      },
      fail:() => {
        console.info('fail')
      },
      complete: () => {
        // complete 
      }
    }) 
  },

4.Java服務jscode2session介面,呼叫微信sns/jscode2session服務,獲取openId,將openid返回給小程式客戶端

/**
     * 換取 使用者唯一標識 OpenID 和 會話金鑰 session_key   ->>>  oopenid 和 session_key 組成會話標識,發給客戶端,客戶端憑藉這個標識進行通訊
     * 會話金鑰 session_key 是對使用者資料進行 加密簽名 的金鑰。
     * 為了應用自身的資料安全,開發者伺服器不應該把會話金鑰下發到小程式,也不應該對外提供這個金鑰。
     * 臨時登入憑證 code 只能使用一次
     * @param request
     * @param requestEntity
     * @param code
     * @return
     */
    @PostMapping("/jscode2session.wc")
    public ResponseBean jscode2session(HttpServletRequest request, HttpEntity<String> requestEntity,String code) {

        HttpMethod requestMethod = HttpMethod.resolve(request.getMethod());
        String body = requestEntity.getBody();
        Map<String,String> mapParam = JacksonUtil.fromJson(body,Map.class);
        code = mapParam.get("code");
        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        requestEntity = new HttpEntity<>(requestEntity.getBody(), headers);

        ResponseEntity<String> response = null;
        response = restTemplate.exchange(
                wcHost+"sns/jscode2session?appid=wxd0ad6e09b4f09a2a&secret=919f06b91b6adbe682c0bd9edc0a008f&js_code="+code+"&grant_type=authorization_code",
                requestMethod,
                requestEntity,
                String.class);

        String result =  response.getBody();
        LOGGER.info("result="+result);
        Map<String,String> mapResult = JacksonUtil.fromJson(result,Map.class);
        return ResponseBean.response(mapResult);

    }

5.小程式獲取openid,呼叫generateOrder方法,生成訂單資訊,呼叫Java服務payOrderPublic.wc介面,以獲取支付引數

/**
   * 用openid,在Java服務請求微信服務,獲取支付的請求引數
   */
  generateOrder(openid){
    var _this = this 

    var paymentPo={
      openid: openid,
      total_fee: '0.1',
      mch_id: 'mch_id',
      body: '支付測試',
      detail: 'detail',
      attach: '假酒'
    }

    wx.request({
      url: config.localServer + 'api/wc/payOrderPublic.wc',
      method: 'POST',
      data: paymentPo,
      success: result => {
        console.info(result)
        var pay = result.data.data
        //發起支付 
        var timeStamp = pay[0].timeStamp;
        console.info("timeStamp:" + timeStamp)
        var packages = pay[0].package;
        console.info("package:" + packages)
        var paySign = pay[0].paySign;
        console.info("paySign:" + paySign)
        var nonceStr = pay[0].nonceStr;
        console.info("nonceStr:" + nonceStr)
        var param = { "timeStamp": timeStamp, "package": packages, "paySign": paySign, "signType": "MD5", "nonceStr": nonceStr };
        _this.pay(param)
      },
    }) 
  },

6.Java服務payOrderPublic.wc介面,將引數生成簽名,打包成xml格式,請求微信的統一下單介面/pay/unifiedorder,將獲取的結果進行再次簽名,得到支付介面需要的引數,返回給小程式客戶端。

/**
     *  商戶server呼叫支付統一下單
//     * @param openid 使用者唯一標識
//     * @param mch_id 商品編號
//     * @param total_fee 商品價格
//     * @param body 商品描述
//     * @param detail 商品詳情
//     * @param attach 附加資料
//     * @param time_start 交易開始時間
//     * @param time_expire 交易結束時間
     * @return
     */
    @PostMapping("/payOrderPublic.wc")
    public ResponseBean  payOrderPublic(HttpServletRequest request,HttpEntity<String> requestEntity,PaymentPo paymentPo) throws UnsupportedEncodingException, DocumentException {

        paymentPo = JacksonUtil.fromJson(requestEntity.getBody(),PaymentPo.class);
        paymentPo.setBody(new String(paymentPo.getBody().getBytes("UTF-8"),"ISO-8859-1")) ;
//        String appid = "替換為自己的小程式ID";//小程式ID
        //當前時間
        String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        //生成8位隨機數
        String code = PayUtil.createCode(8);
        //商戶訂單號
        String out_trade_no = paymentPo.getMch_id()+today+code;//商戶訂單號
        String spbill_create_ip = HttpUtil.getIpAddress(request);//終端IP
        String notify_url = "http://www.weixin.qq.com/wxpay/pay.php";//通知地址
        String trade_type = "JSAPI";//交易型別

        paymentPo.setAppid(appid);
        //隨機字串 32位
        String str=UUIDHexGeneratorUtil.generate();
        paymentPo.setNonce_str(str);
        paymentPo.setOut_trade_no(out_trade_no);
        paymentPo.setSpbill_create_ip(spbill_create_ip);
        paymentPo.setNotify_url(notify_url);
        paymentPo.setTrade_type(trade_type);

        // 把請求引數放進hashmap
        Map<String,String> sParaTemp = new HashMap<String,String>();
        sParaTemp.put("appid", paymentPo.getAppid());
        sParaTemp.put("mch_id", paymentPo.getMch_id());
        sParaTemp.put("nonce_str", paymentPo.getNonce_str());
        sParaTemp.put("body",  paymentPo.getBody());
        sParaTemp.put("out_trade_no", paymentPo.getOut_trade_no());
        sParaTemp.put("total_fee",paymentPo.getTotal_fee());
        sParaTemp.put("spbill_create_ip", paymentPo.getSpbill_create_ip());
        sParaTemp.put("notify_url",paymentPo.getNotify_url());
        sParaTemp.put("trade_type", paymentPo.getTrade_type());
        sParaTemp.put("openid", paymentPo.getOpenid());

        // 除去map中的空值和簽名引數
        Map<String,String> sPara = PayUtil.paraFilter(sParaTemp);
        // 把陣列所有元素,按照“引數=引數值”的模式用“&”字元拼接成字串
        String prestr = PayUtil.createLinkString(sPara);
        String key = "&key=替換為商戶支付金鑰"; // 商戶支付金鑰
        //MD5運算生成簽名
        String mysign = PayUtil.sign(prestr, key, "utf-8").toUpperCase();
        paymentPo.setSign(mysign);
        //打包要傳送的xml
        String respXml = MessageUtil.messageToXML(paymentPo);
        // 列印respXml發現,得到的xml中有“__”不對,應該替換成“_”
        respXml = respXml.replace("__", "_");
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//統一下單API介面連結
        String param = respXml;
        String result =PayUtil.httpRequest(url, "POST", param);
        // 將解析結果儲存在HashMap中
        Map<String,String> map = new HashMap<String,String>();
        InputStream in = new ByteArrayInputStream(result.getBytes());
        // 讀取輸入流
        SAXReader reader = new SAXReader();
        Document document = reader.read(in);
        // 得到xml根元素
        Element root = document.getRootElement();
        // 得到根元素的所有子節點
        @SuppressWarnings("unchecked")
        List<Element> elementList = root.elements();
        for (Element element : elementList) {
            map.put(element.getName(), element.getText());
        }
        // 返回資訊
        String return_code = map.get("return_code");//返回狀態碼
        String return_msg = map.get("return_msg");//返回資訊
        System.out.println("return_msg"+return_msg);
        Map<String,Object> resultMap = new HashMap<String,Object>();
        if(return_code=="SUCCESS"||return_code.equals(return_code)){
            // 業務結果
            String prepay_id = map.get("prepay_id");//返回的預付單資訊
            String nonceStr=UUIDHexGeneratorUtil.generate();
            resultMap.put("nonceStr", nonceStr);
            resultMap.put("package", "prepay_id="+prepay_id);
            Long timeStamp= System.currentTimeMillis()/1000;
            resultMap.put("timeStamp", timeStamp+"");
            String stringSignTemp = "appId="+appid+"&nonceStr=" + nonceStr + "&package=prepay_id=" + prepay_id+ "&signType=MD5&timeStamp=" + timeStamp;
            //再次簽名
            String paySign=PayUtil.sign(stringSignTemp, "&key=替換為自己的金鑰", "utf-8").toUpperCase();
            resultMap.put("paySign", paySign);
        }
            return ResponseBean.response(resultMap) ;
    }

7.小程式端得到支付引數,用pay方法呼叫微信的支付介面,完成支付

/**
   * 支付的動作
   */
  pay(param){
    console.info("支付")
    console.info(param)
    wx.requestPayment({
      timeStamp: param.timeStamp,
      nonceStr: param.nonceStr,
      package: param.package,
      signType: param.signType,
      paySign: param.paySign,
      success: (result) => {
        console.info("支付")
        console.info(result)
     
      },
      fail: (result) => {
        console.info("支付失敗")
        console.info(result)
      },
      complete: () => {
        console.info("pay complete")
      }
    }) 
  }