微信公众号开发中遇到的问题——支付(二)

2024-08-28 07:08

本文主要是介绍微信公众号开发中遇到的问题——支付(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第一次开发微信公众号,也是第一次接触微信公众号的支付,我使用的是jssdk,用h5页面调用的支付,后台使用的是java。首先声明,我不是一个愤世嫉俗的人,也不喜欢吐槽,我认为别人提供接口就已经很不错了,干嘛要吐槽呢?但是,这一次,我不得不说,微信公众号支付的文档真是渣!!!你没有也就算了,既然有了,为何不好好写?就好像别人问你路该怎么走,你给别人指了路,但指偏了。下面我记录一下我这次微信支付开发中遇到的坑,希望能给大家一点帮助。

开始之前,有几个地方需要配置一下(比较容易遗忘):
1、js接口安全域名

公众号设置————功能设置




2、微信支付中的开发配置




支付授权目录:看了文档之后,我以为是jsp页面的地址,最后,才发现原来是调用后台方法的地址,只是不用写到方法名,如果你调用支付的方法是www.XXX.com/pay/prepay.action;那么你的支付授权目录应该是:www.XXX.com/pay/
测试授权目录:就是测试环境的授权目录。可能有些人不知道支付怎么测试,那么,你需要做两点:①调用支付方法的链接地址。②把该链接地址发送到测试公众号的聊天记录里面。注意是你自己测试微信公众号的链接地址中。
测试白名单:测试微信公众号支付时,把你们自己的微信账号添加到测试白名单中;

3、我想吐槽一下,我的支付签名有问题(后来换ios系统测试时弹出框弹出来的),但是我之前用android手机一直测试,都报的是get_brand_wcpay_request:fail或chooseWXPay:fail(取决于你用哪种方法),你根本不知道究竟是什么错误。

4、当我要调用支付接口时,打开商户的微信公众号支付文档,发现了一个H5调起的支付API,我本以为这里的方法应该是chooseWXPay,但是为什么是function onBridgeReady。。。。。。这又是什么鬼?彻底把我搞晕了,有没有?后来查资料才知道,两个方法的本质是一样的,新版本的微信都会使用chooseWXPay方法,chooseWXPay方法中封装了onBridgeReady这东西,但是用onBridgeReady不用引用http://res.wx.qq.com/open/js/jweixin-1.0.0.js文件,用chooseWXPay方法方法需要引用js文件方法,因为chooseWXPay方法封装了onBridgeReady,查看js文件就可以知道。







既然是H5页面调用,那么肯定首先是要看jssdk文档了:
一、绑定域名
    这里有一点建议,绑定的域名最好是你开发用的域名,如果你开发用的是二级域名,那么就用二级域名,不要用一级域名,因为如果在该一级域名下面还有其他的二级域名,也要开发另一个公众号,那么就会遇到问题(我没有试过,这两天在网上查资料时看到的)。
二、引入js文件,在调用微信支付的页面引入http://res.wx.qq.com/open/js/jweixin-1.0.0.js;
三、通过config接口注入权限验证配置
    wx.config({
        debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
        appId: '', // 必填,公众号的唯一标识
        timestamp: , // 必填,生成签名的时间戳
        nonceStr: '', // 必填,生成签名的随机串
        signature: '',// 必填,签名,见附录1
        jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2,如果只是支付,只用这一个参数就够了
    });
    参数说明:debug,测试环境下打开,会alert出注入参数是否正确的信息;其它几个参数一定要注意大小写!大小写!大小写!继续向下看,我会给出timestamp,nonceStr和signature几个参数的算法;

四、通过ready接口处理成功验证


    wx.ready(function(){
     // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。

    });
    wx.error(function(res){

    // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。

    });
五、发起支付
wx.ready(function(){
    wx.chooseWXPay({
        timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
        nonceStr: '', // 支付签名随机串,不长于 32 位
        package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
        signType: '', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
        paySign: '', // 支付签名
        success: function (res) {
            // 支付成功后的回调函数
        },
        //如果你按照正常的jQuery逻辑,下面如果发送错误,一定是error,那你就太天真了,当然,jssdk文档中也有提到
        fail: function(res) {
            //接口调用失败时执行的回调函数。
        },
        complete: function(res) {
            //接口调用完成时执行的回调函数,无论成功或失败都会执行。
        },
        cancel: function(res) {
            //用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。
        },
        trigger: function(res) {
            //监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。
        }
    });
});

至此,微信公众号支付的jssdk已经描述完毕,但是不要高兴得太早,微信支付的坑才刚刚开始。
1、wx.config中signature的算法

    jssdk文档上面给出了算法的步骤,但是没有给一个demo,这里我给出大家一个signature的demo,大家可以根据这个按照自己的需要进行修改。

首先,我封装了一个Jssdk对象(该对象封装了 wx.config中所需要的参数):

<span style="font-family:Microsoft YaHei;">/*** 微信js-sdk安全验证* */public class JsSdk {//随机数private String noncestr;//时间戳private String timestamp;//授权urlprivate String url;//签名private String signature;public String getNoncestr() {return noncestr;}public void setNoncestr(String noncestr) {this.noncestr = noncestr;}public String getTimestamp() {return timestamp;}public void setTimestamp(String timestamp) {this.timestamp = timestamp;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getSignature() {return signature;}public void setSignature(String signature) {this.signature = signature;}}</span>

然后,给Jssdk对象属性赋值:

<span style="font-family:Microsoft YaHei;">public static JsSdk sign(String jsapi_ticket, String url) {JsSdk jsSdk = new JsSdk();jsSdk.setUrl(url);String nonce_str = create_nonce_str();jsSdk.setNoncestr(nonce_str);String timestamp = create_timestamp();jsSdk.setTimestamp(timestamp);String signature = "";//注意这里参数名必须全部小写,且必须有序String string1 = "jsapi_ticket=" + jsapi_ticket +"&noncestr=" + nonce_str +"×tamp=" + timestamp +"&url=" + url;try{MessageDigest crypt = MessageDigest.getInstance("SHA-1");crypt.reset();crypt.update(string1.getBytes("UTF-8"));signature = byteToHex(crypt.digest());jsSdk.setSignature(signature);}catch (NoSuchAlgorithmException e){jsSdk = null;e.printStackTrace();}catch (UnsupportedEncodingException e){jsSdk = null;e.printStackTrace();}return jsSdk;}</span>
生成随机字符串:这里大家可以选择其他算法
<span style="font-family:Microsoft YaHei;"> private static String create_nonce_str() {return UUID.randomUUID().toString().substring(0, 20);}</span>
生成时间戳的方法:
<span style="font-family:Microsoft YaHei;">private static String create_timestamp() {return Long.toString(System.currentTimeMillis() / 1000);}</span>

sign方法的调用需要两个参数,jsapi_ticket和url:

jsapi_ticket根据token来获取,https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi,

url为request.getRequestURL().toString()来获取,实际上就是调用支付方法的完整URL。


2、chooseWXPay方法中的timestamp,nonceStr和wx.config中的参数是一样的,signType为MD5
3、chooseWXPay中的package,格式是'prepay_id=${prepayId }',${prepayId }是后台计算得出的,我用的是java。

prepayId的算法:

<span style="font-family:Microsoft YaHei;">                SortedMap<Object, Object> parameters = new TreeMap<Object,Object>();parameters.put("appid", appId);//你自己公众号的appIdparameters.put("mch_id", CacheManager.getValue("MCH_ID"));//此处为商户号parameters.put("nonce_str", jssdk.getNoncestr());//封装在jssdk对象中的noncestrparameters.put("body", "测试");String outTraceNo = getOutTradeNo();//生成订单号的方法,测试时可以随便指定log.info("outTraceNo = " + outTraceNo);parameters.put("out_trade_no", outTraceNo);parameters.put("total_fee", 1);parameters.put("spbill_create_ip", CommonUtil.getRealIp(request));//测试时就写成本机ip即可parameters.put("notify_url", "");parameters.put("trade_type", "JSAPI");parameters.put("openid", openid);parameters.put("sign", PayCommonUtil.createSign("utf-8", parameters));String requestXml = PayCommonUtil.getRequestXml(parameters);log.info("requestXml = " + requestXml);prepayId = AdvancedUtil.createOrder(requestXml);</span>

createSign和getRequestXml方法:

<span style="font-family:Microsoft YaHei;">       /*** @Description:sign签名* @param characterEncoding 编码格式* @param parameters 请求参数* @return*/@SuppressWarnings("rawtypes")public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){StringBuffer sb = new StringBuffer();Set es = parameters.entrySet();Iterator it = es.iterator();while(it.hasNext()) {Map.Entry entry = (Map.Entry)it.next();String k = (String)entry.getKey();Object v = entry.getValue();if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {sb.append(k + "=" + v + "&");}}//设置微信商户平台上的秘钥sb.append("key=" + CacheManager.getValue("API_KEY"));String sign = CommonUtil.MD5(sb.toString(), characterEncoding).toUpperCase();return sign;}/*** @Description:将请求参数转换为xml格式的string* @param parameters  请求参数* @return*/@SuppressWarnings("rawtypes")public static String getRequestXml(SortedMap<Object,Object> parameters){StringBuffer sb = new StringBuffer();sb.append("<xml>");Set es = parameters.entrySet();Iterator it = es.iterator();while(it.hasNext()) {Map.Entry entry = (Map.Entry)it.next();String k = CommonUtil.getStringNotNullValue(entry.getKey());String v = CommonUtil.getStringNotNullValue(entry.getValue());if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");}else {sb.append("<"+k+">"+v+"</"+k+">");}}sb.append("</xml>");return sb.toString();}</span>

4、wx.chooseWXPay方法中的paySign测算法
<span style="font-family:Microsoft YaHei;">                SortedMap<Object, Object> paySignParameters = new TreeMap<Object,Object>();paySignParameters.put("appId", appId);paySignParameters.put("nonceStr", jssdk.getNoncestr());paySignParameters.put("package", "prepay_id=" + prepayId);paySignParameters.put("signType", "MD5");paySignParameters.put("timeStamp", jssdk.getTimestamp());paySign = PayCommonUtil.createSign("utf-8", paySignParameters);</span>




5、到这里,我也必须吐槽一下微信搞了三个类似签名的东西,wx.config中signature,wx.chooseWXPay中的paySign,和调用统一下单接口所用的sign。这三个东西彻底把我搞晕了,算法描述也不是很清楚,希望我上面写的能给大家一些帮助。


后续还会持续更新一些微信开发中遇到的其他一些不清不楚的地方。。。。。敬请期待




这篇关于微信公众号开发中遇到的问题——支付(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1114073

相关文章

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

使用Python开发一个现代化屏幕取色器

《使用Python开发一个现代化屏幕取色器》在UI设计、网页开发等场景中,颜色拾取是高频需求,:本文主要介绍如何使用Python开发一个现代化屏幕取色器,有需要的小伙伴可以参考一下... 目录一、项目概述二、核心功能解析2.1 实时颜色追踪2.2 智能颜色显示三、效果展示四、实现步骤详解4.1 环境配置4.

MySQL 设置AUTO_INCREMENT 无效的问题解决

《MySQL设置AUTO_INCREMENT无效的问题解决》本文主要介绍了MySQL设置AUTO_INCREMENT无效的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录快速设置mysql的auto_increment参数一、修改 AUTO_INCREMENT 的值。

Python基于微信OCR引擎实现高效图片文字识别

《Python基于微信OCR引擎实现高效图片文字识别》这篇文章主要为大家详细介绍了一款基于微信OCR引擎的图片文字识别桌面应用开发全过程,可以实现从图片拖拽识别到文字提取,感兴趣的小伙伴可以跟随小编一... 目录一、项目概述1.1 开发背景1.2 技术选型1.3 核心优势二、功能详解2.1 核心功能模块2.

关于跨域无效的问题及解决(java后端方案)

《关于跨域无效的问题及解决(java后端方案)》:本文主要介绍关于跨域无效的问题及解决(java后端方案),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录通用后端跨域方法1、@CrossOrigin 注解2、springboot2.0 实现WebMvcConfig

Go语言中泄漏缓冲区的问题解决

《Go语言中泄漏缓冲区的问题解决》缓冲区是一种常见的数据结构,常被用于在不同的并发单元之间传递数据,然而,若缓冲区使用不当,就可能引发泄漏缓冲区问题,本文就来介绍一下问题的解决,感兴趣的可以了解一下... 目录引言泄漏缓冲区的基本概念代码示例:泄漏缓冲区的产生项目场景:Web 服务器中的请求缓冲场景描述代码

Python使用smtplib库开发一个邮件自动发送工具

《Python使用smtplib库开发一个邮件自动发送工具》在现代软件开发中,自动化邮件发送是一个非常实用的功能,无论是系统通知、营销邮件、还是日常工作报告,Python的smtplib库都能帮助我们... 目录代码实现与知识点解析1. 导入必要的库2. 配置邮件服务器参数3. 创建邮件发送类4. 实现邮件

Java死锁问题解决方案及示例详解

《Java死锁问题解决方案及示例详解》死锁是指两个或多个线程因争夺资源而相互等待,导致所有线程都无法继续执行的一种状态,本文给大家详细介绍了Java死锁问题解决方案详解及实践样例,需要的朋友可以参考下... 目录1、简述死锁的四个必要条件:2、死锁示例代码3、如何检测死锁?3.1 使用 jstack3.2

解决JSONField、JsonProperty不生效的问题

《解决JSONField、JsonProperty不生效的问题》:本文主要介绍解决JSONField、JsonProperty不生效的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录jsONField、JsonProperty不生效javascript问题排查总结JSONField

github打不开的问题分析及解决

《github打不开的问题分析及解决》:本文主要介绍github打不开的问题分析及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、找到github.com域名解析的ip地址二、找到github.global.ssl.fastly.net网址解析的ip地址三