微信小程序调用微信支付接口的实现方法


Posted in Javascript onApril 29, 2019

前言:应项目要求,需要使用微信小程序做支付,写完后告知手续费太高方案不予通过(宝宝心里苦,但宝宝不说)。此次开发在因站在巨人的肩膀上顺利完成。

微信支付文档传送门:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3

1.开发工具:

Eclipse+Tomcat+微信web开发工具

2.开发环境:

java+maven

3.开发前准备:

3.1 所需材料

小程序的appid,APPsecret,支付商户号(mch_id),商户密钥(key),付款用户的openid。

申请接入微信商户地址:https://pay.weixin.qq.com/static/applyment_guide/applyment_detail_miniapp.shtml

3.2 开发模式

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

微信小程序调用微信支付接口的实现方法

4 开发

wx.request({
 
  url: address + 'wxPay',
 
  data: {
 
    openId: openId
  
    // amount: amount,
 
    // openId: openId
 
  },
 
  header: {
 
    'content-type': 'application/x-www-form-urlencoded' // 默认值
  
  },
 
  method: "POST",
 
  success: function (res) {
 
    console.log(res);
 
    that.doWxPay(res.data);
  },
 
  fail: function (err) {
 
    wx.showToast({
 
      icon: "none",
 
      title: '服务器异常,清稍候再试'
 
    })
 
  },
 
});
 
 
 
doWxPay(param) {
 
//小程序发起微信支付
 
wx.requestPayment({
 
timeStamp: param.data.timeStamp,//记住,这边的timeStamp一定要是字符串类型的,不然会报错
 
nonceStr: param.data.nonceStr,
 
package: param.data.package,
 
signType: 'MD5',
 
paySign: param.data.paySign,
 
success: function (event) {
 
// success
 
console.log(event);
 
 
 
wx.showToast({
 
title: '支付成功',
 
icon: 'success',
 
duration: 2000
 
});
 
},
 
fail: function (error) {
 
// fail
 
console.log("支付失败")
 
console.log(error)
 
},
 
complete: function () {
 
// complete
 
console.log("pay complete")
 
}
 
});
 
},

4.2 java后台

4.2.1 PayUtil.java

private static Logger logger = Logger.getLogger(PayUtil.class);
public static JSONObject wxPay(String openid,HttpServletRequest request){
JSONObject json = new JSONObject();
        try{
            //生成的随机字符串
            String nonce_str = Util.getRandomStringByLength(32);
            //商品名称 
            String body = new String(WXConst.title.getBytes("ISO-8859-1"),"UTF-8");
            //获取本机的ip地址
            String spbill_create_ip = Util.getIpAddr(request);
            String orderNo = WXConst.orderNo;
            String money = "1";//支付金额,单位:分,这边需要转成字符串类型,否则后面的签名会失败
 
            Map<String, String> packageParams = new HashMap<String, String>();
            packageParams.put("appid", WXConst.appId);
            packageParams.put("mch_id", WXConst.mch_id);
            packageParams.put("nonce_str", nonce_str);
            packageParams.put("body", body);
            packageParams.put("out_trade_no", orderNo);//商户订单号
            packageParams.put("total_fee", money);
            packageParams.put("spbill_create_ip", spbill_create_ip);
            packageParams.put("notify_url", WXConst.notify_url);
            packageParams.put("trade_type", WXConst.TRADETYPE);
            packageParams.put("openid", openid);
 
 
            // 除去数组中的空值和签名参数
            packageParams = PayUtil.paraFilter(packageParams);
            String prestr = PayUtil.createLinkString(packageParams); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
 
 
            //MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
            String mysign = PayUtil.sign(prestr, WXConst.key, "utf-8").toUpperCase();
            logger.info("=======================第一次签名:" + mysign + "=====================");
 
 
            //拼接统一下单接口使用的xml数据,要将上一步生成的签名一起拼接进去
            String xml = "<xml version='1.0' encoding='gbk'>" + "<appid>" + WXConst.appId + "</appid>"
                    + "<body><![CDATA[" + body + "]]></body>"
                    + "<mch_id>" + WXConst.mch_id + "</mch_id>"
                    + "<nonce_str>" + nonce_str + "</nonce_str>"
                    + "<notify_url>" + WXConst.notify_url + "</notify_url>"
                    + "<openid>" + openid + "</openid>"
                    + "<out_trade_no>" + orderNo + "</out_trade_no>"
                    + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"
                    + "<total_fee>" + money + "</total_fee>"
                    + "<trade_type>" + WXConst.TRADETYPE + "</trade_type>"
                    + "<sign>" + mysign + "</sign>"
                    + "</xml>";
 
 
            System.out.println("调试模式_统一下单接口 请求XML数据:" + xml);
 
 
            //调用统一下单接口,并接受返回的结果
            String result = PayUtil.httpRequest(WXConst.pay_url, "POST", xml);
 
 
            System.out.println("调试模式_统一下单接口 返回XML数据:" + result);
 
 
            // 将解析结果存储在HashMap中
            Map map = PayUtil.doXMLParse(result);
 
 
            String return_code = (String) map.get("return_code");//返回状态码
 
 
            //返回给移动端需要的参数
            Map<String, Object> response = new HashMap<String, Object>();
            if(return_code == "SUCCESS" || return_code.equals(return_code)){
                // 业务结果
                String prepay_id = (String) map.get("prepay_id");//返回的预付单信息
                response.put("nonceStr", nonce_str);
                response.put("package", "prepay_id=" + prepay_id);
                Long timeStamp = System.currentTimeMillis() / 1000;
                response.put("timeStamp", timeStamp + "");//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
 
 
                String stringSignTemp = "appId=" + WXConst.appId + "&nonceStr=" + nonce_str + "&package=prepay_id=" + prepay_id+ "&signType=" + WXConst.SIGNTYPE + "&timeStamp=" + timeStamp;
                //再次签名,这个签名用于小程序端调用wx.requesetPayment方法
                String paySign = PayUtil.sign(stringSignTemp, WXConst.key, "utf-8").toUpperCase();
                logger.info("=======================第二次签名:" + paySign + "=====================");
 
 
                response.put("paySign", paySign);
 
 
                //更新订单信息
                //业务逻辑代码
            }
 
 
            response.put("appid", WXConst.appId);
            json.put("errMsg", "OK");
            //json.setSuccess(true);
            json.put("data", response);
            //json.setData(response);
        }catch(Exception e){
            e.printStackTrace();
            json.put("errMsg", "Failed");
            //json.setSuccess(false);
            //json.setMsg("发起失败");
        }
        return json;
    }
 
 
 
/**  
     * 签名字符串  
     * @param text需要签名的字符串  
     * @param key 密钥  
     * @param input_charset编码格式  
     * @return 签名结果  
     */   
    public static String sign(String text, String key, String input_charset) {   
        text = text + "&key=" + key;   
        return DigestUtils.md5Hex(getContentBytes(text, input_charset));   
    }   
    /**  
     * 签名字符串  
     * @param text需要签名的字符串  
     * @param sign 签名结果  
     * @param key密钥  
     * @param input_charset 编码格式  
     * @return 签名结果  
     */   
    public static boolean verify(String text, String sign, String key, String input_charset) {   
        text = text + key;   
        String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));   
        if (mysign.equals(sign)) {   
            return true;   
        } else {   
            return false;   
        }   
    }   
    /**  
     * @param content  
     * @param charset  
     * @return  
     * @throws SignatureException  
     * @throws UnsupportedEncodingException  
     */   
    public static byte[] getContentBytes(String content, String charset) {   
        if (charset == null || "".equals(charset)) {   
            return content.getBytes();   
        }   
        try {   
            return content.getBytes(charset);   
        } catch (UnsupportedEncodingException e) {   
            throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);   
        }   
    }   
    /**  
     * 生成6位或10位随机数 param codeLength(多少位)  
     * @return  
     */   
    public static String createCode(int codeLength) {   
        String code = "";   
        for (int i = 0; i < codeLength; i++) {   
            code += (int) (Math.random() * 9);   
        }   
        return code;   
    }   
    @SuppressWarnings("unused")
private static boolean isValidChar(char ch) {   
        if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))   
            return true;   
        if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))   
            return true;// 简体中文汉字编码   
        return false;   
    }   
    /**  
     * 除去数组中的空值和签名参数  
     * @param sArray 签名参数组  
     * @return 去掉空值与签名参数后的新签名参数组  
     */   
    public static Map<String, String> paraFilter(Map<String, String> sArray) {   
        Map<String, String> result = new HashMap<String, String>();   
        if (sArray == null || sArray.size() <= 0) {   
            return result;   
        }   
        for (String key : sArray.keySet()) {   
            String value = sArray.get(key);   
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")   
                    || key.equalsIgnoreCase("sign_type")) {   
                continue;   
            }   
            result.put(key, value);   
        }   
        return result;   
    }   
    /**  
     * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串  
     * @param params 需要排序并参与字符拼接的参数组  
     * @return 拼接后字符串  
     */   
    public static String createLinkString(Map<String, String> params) {   
        List<String> keys = new ArrayList<String>(params.keySet());   
        Collections.sort(keys);   
        String prestr = "";   
        for (int i = 0; i < keys.size(); i++) {   
            String key = keys.get(i);   
            String value = params.get(key);   
            if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符   
                prestr = prestr + key + "=" + value;   
            } else {   
                prestr = prestr + key + "=" + value + "&";   
            }   
        }   
        return prestr;   
    }   
    /**  
     *  
     * @param requestUrl请求地址  
     * @param requestMethod请求方法  
     * @param outputStr参数  
     */   
    public static String httpRequest(String requestUrl,String requestMethod,String outputStr){   
        // 创建SSLContext   
        StringBuffer buffer = null;   
        try{   
        URL url = new URL(requestUrl);   
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();   
        conn.setRequestMethod(requestMethod);   
        conn.setDoOutput(true);   
        conn.setDoInput(true);   
        conn.connect();   
        //往服务器端写内容   
        if(null !=outputStr){   
            OutputStream os=conn.getOutputStream();   
            os.write(outputStr.getBytes("utf-8"));   
            os.close();   
        }   
        // 读取服务器端返回的内容   
        InputStream is = conn.getInputStream();   
        InputStreamReader isr = new InputStreamReader(is, "utf-8");   
        BufferedReader br = new BufferedReader(isr);   
        buffer = new StringBuffer();   
        String line = null;   
        while ((line = br.readLine()) != null) {   
        buffer.append(line);   
        }   
        br.close();
        }catch(Exception e){   
            e.printStackTrace();   
        }
        return buffer.toString();
    }     
    public static String urlEncodeUTF8(String source){   
        String result=source;   
        try {   
            result=java.net.URLEncoder.encode(source, "UTF-8");   
        } catch (UnsupportedEncodingException e) {   
            // TODO Auto-generated catch block   
            e.printStackTrace();   
        }   
        return result;   
    } 
    /**
* 解析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 static void wxNotify(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);
        }
        br.close();
        //sb为微信返回的xml
        String notityXml = sb.toString();
        String resXml = "";
        System.out.println("接收到的报文:" + notityXml);
 
 
        Map map = PayUtil.doXMLParse(notityXml);
 
 
        String returnCode = (String) map.get("return_code");
        if("SUCCESS".equals(returnCode)){
            //验证签名是否正确
            if(PayUtil.verify(PayUtil.createLinkString(map), (String)map.get("sign"), WXConst.key, "utf-8")){
                /**此处添加自己的业务逻辑代码start**/
 
 
 
 
                /**此处添加自己的业务逻辑代码end**/
 
 
                //通知微信服务器已经支付成功
                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> ";
        }
        System.out.println(resXml);
        System.out.println("微信支付回调数据结束");
 
 
        BufferedOutputStream out = new BufferedOutputStream(
                response.getOutputStream());
        out.write(resXml.getBytes());
        out.flush();
        out.close();  
    }

4.2.2 Util.java

/**
     * Util工具类方法
     * 获取一定长度的随机字符串,范围0-9,a-z
     * @param length:指定字符串长度
     * @return 一定长度的随机字符串
     */
    public static String getRandomStringByLength(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        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();
    }
    
    
    /**
     * Util工具类方法
     * 获取真实的ip地址
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if(index != -1){
                return ip.substring(0,index);
            }else{
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
            return ip;
        }
        return request.getRemoteAddr();
 
    }

4.2.3 WXConst.java

//微信小程序appid
public static String appId = "";
//微信小程序appsecret
public static String appSecret = "";
//微信支付主体
public static String title = "";
public static String orderNo = "";
//微信商户号
public static String mch_id="";
//微信支付的商户密钥
public static final String key = "";
//获取微信Openid的请求地址
public static String WxGetOpenIdUrl = "";
//支付成功后的服务器回调url
public static final String notify_url="https://api.weixin.qq.com/sns/jscode2session";
//签名方式
public static final String SIGNTYPE = "MD5";
//交易类型
public static final String TRADETYPE = "JSAPI";
//微信统一下单接口地址
public static final String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

5 可能出现的问题

5.1 商户号

会出现一个什么异常我忘了,重置一下微信商户密钥就好了

5.2 中文参数

String body = new String(WXConst.title.getBytes("ISO-8859-1"),"UTF-8");

这行很重要,如果报参数索引-2异常,很可能是出现了中文,对中文进行如上处理即可通过。

5.3 invalid spbill_create_ip

使用微信web开发工具直接测试的,出现了这个问题,调试记得用真机哦。

整个小程序前后端一个人开发,测试成功上线前夕又嫌弃微信支付收取的手续费(0.6%)太高,结算周期(T+7)太长,所以就被无情抛弃了,这个月项目重启(2018-11)和工商银行达成一致,直接转账到对公账户,目前项目进展顺利已上线。改需求请先扫码(小声bb)

以上所述是小编给大家介绍的微信小程序调用微信支付接口详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
javascript textContent与innerText的异同分析
Oct 22 Javascript
把字符串按照特定的字母顺序进行排序的js代码
Jan 28 Javascript
解决Jquery鼠标经过不停滑动的问题
Mar 03 Javascript
jquery实现向下滑出的二级导航下滑菜单效果
Aug 25 Javascript
javascript基本算法汇总
Mar 09 Javascript
VUE多层路由嵌套实现代码
May 15 Javascript
集成vue到jquery/bootstrap项目的方法
Feb 10 jQuery
Js面试算法详解
Apr 08 Javascript
Vue点击切换颜色的方法
Sep 13 Javascript
浅谈vue.use()方法从源码到使用
May 12 Javascript
深入浅析ng-bootstrap 组件集中 tabset 组件的实现分析
Jul 19 Javascript
vue页面引入three.js实现3d动画场景操作
Aug 10 Javascript
vue实现微信分享链接添加动态参数的方法
Apr 29 #Javascript
VueJs里利用CryptoJs实现加密及解密的方法示例
Apr 29 #Javascript
微信小程序地图导航功能实现完整源代码附效果图(推荐)
Apr 28 #Javascript
详解vue 路由跳转四种方式 (带参数)
Apr 28 #Javascript
使用vue完成微信公众号网页小记(推荐)
Apr 28 #Javascript
详解VUE项目中安装和使用vant组件
Apr 28 #Javascript
详解JavaScript对数组操作(添加/删除/截取/排序/倒序)
Apr 28 #Javascript
You might like
关于文本留言本的分页代码
2006/10/09 PHP
[原创]php实现 data url的图片生成与保存
2016/12/04 PHP
老生常谈PHP数组函数array_merge(必看篇)
2017/05/25 PHP
laravel实现图片上传预览,及编辑时可更换图片,并实时变化的例子
2019/11/14 PHP
PHP 实现重载
2021/03/09 PHP
有效的捕获JavaScript焦点的方法小结
2009/10/08 Javascript
JavaScript版TAB选项卡效果实例
2013/08/16 Javascript
js特殊字符转义介绍
2013/11/05 Javascript
jQuery多媒体插件jQuery Media Plugin使用详解
2014/12/19 Javascript
JavaScript判断一个字符串是否包含指定子字符串的方法
2015/03/18 Javascript
JS实现新浪微博效果带遮罩层的弹出框代码
2015/10/12 Javascript
AngularJS入门教程之服务(Service)
2016/07/27 Javascript
angular.js之路由的选择方法
2016/09/24 Javascript
使用 jQuery.ajax 上传带文件的表单遇到的问题
2016/10/31 Javascript
JavaScript自动点击链接 防止绕过浏览器访问的方法
2017/01/19 Javascript
浅析bootstrap原理及优缺点
2017/03/19 Javascript
javascript计算渐变颜色的实例
2017/09/22 Javascript
Vue 2.0学习笔记之使用$refs访问Vue中的DOM
2017/12/19 Javascript
使用weixin-java-tools完成微信授权登录、微信支付的示例
2018/09/26 Javascript
js 递归json树实现根据子id查父id的方法分析
2019/11/08 Javascript
重置Redux的状态数据的方法实现
2019/11/18 Javascript
Vue请求java服务端并返回数据代码实例
2019/11/28 Javascript
vue.js中使用微信扫一扫解决invalid signature问题(完美解决)
2020/04/11 Javascript
一个基于flask的web应用诞生 bootstrap框架美化(3)
2017/04/11 Python
用python处理图片实现图像中的像素访问
2018/05/04 Python
Python可视化工具如何实现动态图表
2020/10/23 Python
基于tensorflow __init__、build 和call的使用小结
2021/02/26 Python
服装销售人员求职自我评价
2013/09/26 职场文书
物业管理专业求职信
2014/06/11 职场文书
科技活动周标语
2014/10/08 职场文书
2014年社区计生工作总结
2014/11/18 职场文书
2014年城管工作总结
2014/11/20 职场文书
2014年终工作总结范本
2014/12/15 职场文书
信用卡催款律师函
2015/05/27 职场文书
如何用Python搭建gRPC服务
2021/06/30 Python
面试中老生常谈的MySQL问答集锦夯实基础
2022/03/13 MySQL