当前位置: 澳门新濠3559 > 编程 > 正文

最近做的 SPA 网站集成了微信支付,java程序也可

时间:2019-10-06 23:47来源:编程
简单介绍微信公众号支付的申请、接入、使用、支付等相关流程,具体会调到微信的授权登录、支付以及关单等api,里面会着重提到需要注意的坑。 在Web应用中接入微信支付的流程之极

简单介绍微信公众号支付的申请、接入、使用、支付等相关流程,具体会调到微信的授权登录、支付以及关单等api,里面会着重提到需要注意的坑。

在Web应用中接入微信支付的流程之极简清晰版,极简清晰版

背景:

在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可。

没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布下的所有坑。

简要介绍几个主要大坑:

坑一:关于WeixinJSBridge这个对象

查阅网页端调起支付API的开发文档,此对象即旁若无人的映入眼帘。

然后我们就理所应当的在代码里调用了丫的。

可是,too young too naive……

尽管此对象只在微信浏览器里有效,借此可判断用户是否在微信浏览器里访问应用。

但其实调用了丫之后,你是没有未来的……

此坑所耗时间:1.5天。

坑二:几个签名的混淆

关于签名的调试,网上各种哭嚎,不少人在签名处调试多日无果。

那是因为整个工作流程中,涉及到了至少三个签名,稍不小心就会用错。

在统一下单的接口里,向微信服务器发送请求中的参数有一枚签名;

其返回的参数中,亦有一枚签名,后经观察,发现二者为同一签名。

但在使用JSSDK后所调用的wx.config里所需的参数signature却是另一枚签名。

而在wx.chooseWXPay里所需的支付签名paySign,却又是另另另外的一枚。

此坑所耗时间:2天。

坑三:两个Access_token

查看开发文档可以看到,微信搞了两个Access_token!

即使他们用再多的加粗文字来提醒我们两个token的不同之处,

也难以抚平我们在开发过程中陷入混乱的受伤之身心!

一个是网页授权的access_token,需要用户授权之后才能拿到。

而另一个是普通的access_token,支持公众号的各种基础服务,并非支付独用。

看文档所述的流程,以为用网页授权的token就对了?

恭喜,又进坑了……

最后真正用到的是第二个token。

此坑所耗时间:1.5天。

…………

坑坑相套,防不胜防。最终跑通功能后,心力交瘁矣!

为防以后再次进坑,在此总结一番,供各位参考。

背景毕。

 

———————————————— 我是悲愤不已的分割线 ————————————————

 

准备工作第一弹:

公众号一枚,并开通微信支付功能。

在微信公众平台(mp.weixin.qq.com)的[微信支付-开发配置]里对支付授权目录做配置。

测试期间,需要配置测试授权目录,以及将参与测试的微信号添加到白名单中。

由于测试必须在线上(测试)环境,debug不太方便,才耗费了辣么多美好的时光。

授权目录,即为调用微信支付控件的页面所在目录。

准备工作第二弹:

还是在公众平台上,进入开发者中心,找到接口权限表。

在其中的网页服务中,找到“网页授权用户基本信息”并填写之。

这里所填的URL必须是用户进入应用的URL,且必须是oauth验证所用的URL里的redirect_uri里的值……

一处不对,都会在坑里苦熬良久。

准备工作第三弹:

还是在公众平台上,进入公众号设置,找到功能设置。

需要对JS接口安全域名做设置。

这里的安全域名就是你的应用相关的域名,改动次数受限制,要注意。

准备工作第四弹:

还是在公众平台上,在开发者中心里找到配置项。

这里需要有APPID和APPSecret的访问权限。

另外,还需要准备的是商户id:mch_id 和 key。

注:secret 和 key 在通信接口里有用到,不允许在前端页面使用!

 

———————————————— 我是跃跃欲试的分割线 ————————————————

 

第一步骤:一个带验证的入口URI

首先引导用户进入应用,这里以H5页面为例。

由于微信支付必须在微信浏览器里进行,因此入口URL链接应当从公众号发布给用户,

这样用户才会在微信客户端中点击链接,进入应用。

(不要问京东为什么可以在纯网页端使用微信支付,那是微信给他的小伙伴京东开的后门好吗)

这个入口URL不是简单的URL,这里需要做oauth的验证,因此我们暂设此URL为entranceURL。

entranceURL的格式为:

标红的几个变量需要说明:

  1. APPID就是公众号的appid

  2. REDIRECT_URI很眼熟?就是刚在网页授权里写的URI,要转义哦亲。

3. response_type须填code,理由是这是一个GET请求,微信会返回code然后用code再……blah blah blah

4. SCOPE,填snsapi_userinfo(需用户手动授权)或snsapi_base(直接进入页面)

  1. STATE 会跟随code一起返回,看业务需要自己填。一般格式是有大小写和数字的组合。

 

第二步骤:用code获取openId

用户点击了这个entranceURL之后会发生什么奇妙的事情咩?

这时候微信会返回给你一个code,并重定向到你在REDIRECT_URI里指定的页面。

你需要在这个页面的URL里拿到code的值,如果拿不到,要提示用户授权失败或未授权。

由于这个entranceURL可能是应用的首页,而需要调用支付控件的是其他页面,

所以可以先将其缓存到例如sessionStorage里,到了需要调用支付控件时再用。

拿到这个code需要做什么咩?

这时候要做的事情就不能在前端页面里继续进行了,这里必须写一个前后端通信的接口,

将这个code传送给后端服务器,然后在服务器里发起对微信的GET请求:

;

标红的是需要被替换的变量,其中appidsecret都应该保存在服务端文件中。

这个请求返回的Json里的openid就是我们要的。

 

第三步骤:统一下单接口

接口地址:

参考文档:

这个接口在文档里讲得很清楚,在此不赘述。

注意这里有签名sign,它与后面要用的signaturepaySign虽然都是签名,但完全不同。

如果你的trade_type是NATIVE就不需要openid,如果是JSAPI就必须要openid。

在统一下单的接口中,你会告诉微信当前这笔交易的一些基本信息,

包括但不限于商户id、交易号、交易金额等等。

微信会将其打包到一个package里,从接口返回的prepay_id里其实就对应了你的交易详情。

注意这个接口返回的状态码有两种,一个标识的是通信是否成功:return_code

另一个标识的是业务是否成功:result_code。详情见文档。

 

第四步骤:前端配置wx.config

在上一步接口的业务返回成功之后,可以在前端引用JSSDK了。

开发文档在:

首先注入权限验证配置时,需要用到wx.config

它有几个参数要格外注意,不然又进坑了:

  1. 随机字符串nonceStr,必须使用上一步的统一下单接口返回的nonceStr。

2. 签名signature,不是上步接口里的sign,需在服务端生成,返给前端。(见下一步骤)

3. jsApiList 里要指定微信支付接口“chooseWXPay”。

  1. debug参数设为true时,会在移动端打出alert帮助调试。

 

第五步骤:服务端生成wx.config所需的signature

首先用appid和secret从微信基础支持接口中获取access_token

前面提到了两个token:网页授权型token和普通型token。

这里获取的是普通型token。

由于这个token是公众号所有服务都可能用到的,因此要格外注意。

有效期7200s,建议过期之前应当缓存之。

然后用这个token去获取jsapi_ticket,接口地址:

access_token "&type=jsapi

这个ticket同样也要做缓存。

现在可以准备生成signature了,所需的参数有:

  1. jsapi_ticket

  2. nonceStr 即统一下单接口返回的随机字符串

  3. 时间戳:秒为单位(后续还会用到时间戳,要保持一致)

  4. 当前页面URL,不需转义

注意这个签名是用sha1算法加密,其他的签名是用MD5算法加密的。

 

第六步骤:终于开始配置chooseWXPay了

wx.config的验证通过之后,就可以在wx.ready的回调里执行wx.chooseWXPay了。

chooseWXPay的几个属性配置说明如下:

  1. timeStamp:刚才用到的那个时间戳,秒为单位。

  2. nonceStr:统一下单接口里的随机字符串

3. package:值的格式为“prepay_id=” 统一下单接口返回的prepay_id

  1. signType:由于这里的签名是MD5加密,因此这个值就是“MD5”

5. paySign:呵呵,此处是坑,这个签名也是在服务端生成,返给前端,见下一步骤。

  1. success:支付成功后的回调。

  2. fail: 支付失败后的回调。

  3. cancel:用户取消支付后的回调。

这里注意,由于进入应用时是带着oauth验证的,因此在用户取消支付后,

code可能已过期,因此建议在取消支付的回调里,调用wx.closeWindow()退出Web页返回微信客户端。

因为即使你不退出,再执行支付,code之后的验证也不会通过。

这时只能重新访问一次第一步骤中的entranceURL才行。

 

第七步骤:生成chooseWXPay所需的签名paySign

这个签名不同于刚才在wx.config里的签名signature,这是用MD5加密的。

生成签名所需参数有appidnonceStrpackagesignTypetimeStamp。(字典序排列)

其中nonceStr和package都是前面调用的统一下单的接口返回的。

这里的signType是“MD5”.

另外,这个签名拼合的时候,最后要加一个key,就是app的key。

key的排序在最后,排序不参与字典序。具体规则见官方文档。

 

第八步骤:测试

至此,万事具备,包括东风。可以测试了。

建议使用iPhone手机测试,如果有问题IOS会报出错信息,Android不会。

最常见的问题就是“Invalid signature”,这个错误信息太笼统了。

需要更细致的调试和辛勤的打Log。我在这个坑里淌了两天……

注:本地测试环境不能测试,必须发布到线上测试环境(非生产环境),

并且线上测试环境的域名和URI等信息都要在公众号设置里有授权

 

———————————————— 我是蠢蠢欲动的分割线 ————————————————

 

如此看来,流程清晰多了,也无甚难处。

可当初做开发和调试时,因为对后端语言不熟,耗费了很多时间,

用JS三秒写出的方法,用后端语言写就频繁报错,

什么也不说了,只恨自己读书少。

最后感谢在此过程中提供语法指点的盆友。

 

背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可。 没想...

原文:

demo

wxpay demo

1. 准备阶段: 申请

最近做的 SPA 网站集成了微信支付,使用的是微信 H5 调起支付API接口。做完后对微信 H5 支付的流程有了进一步的了解,在前后端调试接口的过程中也遇到了一些问题,在这里记录下来。

技术栈

  1. Spring boot
  2. java
  3. XML (微信在http协议中数据传输方案)
  4. MD5 签名

申请步骤请直接查看微信公众平台官方文档

支付流程

  1. 在订单页 ajax 请求后端发起下单,后端挂起请求
  2. 后端根据订单号结合微信支付相关配置参数向微信服务器发起统一下单
  3. 下单成功,微信通知前面传递的 notify_url,返回 prepay_id (预支付交易会话标识)
  4. 后端返回前端 JSAPI 调用的参数
  5. 前端使用 JSSDK 的 wx.chooseWXPay 发起支付
wx.chooseWXPay({
  timestamp: '',   // 支付签名时间戳
  nonceStr: '',    // 支付签名随机串
  package: '',     // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
  signType: 'MD5', // 签名方式
  paySign: '',     // 支付签名
})

这里看起来是 5 步,但其实还少了一步。调用“统一下单”接口的时候需要微信用户的 openid。

微信支付术语

  1. openid (OpenID是公众号一对一对应用户身份的标识)
  2. app_id (公众号id,登录微信公众号–开发–基本配置中获得;)
  3. key (收款商户后台进行配置,登录微信商户平台–账户中心–API安全-设置秘钥,设置32位key值;)
  4. mch_id
  5. certPath (API证书, 登录微信商户平台–账户中心-API安全-下载证书)

注册开通微信服务号

openid 是什么?

官方解释

加密后的微信号,每个用户对每个公众号的 openid 是唯一的。对于不同公众号,同一用户的 openid 不同

这个 openid 只能在微信环境通过重定向拿到。但是在下单的时候用的是 ajax 请求,还用重定向用户体验就比较差。所以需要在进入微信的时候就通过重定向拿到 openid 缓存起来,后面就可以直接使用了。所以入口页面就要做点小动作。

后端流程

服务端需要的核心操作, 总共分为以下几步:

  1. 统一下单
  2. 前端调起微信支付必要参数
  3. 订单结果主动通知
  4. 查询订单结果
  5. 结束订单支付接口(关闭订单,支付订单关闭)

申请开通支付商户平台

获取 openid

官方有OpenID的获取指引。对于 H5 应用,获取方式分 3 步走:

代码

微信总共支持多种语言的sdk, 在官网可以下载例子, java程序也可以引入微信支付的sdk包, 但是github上的sdk已经很久没有更新了, 最好的选择, 也是我的选择, 在官网上下载sdk项目, 将其中所有java类copy到自己的项目中.

官网sdk下载目录链接: 商户平台首页

澳门新濠3559 1微信sdk下载

创建IWxPayConfig.class, 继承sdk WXPayConfig.class, 实现sdk中部分抽象方法, 读取本地证书, 加载到配置类中.package core.com.chidori.wxpay;

import core.com.wxpay.IWXPayDomain;import core.com.wxpay.WXPayConfig;import core.com.wxpay.WXPayConstants;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import java.io.ByteArrayInputStream;import java.io.File;import java.io.FileInputStream;import java.io.InputStream;@Servicepublic class IWxPayConfig extends WXPayConfig { // 继承sdk WXPayConfig 实现sdk中部分抽象方法 private byte[] certData; @Value("${vendor.wx.config.app_id}") private String app_id; @Value("${vendor.wx.pay.key}") private String wx_pay_key; @Value("${vendor.wx.pay.mch_id}") private String wx_pay_mch_id; public IWxPayConfig() throws Exception { // 构造方法读取证书, 通过getCertStream 可以使sdk获取到证书 String certPath = "/data/config/chidori/apiclient_cert.p12"; File file = new File; InputStream certStream = new FileInputStream; this.certData = new byte[ file.length()]; certStream.read(this.certData); certStream.close(); } @Override public String getAppID() { return app_id; } @Override public String getMchID() { return wx_pay_mch_id; } @Override public String getKey() { return wx_pay_key; } @Override public InputStream getCertStream() { return new ByteArrayInputStream(this.certData); } @Override public IWXPayDomain getWXPayDomain() { // 这个方法需要这样实现, 否则无法正常初始化WXPay IWXPayDomain iwxPayDomain = new IWXPayDomain() { @Override public void report(String domain, long elapsedTimeMillis, Exception ex) { } @Override public DomainInfo getDomain(WXPayConfig config) { return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true); } }; return iwxPayDomain; }}

// 发起微信支付WXPay wxpay = null;Map<String, String> result = new HashMap<>();try { // ****************************************** // // 统一下单 // // ****************************************** wxpay = new WXPay(iWxPayConfig); // *** 注入自己实现的微信配置类, 创建WXPay核心类, WXPay 包括统一下单接口 Map<String, String> data = new HashMap<String, String>(); data.put("body", "订单详情"); data.put("out_trade_no", transOrder.getGlobalOrderId; // 订单唯一编号, 不允许重复 data.put("total_fee", String.valueOf(transOrder.getOrderAmount().multiply(new BigDecimal.intValue; // 订单金额, 单位分 data.put("spbill_create_ip", "192.168.31.166"); // 下单ip data.put("openid", openId); // 微信公众号统一标示openid data.put("notify_url", "http://wxlj.oopmind.com/payCallback"); // 订单结果通知, 微信主动回调此接口 data.put("trade_type", "JSAPI"); // 固定填写 logger.info("发起微信支付下单接口, request={}", data); Map<String, String> response = wxpay.unifiedOrder; // 微信sdk集成方法, 统一下单接口unifiedOrder, 此处请求 MD5加密 加密方式 logger.info("微信支付下单成功, 返回值 response={}", response); String returnCode = response.get("return_code"); if (!SUCCESS.equals(returnCode)) { return null; } String resultCode = response.get("result_code"); if (!SUCCESS.equals(resultCode)) { return null; } String prepay_id = response.get("prepay_id"); if (prepay_id == null) { return null; } // ****************************************** // // 前端调起微信支付必要参数 // // ****************************************** String packages = "prepay_appId", iWxPayConfig.getAppID; wxPayMap.put("timeStamp", String.valueOf(Utility.getCurrentTimeStamp; wxPayMap.put("nonceStr", Utility.generateUUID; wxPayMap.put("package", packages); wxPayMap.put("signType", "MD5"); // 加密串中包括 appId timeStamp nonceStr package signType 5个参数, 通过sdk WXPayUtil类加密, 注意, 此处使用 MD5加密 方式 String sign = WXPayUtil.generateSignature(wxPayMap, iWxPayConfig.getKey; // ****************************************** // // 返回给前端调起微信支付的必要参数 // // ****************************************** result.put("prepay_id", prepay_id); result.put("sign", sign); result.putAll; return result;} catch (Exception e) {}

核心是支付订单回调时, 需校验加密签名是否匹配, 防止出现模拟成功通知

@RequestMapping(value = "/payCallback", method = RequestMethod.POST)public String payCallback(HttpServletRequest request, HttpServletResponse response) { logger.info("进入微信支付异步通知"); String resXml=""; try{ // InputStream is = request.getInputStream(); //将InputStream转换成String BufferedReader reader = new BufferedReader(new InputStreamReader; StringBuilder sb = new StringBuilder(); String line = null; try { while ((line = reader.readLine != null) { sb.append(line   "n"); } } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } resXml=sb.toString(); logger.info("微信支付异步通知请求包: {}", resXml); return wxTicketService.payBack; }catch (Exception e){ logger.error("微信支付回调通知失败",e); String result = "<xml>"   "<return_code><![CDATA[FAIL]]></return_code>"   "<return_msg><![CDATA[报文为空]]></return_msg>"   "</xml> "; return result; }}@Overridepublic String payBack(String notifyData) { logger.info("payBack() start, notifyData={}", notifyData); String xmlBack=""; Map<String, String> notifyMap = null; try { WXPay wxpay = new WXPay(iWxPayConfig); notifyMap = WXPayUtil.xmlToMap(notifyData); // 转换成map if (wxpay.isPayResultNotifySignatureValid(notifyMap)) { // 签名正确 // 进行处理。 // 注意特殊情况:订单已经退款,但收到了支付结果成功的通知,不应把商户侧订单状态从退款改成支付成功 String return_code = notifyMap.get("return_code");//状态 String out_trade_no = notifyMap.get("out_trade_no");//订单号 if (out_trade_no == null) { logger.info("微信支付回调失败订单号: {}", notifyMap); xmlBack = "<xml>"   "<return_code><![CDATA[FAIL]]></return_code>"   "<return_msg><![CDATA[报文为空]]></return_msg>"   "</xml> "; return xmlBack; } // 业务逻辑处理 **************************** logger.info("微信支付回调成功订单号: {}", notifyMap); xmlBack = "<xml>"   "<return_code><![CDATA[SUCCESS]]></return_code>"   "<return_msg><![CDATA[SUCCESS]]></return_msg>"   "</xml> "; return xmlBack; } else { logger.error("微信支付回调通知签名错误"); xmlBack = "<xml>"   "<return_code><![CDATA[FAIL]]></return_code>"   "<return_msg><![CDATA[报文为空]]></return_msg>"   "</xml> "; return xmlBack; } } catch (Exception e) { logger.error("微信支付回调通知失败",e); xmlBack = "<xml>"   "<return_code><![CDATA[FAIL]]></return_code>"   "<return_msg><![CDATA[报文为空]]></return_msg>"   "</xml> "; } return xmlBack;}

全部申请通过后,获取支付必须的参数如下:

1. 后端重定向到获取 code 接口

后端可以定义一个 /wechat/redirect 接口,例如:

http://yoursite.com/wechat/redirect?url=home_url 

用这个地址重定向获取 code 地址,其中的 url 参数为最终的重定向地址,在拿到 openid 后跳转到该 url

注意点

  1. 统一下单的签名和后续前端拉取微信支付的签名需要统一, 也就是都采用MD5加密, 如果2者不同, 会导致前端拉取微信支付fail, 这是一个巨大的坑, 因为这个原因调试了好久, 微信在文档里没有明确标出统一下单的签名校验方式 需要和前端拉取微信支付的签名校验保持一致.微信sdk里的源码需要针对这个问题调整一下, 调整如下:WXPay类需要修改下加密判断,在WXPay构造方法中,调整如下

    public WXPay(final WXPayConfig config, final String notifyUrl, final boolean autoReport, final boolean useSandbox) throws Exception { this.config = config; this.notifyUrl = notifyUrl; this.autoReport = autoReport; this.useSandbox = useSandbox; if (useSandbox) { this.signType = SignType.MD5; // 沙箱环境 } else { this.signType = SignType.MD5; // 将这里的加密方式修改为SignType.MD5, 保持跟前端吊起微信加密方式保持一致 } this.wxPayRequest = new WXPayRequest;}
    

1.1 appid 和appSecret

2. 获取 code:

接口地址

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirec

接口参数:

  • appid: 公众账号ID
  • redirect_uri: 接收 code 的回调地址(请UrlEncode)
  • response_type: 固定值 code
  • scope: 应用授权作用域,填 snsapi_base 或者 snsapi_login(可获取用户信息,如头像、昵称等)
  • state: 用于保持请求和回调的状态,防止 csrf 攻击,可设置为简单的随机数加 session 进行校验

提示:snsapi_login 会跳转到授权页让用户授权
微信接着会重定向三次,第三次重定向返回到 redirect_uri 地址,并且带上了 code 参数

结束语

做完以后, 微信支付的后端逻辑还是很清晰的, 但是在开发过程中很煎熬, 不清楚每个专业术语在微信哪里配置, 加密方式乱的很,

公众平台创建应用的唯一标识。

3. 通过 code 获取 openid 和 access_token

接口地址

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

获取到 openid 后注意在 session 中缓存起来

登录微信公众平台,进入应用详情可查看AppId和AppSecret

一些需要注意的坑

澳门新濠3559 2澳门新濠3559,开发者中心配置入口

1. 获取 code 的接口地址

在OpenID的获取指引文档,请注意黑色标题“微信公众平台”和“微信开放平台”。两个地方获取 code 的接口地址不一样,但参数是一样的。最开始后端看错了文档使用的是“网站应用微信登录”文档里面提供的获取 code 的接口,这样子怎么都是调不通的。

// 微信公众平台是
https://open.weixin.qq.com/connect/oauth2/authorize
// 微信开放平台是
https://open.weixin.qq.com/connect/qrconnect

1.2 mch_id

2. 后端签名用 timeStamp,而前端调用支付接口使用全小写 timestamp

微信官方网页端调起支付API这个接口文档参数却误导观众,写的是驼峰的 timeStamp
后端为了方便返回给前端的也是 timeStamp,所以在前端需要转换
解决办法:

if (!data.timestamp) data.timestamp = data.timeStamp

微信支付商户号

3. iOS 和 Android 版微信对“支付授权目录”的检测不同

http://yousite.com/mobile/#!/checkout

以上路径,iOS 微信识别正确:http://yousite.com/mobile/
而 Android 微信识别出的目录是:http://yousite.com/mobile/#!/checkout
这应该是 Android 版微信的 Bug
解决办法是在 # 前添加一个 ?http://yousite.com/mobile/?#!/checkout

微信支付申请完成后,微信商户平台会给你邮箱发通知邮件,里面包含开通支付的商户信息

澳门新濠3559 3微信支付商户号配置

1.3 api密钥

即商户支付秘钥,主要负责处理通信相关参数加密。登陆微信商户平台(账号密码在微信商户平台发来的邮件里)

点击左侧的「账户设置 - API 安全」(第一次登陆会让你安装操作证书,请先安装操作证书)。点击设置密钥,设置自己的密钥。

澳门新濠3559 4

1.4 商户证书

用于退款等一些需要证书验证的接口使用。在微信商户平台点击「账户中心 - API 安全」,点击「下载证书」

澳门新濠3559 5

证书下载后,打开压缩包会看到「apiclient_cert.pem」和「apiclient_key.pem」和rootca.pem证书。

2. 开始搞事---微信公众号开发

主要步骤:

查文档

公众号授权登录

获取jsapi的ticket

统一下单

微信端拉起支付

服务端异步接收支付结果

异步关单

2.1 查看API

  1. 微信支付开发文档:

2. 微信公众平台技术文档:

  1. 公众号支付业务流程:

4. 微信支付签名规范:

5. 微信公众平台支付接口调试工具:

  1. 微信网页授权:

  2. JSSDK说明文档 :

  3. 微信js 接口签名校验工具:

2.2 公众号授权登录

坑 一: AccessToken会混淆。

混淆是因为在微信公众号开发中有两个accessToken。

一个是网页授权accessToken(显式登录时用于换取个人信息,是一次性的),另一个是基础支持的accessToken(该accessToken用于调用微信其他接口,一天只能获取50次,服务端需要中控缓存,可以使用定时任务去获取的机制)。

解决方案 :

授权登录主要是后端来完成。前端跳转后端的一个请求(location.href方式),把目标页面地址写进state参数(后端登录完成后会跳转到这个页面,并且会把登录态放进去),后端接口判断code是否有值,如果没有说明是前端带过来的,直接去访问微信的接口获取code,微信回调会把code带过来,后端接收到后用code去置换accessToken和openid。 如果是静默授权,就在这里结束了。如果是是普通授权,需要拿accessToken去获取个人信息。后端缓存登录态并返回给前端。(可以考虑redis机制和cookie)

Coding: 贴出主要代码逻辑

澳门新濠3559 6澳门新濠3559 7澳门新濠3559 8微信服务号配置类,springboot机制读取配置

2.3 接入jsapi 开发

这是为前端注入jssdk环境准备的,为wx.config提供参数。后端生成完后可以用官方校验工具校验下。

Coding:

澳门新濠3559 9获取jsapi,可以不用同步机制澳门新濠3559 10

2.4 统一下单

获取预支付id

坑二: 非必需参数一定不要传,免得失败了。

坑三: 必须传openid参数

坑四: 签名使用 MD5

Coding: 这个是核心逻辑,由于微信sdk写的很粗犷,没有什么特别的干货,所以自己封装了。(还好,没有调微信sdk,最近爆出有坑!)

澳门新濠3559 11澳门新濠3559 12澳门新濠3559 13

2.5 微信端拉起支付

下面提到的坑就是在你保证签名是正确的情况下哦!

坑五: 必须真机调试支付,无法正确定位bug。

这个关系到前后端的沟通,接口文档就得落实清楚。我那时候就因为前端取参数,timeStamp 取成了timestamp,导致我连续加班了两天一直在检查签名,逐行逐行的调试代码。 发现跟签名调试工具上产生的签名都一致,最后被研发boss指出来,说有可能前端参数取错。 这个跟前后端沟通以及真机调试限制有很大关系。

坑六: 商户平台支付目录配置错误或者忘记配置。

支付域名必须配到调起支付的那个页面的上一级目录。 如果是vuejs框架,如果上一级目录是#,那么就得配到#。

坑七: 预支付id过期,也拉不起支付。(相信我,微信是不会告诉你他们的统一下单id是有超时限制的!!!大厂就是这么任性)

这个问题是在实际开发中才遇得到,主要就是同一个预支付id在五个小时前能正常拉起支付,五个小时后突然就拉不起支付了。

Coding:

澳门新濠3559 14

看到拉起支付界面觉得好亲切啊!!!一定是拯救了银河系

澳门新濠3559 15

2.6 异步接收微信返回的支付结果

坑八: 微信给你的结果是放在response里面,是xml格式,需要解析xml后取出支付结果。

坑九: 这个接口需要避开我们的登录校验。就是直接放在浏览器里面就可以访问的接口。

坑十: 接口返回也是通过流的方式,不要通过restful。 不然微信会在告警群里面提示你返回有问题啥的。

Coding:

澳门新濠3559 16澳门新濠3559 17

2.7: 异步关单

取消订单的时候 需要调这个接口,做成一个线程去跑就好!

澳门新濠3559 18

一些代码贴出来,只是为了整理一下思路。如果看完后还是没法调起支付,可以给我留言,或者关注我的公众号:互联网修真院

编辑:编程 本文来源:最近做的 SPA 网站集成了微信支付,java程序也可

关键词: 澳门新濠3559