微信公众号中的支付宝支付与微信支付 && 支付宝支付问题(微信bug)
一般,在微信公众号中的商城都是需要支持微信支付和支付宝支付的,当然,较大的公司对于鹅厂和阿里的站队就不说了,所以这里简单记录一下支付宝支付和微信支付的主要流程。说是简单介绍,这是因为确实不难,因为前端在这方面,包括微信授权登陆这一块需要做的都不是很多,而主要的工作量都在后端部分。
支付宝支付
无论是支付宝支付还是微信支付,最开始的步骤当然是将商品列表、商家相关信息、用户remark、运费、总价等等支付需要的信息通过post请求向后端传递,这里介绍支付宝支付,所以假设用户选择的是支付宝支付,那么后端返回的就是一个阿里的url,我们通过这个url来进行支付宝的支付。 当然, 首先,我们应该进入一个支付宝引导页面,即指导用户在浏览器中打开,这是因为微信是不支持支付宝进行付款的,所以支付宝需要单独在网页中打开进行支付。注意: 如果真的希望在微信上使用支付宝付款,可以使用微信开发者工具v0.7.0 , 经过测试,这个至少是可以的。 用户在这个支付宝的url中支付完成之后,最终再跳转到公众号的页面上即可。 至于判断微信浏览器的方法,可以使用下面这种:
util.isWeChat = function () {
var browserInfo = navigator.userAgent.toLowerCase();
var weChatPattern = /MicroMessenger/i;
if (weChatPattern.test(browserInfo)) {
return true;
} else {
return false;
}
}
微信支付
微信支付较之于支付宝支付稍显复杂,公众号支付开发者文档对此做了详细的说明。此文档的阅读对象当然就是一些涉及微信支付的研发工程师、运维工程师等等了。并且微信支付从2014年开始到目前2017年6月都在不断地迭代更新。
支付方式
为了对微信支付有更深层次的了解,我们需要知道下面几种支付方式:
- 刷卡支付 --- 即微信里我的钱包中的二维码、条形码的支付方式,主要用在线下的面对面的收银场景。
- 扫码支付 --- 即商户根据微信支付协议生成的二维码,用户通过扫一扫来进行支付。
- 公众号支付 --- 这也就是用户在H5页面中使用JSAPI进行支付的场景。这也是使用比较多的场景。
- APP支付 --- 即在app中使用的支付方式。
支付账户
如果商户希望使用上面的支付方式的其中一种,就需要向微信进行申请,申请成功之后,微信会发来一封邮件,这个邮件上会记录关于这个支付账户相关的信息,即提供给商户一些API供其调用来完成公众号内的支付功能。邮件中的参数与将会调用的API有下面的对应关系:
即一个公众号就是一个应用,它就有唯一的appid,这是在申请公众号的时候就会有的,而mch_id是微信支付商户号,这必须是在申请了微信支付功能之后才会有的账号,用于给商户分配收款账号。 key就是api秘钥。 Appsecret是appid对应的接口密码,通过这个接口密码可以使用access_token调用api了,然后通过OAuth2.0接口获取openid,openid是每一个用户在一个公众号中特有的标识,也就是用于标识一个公众号下不同的用户。
接口规则
如果希望使用微信支付接口,那就就要遵守它所指定的一些规则: 比如使用https协议,采用post方式提交相应数据,并且这些数据必须是XML的格式,签名算法是MD5,申请退款、取消订单也是需要证书的。
并且在调用接口的时候,当然需要传递必要的订单、商品、商铺等相关的参数。 如交易金额默认单位为分,并且不能有小数。
交易类型也有多种,比如 JSAPI对应的是公众号支付,NATIVE对应的时原生扫码支付,APP为app支付,MICROPY是刷卡支付等等。
货币类型是CNY,即china yuan。
我们采用的时间都是标准的北京时间。
时间戳就是js中的1970年1月1日0点0分0秒到目前的秒数。
订单号是由商户自己定义的不得重复的数字。 重新发起一笔支付要是用原订单号,避免重复支付。
紧接着就是不同的银行类型。
安全规范
这一部分是非常重要的一部分。
1. 签名算法
首先,舍所有发送或者接收的数据为集合M,将集合M中非空参数值的参数(也是键值对)按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2)的形式拼接成字符串 stringA。 特别注意下面的规则:
- 参数名ASCII码从小到大进行排序(字典序)。
- 如果参数值为空则不参与签名。
- 参数名区分大小写。
- 验证调用返回或微信主动通知签名时所传递的sign参数不参与签名,将生成的签名与该sign值作校验。
- 微信接口可能增加字段(即微信自己增加的,不是我们传递的),验证签名时必须支持增加的扩展字段。
紧接着在stringA后面拼接上key(即秘钥)得到stringSignTemp字符串,然后对stringSignTemp进行MD5运算。 将得到的字符串所有字符转化为大写,得到sign值signValue。
举例如下:
假设传送的参数如下:
appid: wxd930ea5d5a258f4f
mch_id:
device_info:
body: test
nonce_str: ibuaiVcKdpRxkhJA
第一步就是按照key=value的方式按照参数名的ASCII字典序进行排序:
stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
然后拼接api秘钥并使用md5运算:
stringSignTemp=stringA+"&key=192006250b4c09247ec02edce69f6a2d"
sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7"
最后就可以得到要发送的数据了:
<xml>
<appid>wxd930ea5d5a258f4f</appid>
<mch_id></mch_id>
<device_info><device_info>
<body>test</body>
<nonce_str>ibuaiVcKdpRxkhJA</nonce_str>
<sign>9A0A8659F005D6984697E2CA0A9CF3B7</sign>
</xml>
这个就做到了加密。
2. 生成随机数算法
即在微信支付的api接口协议中包含字段nonce_str, 主要是保证签名不可预测,推荐生成随机数算法如下: 调用随机数函数生成,将得到的数值转化为字符串。
3. 商户证书
资金退款、撤销接口需要用到商户证书,商家在申请微信支付成功后,收到的相应邮件后,可以按照指引下载api证书。 商户证书不能放在web服务器虚拟目录, 应该放在有访问权限控制的目录中,防止被他人下载。在普通的网络环境下,http请求存在DNS劫持、运营商插入广告、数据被窃取、正常数据被修改等安全风险。 用户回调接口使用HTTPs协议可以保证数据传输的安全性。 所以微信支付建议商户提供微信支付的各种回调采用HTTPs协议。
4. 获取openid
在关注者与公众号产生了消息交互之后,公众号可以获得关注着的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的。对于不同的公众号,同一用户的openid不同)。
公众号可以通过接口来获取用户的openid,还可以获得用户的昵称、头像、性别、所在城市、语言和关注时间,但是这些都是需要用户授权。
公众号支付
场景介绍
用户选择商铺,挑选商品,点击购买,调起微信支付控件,用户开始输入支付密码,密码验证通过,支付成功,返回用户页面,显示购买成功(商户自定义),当然,用户也可以吧商品网页的链接生成二维码, 用户扫一扫打开后即可完成购买支付。
以下是重要的逻辑
- 用户选购商品后需要发起支付,前端通过JavaScript调用getBrandWCPayRequest(get-获取、brand-新的、WC-wechat、payRequest-支付请求)接口,发起微信支付请求,用户进入支付流程。
- 用户支付成功并点击完成按钮后,商户的前端会收到JavaScript的返回值,商户可以直接跳转到支付成功的静态页面(即商户自定义的静态页面,可以嵌入相应的返回值)进行展示。
- 商户后台收到来自微信开放平台的支付成功回调通知, 标志着该笔订单成功支付。
值得注意的时,后两者显然是不会保证严格的触发时序的,如果用户不点击完成,那么后台当然也会接收到相应的支付成功回调通知。JS API返回值作为触发商户网页跳转的标志,但是商户后台应该只是在收到微信后台的支付成功回调通知后,才做真正的支付成功的处理。
开发步骤
首先需要设置支付目录。
接着设置授权域名 --- 在统一下单接口中要求必须传入用户的openid, 而获取openid需要在公众平台上获取openid的域名,只有被设置过的域名才是一个有效的获取openid的域名,否则将获取失败。
获取微信版本号
微信5.0版本后才加入微信支付模块,所以低于微信5.0的使用用户将无法使用支付功能。
微信内H5调起支付
在微信路蓝旗中打开H5页面执行JS调起支付,接口输入输出数据格式为JSON。
(注意)在微信中有一个 WeixinJSBridge 内置对象,而其他浏览器中是不存在的。
(注意)在列表中的参数名区分大小写,大小写错误签名验证会失败。
注:JS API的返回结果get_brand_wcpay_request:ok仅在用户成功完成支付时返回。由于前端交互复杂,get_brand_wcpay_request:cancel或者get_brand_wcpay_request:fail可以统一处理为用户遇到错误或者主动放弃,不必细化区分。
示例代码如下所示:
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"wx2421b1c4370ec43b", //公众号名称,由商户传入
"timeStamp":"", //时间戳,自1970年以来的秒数
"nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串
"package":"prepay_id=u802345jgfjsdfgsdg888",
"signType":"MD5", //微信签名方式:
"paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
其中最后if判断的代码是用于支持浏览器不能实现WeixinJSBridge对象的浏览器支持的,而else明显是在调用函数的。
前端所需要的只是大概就这么多了,更多请参考官方文档。
支付宝支付问题
this.commitOrder(postObj).then(function (response) {
that.$loading.close();
switch(response.data.code){
// 支付宝支付方式
case :
that.$router.push({ path: '/commodity/payment/AlipayHint', query: { alipay: encodeURIComponent(response.data.data) }})
break;
vue中支付的接口如上所示,提交订单之后,如果返回值为74,那么用户选择的就是支付宝支付,由于支付宝不能在微信中使用,所以push到支付宝的提示页面,提示页面代码如下所示:
<template>
<div class="alipay-hint">
<img src="../assets/img/alipay-hint.png" alt="">
<MenuFooter></MenuFooter>
</div>
</template> <style lang="less" scoped>
div.alipay-hint {
background: #eeefef;
img {
width: %;
height: %;
}
}
</style> <script>
import MenuFooter from "@components/menu"
import util from '../utils/js/util'
export default {
data: function () {
return {
name: 'alipay-hint'
}
},
created () {
if (!util.isWeChat()) {
window.location.href = decodeURIComponent(this.$route.query.alipay);
}
},
components: {
MenuFooter
}
} </script>
即首先进入这个页面,然后判断当前的浏览器是否是微信,如果是微信,那么就什么都不做,显示的是提示在浏览器中打开的图片; 如果不是微信,也就是说在其他浏览器打开,就导航到阿里支付的页面,这样就可以进行支付了。 现在的问题在于:后端返回的数据(即阿里支付的链接)是没有问题的,但是一旦选择在浏览器中打开,并没有打开当前的阿里提示支付的页面,所以也就没有打开阿里支付页面,而是跳转到了首页,于是支付宝支付一直是有问题的。
问题总结:
从微信中复制到的链接的查询字符串是否含有阿里支付的链接,如果有,那么在浏览器打开之后,在判断了不是微信浏览器之后,就应该打开了支付宝支付,为什么没有打开,甚至即使打不开,也应该打开阿里支付的图片啊,为什么回到了首页?????
问题猜测以及解决尝试方法:
- 是否是因为后端返回的数据根本就不含阿里支付的链接,所以才打不开?
验证方法 --- 对于每次返回的数据,alert出来,看看是否是阿里的支付链接?
验证结果 --- 返回的结果的确是阿里支付的链接,所以说并不是因为后端没有返回正确的数据,此问题猜测排除。
2. 当我进入到支付宝支付页面时,讲到里我通过decodeURIComponent应该是可以拿到传递过来的阿里支付链接的参数,是否是因为这个链接没有正确拿到,所以即使在浏览器中打开,也是打开不了的。
验证方法 --- 进入支付宝支付页面的时候(在微信中),alert出来 decodeURIComponent 阿里支付链接的值,看是否正常拿到了这个值?
验证结果 --- 在支付宝支付页面中,alert时, decodeURIComponent的值的确就是后端返回的数据 --- 阿里支付的链接, 此问题猜测排除。
3. 经过2可以知道,数据(链接)确实是准确的拿到了的,那么就应该在检测到浏览器不是微信的时候就进入支付链接。那是不是因为在微信中选择在浏览器打开的时候它复制到的链接和实际的链接不同呢?
验证方法 --- 进入支付宝支付页面的时候,先alert出当前的location.href, 然后再亲自复制链接,然后粘贴到别的地方,进行比对。
验证结果 --- alert出来的当前的url和copy出来的url差别很大!!! 在实际的url中,是http://xxx.xxx.com/index.html?84654685465#/dfjlajfoasjfoa8653465fdasl; 在copy得到的url中,只copy到了前面一部分,http://xxx.xxx.com/index.html?84654685465, 把锚点之后的省略了,并且用=来代替锚点之后的所有内容。
问题 --- 也就是说微信中在copy的时候并没有copy到所有的url,而是截取了一部分,把锚点给删除了!
再次验证 --- 把vue中的其他页面(微信中)在浏览器打开 --- 问题同样 --- 即打开后都没有得到对应的url,而是都把锚点去掉了,在浏览器打开时得到的都是相同的index.html, 而没有复制到锚点。
4. 对于3的问题追踪
但是对于其他的页面是否也是相同的问题呢? 我尝试着打开了vuejs官网,然后进入了api页面,发现这里面的每一个知识点也是使用锚点定位的? 所以问题并没有出在这里。 那么为什么没有copy上呢?
经过尝试,发现了这样的一个问题,对于下面的链接:
www.biangou.cn/hat2/index.html?495452482fbv56a3dc2130aba8b32fbv549479711
进入之后,然后点击不同的路由,即这时应该是带有锚点的, 但是在copy之后,发现锚点并没有被复制下来。 但是如果我们使用下面的链接进入。
www.biangou.cn/hat2/index.html?495452482fbv56a3dc2130aba8b32fbv549479711#
这样,如果在使用锚点,那么复制的时候,锚点就可以复制下来了。
微信中的bug
在微信中, 我们可以尝试输入 vuejs.org ,然后进入页面之后找到api页面,然后点击一个标题,这时url中应该是添加了一个#的,即锚点定位,但是实际上却没有。在除了微信之外的浏览器中这样的做法是可以实现的。
但是我们如果想要准确记录下微信中的锚点,即copy到并且可以在浏览器中打开,那么解决这个问题是非常必要的,我们只要在url的最后添加一个#即可,这样,带着#号进入就可以解决到所有的问题了。
微信公众号中的支付宝支付与微信支付 && 支付宝支付问题(微信bug)的更多相关文章
- C#/ASP.NET MVC微信公众号接口开发之从零开发(二) 接收微信消息并且解析XML(附源码)
文章导读: C#微信公众号接口开发之从零开发(一) 接入微信公众平台 微信接入之后,微信通过我们接入的地址进行通信,其中的原理是微信用户发送消息给微信公众账号,微信服务器将消息以xml的形式发送到我们 ...
- 微信公众号中ip白名单用谁的ip
https://segmentfault.com/q/1010000010201211 白名单怎么说 我该填写谁的 我的ip地址每天都变化的 服务器ip啊,为了防止未授权的代码盗用你的权限.写你ip是 ...
- C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台
微信公众平台接入:其实很简单,把两个参数(地址和token)填入微信公众平台后台,暂时选择明文模式 ,其中token自己定义.微信服务器会根据后台填写的地址访问,并且带上对于的参数 如 url+&am ...
- JAVA微信公众号网页开发——将文章群发到微信公众号中(文章使用富文本,包含图片)
SendTextToAllUserAct.java package com.weixin.sendmessage; import org.apache.commons.lang.StringUtils ...
- 玩玩微信公众号Java版之二:接收、处理及返回微信消息
前面已经配置了微信服务器,那么先开始最简单的接收微信消息吧~ 可以用我们的微信号来直接进行测试,下面先看测试效果图: 这是最基本的文本消息的接收.处理及返回,来看看是怎么实现的吧! 首先可 ...
- asp.net core 微信公众号支付(扫码支付,H5支付,公众号支付,app支付)之3
在微信公众号中访问手机网站,当需要调用支付时候无法使用H5支付,只有使用微信公众号支付,使用公众号支付用户必须关注该公众号同时该公众号必须开通公众号支付功能. 1.获取用户的OpenId ,参考之前写 ...
- 微信公众号开发之网页中及时获取当前用户Openid及注意事项
目录 (一)微信公众号开发之VS远程调试 (二)微信公众号开发之基础梳理 (三)微信公众号开发之自动消息回复和自定义菜单 (四)微信公众号开发之网页授权获取用户基本信息 (五)微信公众号开发之网页中及 ...
- thinkphp.2 thinkphp5微信支付 微信公众号支付 thinkphp 微信扫码支付 thinkphp 微信企业付款5
前面已经跑通了微信支付的流程,接下来吧微信支付和微信企业付款接入到thinkphp中,版本是3.2 把微信支付类.企业付款类整合到一起放到第三方类库,这里我把微信支付帮助类和企业付款类放到同一个文件了 ...
- 网站如何接入微信公众号JSAPI支付PHP版
1.首先,我们要有一个微信公众号(分类类型有订阅号,服务号,企业号)我们的微信公众号一定是个服务号只有它才有微信支付接口.. 并且这个微信公众号一定要进行微信认证才能申请微信支付接口. 2.申请JSA ...
随机推荐
- Mysql企业实战
==========================业务垂直分割:1>介绍说明: 随着公司的业务规模扩展,DBA需要根据企业数据业务进行切割,垂直切割又称为纵向切割,垂直数据切割是根据企业网站业 ...
- 通过python实现wc基本功能
---恢复内容开始--- 1.Github项目地址: https://github.com/zhg1998/ww/blob/master/wc.py 2.项目相关要求: 写一个命令行程序,模仿已有wc ...
- XE5安卓手机要求
1 ARMv7 的 CPU v6 的肯定不支持.2 黑屏是因为你的手机 CPU 不支持 NEON 特性.或者是 T2 CPU.3 系统版本 2.3.3 到 2.3.9 或者 4.0 以上.4. SD ...
- Linq分区操作之Skip,SkipWhile,Take,TakeWhile源码分析
Linq分区操作之Skip,SkipWhile,Take,TakeWhile源码分析 二:linq的分区操作 常用的分区操作:Take,TakeWhile,Skip,SkipWhile 三:Take ...
- 前台通过form表单向Django后台传输数据,Django处理后返回给前台
摘要:Django前后台数据传递 通过action将数据传输给apitest这个地址,使用get方法传递,此处需要传递name="request_method"的下拉列表值和nam ...
- C#在线运行
初步完成c#代码的在线编辑. 首先,传回前端的c#在线代码,进行预编译,用CSharpCodeProvider这个方法.设置编译版本3.5 设置编译参数GenerateInMemory:是 ...
- 编写高质量JS代码中
前段时间看了几道关于前端javascript的面试题目,方觉函数调用模式等基础的重要性.于是,下定决心,好好补补基础,即便不能深入语言的内部设计模式,也要对基本面向对象概念有比较深入的理解. 继续上一 ...
- IO模型《四》多路复用IO
多路复用IO(IO multiplexing) IO multiplexing这个词可能有点陌生,但是如果我说select/epoll,大概就都能明白了.有些地方也称这种IO方式为事件驱动IO (ev ...
- 安装yum仓库
1.yum仓库是在系统镜像文件里,所以我们要安装yum仓库要把系统镜像文件添加进来: 2.进行挂载配置,并且要使配置永久生效,要进行配置: 3.接下来要创建yum仓库 配置文件: 总结:以上就是安装y ...
- php从文本读入数据,处理结果再导入到文本
1,php从文本逐行读入数据,保存到数据组.使用fopen读取文本内容,逐行读取文本是$majorId = trim(fgets($rfile, 4096));. $rfile = fopen(&qu ...