玩轉小程序支付之付款(統一下單)

小程序的業務流程以下php

商戶系統和微信支付系統主要交互說明:java

步驟1:用戶在商戶APP中選擇商品,提交訂單,選擇微信支付。git

步驟2:商戶後臺收到用戶支付單,調用微信支付統一下單接口。參見【統一下單API】。redis

步驟3:統一下單接口返回正常的prepay_id,再按簽名規範從新生成簽名後,將數據傳輸給APP。參與簽名的字段名爲appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式爲Sign=WXPay算法

步驟4:商戶APP調起微信支付。api參見本章節【app端開發步驟說明apache

步驟5:商戶後臺接收支付通知。api參見【支付結果通知APIjson

步驟6:商戶後臺查詢支付結果。,api參見【查詢訂單API小程序

API連接:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_3api

 

支付的流程爲:先調用統一下單API---->接着在小程序wx.requestPayment發起支付---->支付完以後會調用支付結果通知微信

小程序端代碼

/**
  * 支付
  */
var pay = function (event, that) {
  if (that.data.detail.fee.indexOf("免費")>-1){
    goApply(event, that)
  }else{
    wx.request({
      url: app.globalData.server + 'requestPay/',
      method: "POST",
      data: {
        activityId: event.currentTarget.dataset.activityid,
        userId: app.globalData.userInfo.id,
        sessionThirdKey: wx.getStorageSync('sessionThirdKey'),
        money: that.data.detail.fee,
        describe: that.data.detail.name,
        detail: '報名活動費用'
      },
      header: {
        "Content-Type": "application/x-www-form-urlencoded"
      },
      success: function (res) {
        console.info(res);
        //發起微信支付
        wx.requestPayment({
          'timeStamp': res.data.timeStamp,
          'nonceStr': res.data.nonceStr,
          'package': res.data.package_,
          'signType': 'MD5',
          'paySign': res.data.paySign,
          success: function (res) {
            console.info(res)
            //報名
            goApply(event, that)
          },
          fail: function (res) {
            console.info(res)
          },
          complete: function (res) {
            console.info(res)
          }
        })
      }
    })
  }
  }

後臺Java代碼:(基於SpringBoot)

@RestController
public class PayApi {
    @Value("${wxapp.appid}")
    private String appId;
    @Value("${wxapp.secret}")
    private String secret;
    @Value("${wxapp.url.jscode2session}")
    private String jscode2session;
    @Value("${wx.mch.id}")
    private String mchId;
    @Value("${wx.unified.order.url}")
    private String createOrderURL;
    @Value("${wx.pay.api.key}")
    private String key;
    @Value("${wx.bill.create.ip}")
    private String spBillCreateIp;
    @Value("${wx.server.url}")
    private String baseUrl;
    @Autowired
    private RedisClient redisClient;
    @Resource(name = "wxappUserServiceImpl")
    private IWxappUserService wxappUserService;
    @Resource(name = "wxappActivityServiceImpl")
    private IWxappActivityService wxappActivityService;
    @Resource(name = "wxappActivityApplyServiceImpl")
    private IWxappActivityApplyService wxappActivityApplyService;
    @Resource(name = "wxappPayServiceImpl")
    private IWxappPayService wxappPayService;
    @RequestMapping(value = "/requestPay",method = RequestMethod.POST)
    public WxappPayDto requestPay(String userId, String activityId, String sessionThirdKey, String money, String describe, String detail) throws Exception {
        WxappPayDto dto = new WxappPayDto();
        //獲取保存的sessionThirdKey(裏面保存了openId)
        String sessionKey = redisClient.get(sessionThirdKey);
        String openId = sessionKey.split("w#w#w")[0];
        //訂單號
        String orderNo="wx"+userId+"_"+System.currentTimeMillis();
        dto = prePay(userId,activityId,openId,orderNo,money,describe,detail);  
        return dto;
    }
    
    /**
     * 統一下單
     * @param userId
     * @param activityId
     * @param openId
     * @param orderNo
     * @param money
     * @param describe
     * @param detail
     * @return
     */
    private WxappPayDto prePay(String userId,String activityId,String openId,String orderNo,String money,String describe,String detail){
        money = String.valueOf(Long.valueOf(money.substring(0, money.length()-1))*100);
        String currTime = PayUtils.getCurrTime();
        //8位日期
        String strTime = currTime.substring(8, currTime.length());
        //四位隨機數
        String strRandom = PayUtils.buildRandom(4) + "";
        //10位序列號,能夠自行調整。
        String nonceStr = strTime + strRandom;
        //這裏notify_url是 支付完成後微信發給該連接信息,能夠判斷會員是否支付成功,改變訂單狀態等。
        String notifyUrl = baseUrl+"/notify";
        //附加數據,以必定格式保存userId和activityId。原樣返回。
        String attach = userId+"#wx#"+activityId;

        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("appid", appId);
        packageParams.put("attach", attach);//附加數據
        packageParams.put("body", describe);//商品描述
        packageParams.put("detail", detail);
        packageParams.put("mch_id", mchId);//商戶號
        packageParams.put("nonce_str", nonceStr);//隨機數
        packageParams.put("notify_url", notifyUrl);
        packageParams.put("openid", openId);
        packageParams.put("out_trade_no", orderNo);//商戶訂單號
        packageParams.put("spbill_create_ip", spBillCreateIp);//訂單生成的機器 IP
        packageParams.put("total_fee", money);//總金額
        packageParams.put("trade_type", "JSAPI");  
        
        String sign = PayUtils.createSign(packageParams,key);
        String xml="<xml>"+
                "<appid>"+appId+"</appid>"+
                "<attach>"+attach+"</attach>"+
                "<body><![CDATA["+describe+"]]></body>"+
                "<detail><![CDATA["+detail+"]]></detail>"+
                "<mch_id>"+mchId+"</mch_id>"+
                "<nonce_str>"+nonceStr+"</nonce_str>"+
                "<sign>"+sign+"</sign>"+
                "<notify_url>"+notifyUrl+"</notify_url>"+
                "<openid>"+openId+"</openid>"+
                "<out_trade_no>"+orderNo+"</out_trade_no>"+
                "<spbill_create_ip>"+spBillCreateIp+"</spbill_create_ip>"+
                "<total_fee>"+money+"</total_fee>"+
                "<trade_type>JSAPI</trade_type>"+
                "</xml>";
        String prepay_id="";
        try {
            prepay_id = PayUtils.getPayNo(createOrderURL, xml);
            if(prepay_id.equals("")){
                //錯誤提示
                System.out.println("統一支付接口獲取預支付訂單出錯");
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        SortedMap<String, String> finalpackage = new TreeMap<String, String>();
        String timestamp = PayUtils.getTimeStamp();
        String packages = "prepay_id="+prepay_id;
        finalpackage.put("appId", appId);
        finalpackage.put("nonceStr", nonceStr); 
        finalpackage.put("package", packages);  
        finalpackage.put("signType", "MD5");
        finalpackage.put("timeStamp", timestamp);  
        String finalsign = PayUtils.createSign(finalpackage,key);
        
        WxappPayDto dto = new WxappPayDto();
        dto.setNonceStr(nonceStr);
        dto.setPackage_(packages);
        dto.setPaySign(finalsign);
        dto.setSignType("MD5");
        dto.setTimeStamp(timestamp);
        return dto;
    }
    
    /**
     * 支付完成通知
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/notify",method = RequestMethod.POST)
    public String notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream)request.getInputStream()));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while((line = br.readLine())!=null){
            sb.append(line);
        }
        //解析並給微信發回收到通知確認
        Map map =  PayUtils.doXMLParse(sb.toString());
        String returnCode = map.get("return_code").toString();
        if(returnCode.equals("SUCCESS")){
            String resultCode = map.get("result_code").toString();
            if(resultCode.equals("SUCCESS")){
                SortedMap<String, String> packageParams = new TreeMap<String, String>();
                packageParams.put("appid", map.get("appid").toString());
                packageParams.put("attach", map.get("attach").toString());
                packageParams.put("bank_type", map.get("bank_type").toString());
                packageParams.put("cash_fee", map.get("cash_fee").toString());
                packageParams.put("fee_type", map.get("fee_type").toString());
                packageParams.put("is_subscribe", map.get("is_subscribe").toString());
                packageParams.put("mch_id", map.get("mch_id").toString());
                packageParams.put("nonce_str", map.get("nonce_str").toString());
                packageParams.put("openid", map.get("openid").toString());
                packageParams.put("out_trade_no", map.get("out_trade_no").toString());
                packageParams.put("result_code", map.get("result_code").toString());
                packageParams.put("return_code", map.get("return_code").toString()); 
                packageParams.put("time_end", map.get("time_end").toString());
                packageParams.put("total_fee", map.get("total_fee").toString());
                packageParams.put("trade_type", map.get("trade_type").toString());
                packageParams.put("transaction_id", map.get("transaction_id").toString());
                String sign = PayUtils.createSign(packageParams,key);
                String originSign = map.get("sign").toString();
                if(sign.equals(originSign)){
                    //簽名一致,保存支付流水
                    String xml="<xml>"
                              +"<return_code>SUCCESS</return_code>"
                              +"<return_msg>OK</return_msg>"
                              +"</xml>";
                    ShopPayLog payLog = new ShopPayLog();
                    payLog.setCreatedAt(new Date());
                    payLog.setSource(Source.WeiXin);
                    DecimalFormat df = new DecimalFormat("######0.00"); 
                    payLog.setTotalFee(String.valueOf(df.format((Double.valueOf(map.get("total_fee").toString())/100))));
                    payLog.setTradeNo(map.get("out_trade_no").toString());
                    payLog.setTransactionId(map.get("transaction_id").toString());
                    String attach = map.get("attach").toString();//userId+"#wx#"+activityId
                    payLog.setUserId(attach.split("#wx#")[0]);
                    payLog = wxappPayService.save(payLog);
                    WxappUser user = wxappUserService.find(Long.valueOf(attach.split("#wx#")[0]));
                    WxappActivity activity = wxappActivityService.find(Long.valueOf(attach.split("#wx#")[1]));
                    WxappActivityApply activityApply = wxappActivityApplyService.findActivityApplyByUserAndActivity(user, activity);
                    //在活動申請表中關聯上支付流水的id
                    activityApply.setPayLogId(String.valueOf(payLog.getId()));
                    wxappActivityApplyService.save(activityApply);
                    return xml;
                }else{
                    String xml="<xml>"
                                +"<return_code>FAIL</return_code>"
                                +"<return_msg>簽名不一致</return_msg>"
                                +"</xml>";
                      return xml;
                }
            }else{
                String xml="<xml>"
                          +"<return_code>FAIL</return_code>"
                          +"<return_msg>支付通知失敗</return_msg>"
                          +"</xml>";
                        return xml;
            }
        } else {
            String xml="<xml>"
                    +"<return_code>FAIL</return_code>"
                    +"<return_msg>支付通知失敗</return_msg>"
                    +"</xml>";
                  return xml;
        }
    }

PayUtils.java

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

import com.pro.profwxappapi.api.PayApi;

@SuppressWarnings("deprecation")
public class PayUtils {
    private static Object Server;
    @SuppressWarnings("deprecation")
    public static DefaultHttpClient httpclient;
    private static SortedMap parameters;
    
    static {
        httpclient = new DefaultHttpClient();
        httpclient = (DefaultHttpClient) HttpClientConnectionManager.getSSLInstance(httpclient);
        parameters = new TreeMap();
    }

    /**
     * 把對象轉換成字符串
     * 
     * @param obj
     * @return String 轉換成字符串,若對象爲null,則返回空字符串.
     */
    public static String toString(Object obj) {
        if (obj == null)
            return "";

        return obj.toString();
    }

    /**
     * 把對象轉換爲int數值.
     * 
     * @param obj
     *            包含數字的對象.
     * @return int 轉換後的數值,對不能轉換的對象返回0。
     */
    public static int toInt(Object obj) {
        int a = 0;
        try {
            if (obj != null) {
                a = Integer.parseInt(obj.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return a;
    }

    /**
     * 獲取從1970年開始到如今的秒數
     * 
     * @param date
     * @return
     */
    public static String getTimeStamp() {
        long seconds = System.currentTimeMillis() / 1000;
        return String.valueOf(seconds);
    }

    /**
     * 獲取當前時間 yyyyMMddHHmmss
     * @return String
     */
    public static String getCurrTime() {
        Date now = new Date();
        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String s = outFormat.format(now);
        return s;
    }

    /**
     * 獲取當前日期 yyyyMMdd
     * @param date
     * @return String
     */
    public static String formatDate(Date date) {
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
        String strDate = formatter.format(date);
        return strDate;
    }

    /**
     * 取出一個指定長度大小的隨機正整數.
     * @param length  int 設定所取出隨機數的長度。length小於11
     * @return int 返回生成的隨機數。
     */
    public static int buildRandom(int length) {
        int num = 1;
        double random = Math.random();
        if (random < 0.1) {
            random = random + 0.1;
        }
        for (int i = 0; i < length; i++) {
            num = num * 10;
        }
        return (int) ((random * num));
    }

    /**
     * 獲取編碼字符集
     * @param request
     * @param response
     * @return String
     */

    public static String getCharacterEncoding(HttpServletRequest request, HttpServletResponse response) {

        if (null == request || null == response) {
            return "utf-8";
        }
        String enc = request.getCharacterEncoding();
        if (null == enc || "".equals(enc)) {
            enc = response.getCharacterEncoding();
        }
        if (null == enc || "".equals(enc)) {
            enc = "utf-8";
        }
        return enc;
    }

    public static String URLencode(String content) {
        String URLencode;
        URLencode = replace(Server.equals(content), "+", "%20");
        return URLencode;
    }

    private static String replace(boolean equals, String string, String string2) {
        return null;
    }

    /**
     * 獲取unix時間,從1970-01-01 00:00:00開始的秒數
     * @param date
     * @return long
     */
    public static long getUnixTime(Date date) {
        if (null == date) {
            return 0;
        }
        return date.getTime() / 1000;
    }

    public static String QRfromGoogle(String chl) {
        int widhtHeight = 300;
        String EC_level = "L";
        int margin = 0;
        String QRfromGoogle;
        chl = URLencode(chl);
        QRfromGoogle = "http://chart.apis.google.com/chart?chs=" + widhtHeight + "x" + widhtHeight + "&cht=qr&chld="
                + EC_level + "|" + margin + "&chl=" + chl;
        return QRfromGoogle;
    }

    /**
     * 時間轉換成字符串
     * @param date  時間
     * @param formatType  格式化類型
     * @return String
     */
    public static String date2String(Date date, String formatType) {
        SimpleDateFormat sdf = new SimpleDateFormat(formatType);
        return sdf.format(date);
    }

    public static String getNonceStr() {
        Random random = new Random();
        return MD5Utils.MD5Encode(String.valueOf(random.nextInt(10000)), "UTF-8");
    }

    /**
     * 建立簽名SHA1
     * @param signParams
     * @return
     * @throws Exception
     */
    public static String createSHA1Sign(SortedMap<String, String> signParams) throws Exception {
        StringBuffer sb = new StringBuffer();
        Set es = signParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            sb.append(k + "=" + v + "&");
            // 要採用URLENCODER的原始值!
        }
        String params = sb.substring(0, sb.lastIndexOf("&"));
        return getSha1(params);
    }

    /**
     * Sha1簽名
     * @param str
     * @return
     */
    public static String getSha1(String str) {
        if (str == null || str.length() == 0) {
            return null;
        }
        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
        try {
            MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
            mdTemp.update(str.getBytes("UTF-8"));

            byte[] md = mdTemp.digest();
            int j = md.length;
            char buf[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
                buf[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(buf);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 得到預支付訂單號
     * @param url
     * @param xmlParam
     * @return
     */
    public static String getPayNo(String url, String xmlParam) {
        String prepay_id = "";
        try {
            String jsonStr = postWithXmlParams(url, xmlParam);
            if (jsonStr.indexOf("FAIL") != -1) {
                return prepay_id;
            }
            Map<String, Object> map = doXMLParse(jsonStr);
            prepay_id = (String) map.get("prepay_id");
            System.out.println("prepay_id:" + prepay_id);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return prepay_id;
    }

    /**
     * 發送請求
     * @param url 請求路徑
     * @param xmlParams xml字符串
     * @return
     */
    public static String postWithXmlParams(String url, String xmlParams) {
        HttpPost httpost = HttpClientConnectionManager.getPostMethod(url);
        try {
            httpost.setEntity(new StringEntity(xmlParams, "UTF-8"));
            HttpResponse response = httpclient.execute(httpost);
            return EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 解析xml,返回第一級元素鍵值對。若是第一級元素有子節點,則此節點的值是子節點的xml數據。
     * @param strxml
     * @return
     * @throws JDOMException
     * @throws IOException
     */
    public static Map doXMLParse(String strxml) throws Exception {
        if (null == strxml || "".equals(strxml)) {
            return null;
        }
        Map m = new HashMap();
        InputStream in = String2Inputstream(strxml);
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Element e = (Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }
            m.put(k, v);
        }
        // 關閉流
        in.close();
        return m;
    }

    /**
     * 獲取子結點的xml
     * @param children
     * @return String
     */
    public static String getChildrenText(List children) {
        StringBuffer sb = new StringBuffer();
        if (!children.isEmpty()) {
            Iterator it = children.iterator();
            while (it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if (!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }
        return sb.toString();
    }

    public static InputStream String2Inputstream(String str) {
        return new ByteArrayInputStream(str.getBytes());
    }

    public String getParameter(String parameter) {
        String s = (String) this.parameters.get(parameter);
        return (null == s) ? "" : s;
    }

    /**
     * 特殊字符處理
     * @param src
     * @return
     * @throws UnsupportedEncodingException
     */
    public String UrlEncode(String src) throws UnsupportedEncodingException {
        return URLEncoder.encode(src, "UTF-8").replace("+", "%20");
    }

    /**
     * 獲取package的簽名包
     * @param packageParams
     * @param key
     * @return
     * @throws UnsupportedEncodingException
     */
    public String genPackage(SortedMap<String, String> packageParams, String key) throws UnsupportedEncodingException {
        String sign = createSign(packageParams, key);

        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            sb.append(k + "=" + UrlEncode(v) + "&");
        }

        // 去掉最後一個&
        String packageValue = sb.append("sign=" + sign).toString();
        return packageValue;
    }

    /**
     * 建立md5摘要,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。
     */
    public static String createSign(SortedMap<String, String> packageParams, String key) {
        StringBuffer sb = new StringBuffer();
        Set es = packageParams.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + key);
        System.out.println("md5:" + sb.toString());
        String sign = MD5Utils.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
        System.out.println("packge簽名:" + sign);
        return sign;

    }

    /**
     * 建立package簽名
     */
    public boolean createMd5Sign(String signParams) {
        StringBuffer sb = new StringBuffer();
        Set es = this.parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k + "=" + v + "&");
            }
        }

        // 算出摘要
        String sign = MD5Utils.MD5Encode(sb.toString(), "utf-8").toLowerCase();
        String paySign = this.getParameter("sign").toLowerCase();
        return paySign.equals(sign);
    }

    /**
     * 輸出XML
     * @return
     */
    public String parseXML() {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set es = this.parameters.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (null != v && !"".equals(v) && !"appkey".equals(k)) {

                sb.append("<" + k + ">" + getParameter(k) + "</" + k + ">\n");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }
    
    public static String post(String url, String xmlParam){
        StringBuilder sb = new StringBuilder();
         try {
                KeyStore keyStore  = KeyStore.getInstance("PKCS12");
                FileInputStream instream = new FileInputStream(new File(PayApi.class.getClassLoader().getResource("apiclient_cert.p12").getPath()));
                try {
                    keyStore.load(instream, "1344023801".toCharArray());
                } finally {
                    instream.close();
                }
         
                // 證書
                SSLContext sslcontext = SSLContexts.custom()
                        .loadKeyMaterial(keyStore, "1344023801".toCharArray())
                        .build();
                // 只容許TLSv1協議
                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                        sslcontext,
                        new String[] { "TLSv1" },
                        null,
                        SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
                //建立基於證書的httpClient,後面要用到
                CloseableHttpClient client = HttpClients.custom()
                        .setSSLSocketFactory(sslsf)
                        .build();
                
             HttpPost httpPost = new HttpPost(url);//退款接口
             StringEntity  reqEntity  = new StringEntity(xmlParam);
             // 設置類型 
             reqEntity.setContentType("application/x-www-form-urlencoded"); 
             httpPost.setEntity(reqEntity);
             CloseableHttpResponse response = client.execute(httpPost);
             try {
                 HttpEntity entity = response.getEntity();
                 System.out.println(response.getStatusLine());
                 if (entity != null) {
                     BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
                     String text="";
                     while ((text = bufferedReader.readLine()) != null) {
                         sb.append(text);
                     }
                 }
                 EntityUtils.consume(entity);
             } catch(Exception e){
                 e.printStackTrace();
             }finally {
                 try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
             }
         } catch (Exception e) {
                e.printStackTrace();
        }finally {
             httpclient.close();
        }
         return sb.toString();
    }
}
public class WxappPayDto {
    private String appId;
    private String timeStamp;
    private String nonceStr;
    private String package_;
    private String signType;
    private String paySign;
    
    public String getAppId() {
        return appId;
    }
    public void setAppId(String appId) {
        this.appId = appId;
    }
    public String getTimeStamp() {
        return timeStamp;
    }
    public void setTimeStamp(String timeStamp) {
        this.timeStamp = timeStamp;
    }
    public String getNonceStr() {
        return nonceStr;
    }
    public void setNonceStr(String nonceStr) {
        this.nonceStr = nonceStr;
    }
    public String getPackage_() {
        return package_;
    }
    public void setPackage_(String package_) {
        this.package_ = package_;
    }
    public String getSignType() {
        return signType;
    }
    public void setSignType(String signType) {
        this.signType = signType;
    }
    public String getPaySign() {
        return paySign;
    }
    public void setPaySign(String paySign) {
        this.paySign = paySign;
    }
}

先從小程序端請求後臺,在後臺發出請求先得到prepay_id,而後再組裝成參數返回到小程序端,再在小程序端發出支付請求。

注意點:

1,算法https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3很重要,必定要遵照規範。這個算法彷佛全部微信支付都會用到。

2,參數https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4,必需的參數一個不能少,並且還要按字典順序。

相關文章
相關標籤/搜索