最近负责的一些项目开发,都用到了微信支付(微信公众号支付、微信H5支付、微信扫码支付、APP微信支付)。在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存。

先说注意的第一点,所有支付的第一步都是请求统一下单,统一下单,统一下单,请求URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder。统一下单的目的是拿到预支付交易会话标识prepay_id,这个是必须的。所有的支付调用都是通过prepay_id来识别。

再说一个微信官方提供的一个很重要的工具,微信支付接口签名校验工具(网址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1),此工具旨在帮助开发者检测调用【微信支付接口API】时发送的请求参数中生成的签名是否正确,提交相关信息后可获得签名校验结果。特别实用!特别实用!特别实用!签名只要正确了,一切就OK了!

还有就是官方提供的几种支付方式的对比说明,如下图所示。

第一部分 微信公众号支付

微信公众号支付需要配置的参数有:APPID(微信公众号开发者ID)、APPSECRET(微信公众号开发者密码)、MCHID(商户ID)、KEY(商户密钥)。

微信公众号支付应用的场景是在微信内部的H5环境中是用的支付方式。因为要通过网页授权获取用户的OpenId,所以必须要配置网页授权域名。同时要配置JS接口安全域名,如下图所示:

以PHP为例,使用官方demo一个最常见的问题就是500错误,回调没反应,这个一般情况下是xml数据解析出现的问题(错误在这里WxPay.Data.php中WxPayDataBase类的FromXml()方法),解决方案如下:

public function FromXml($xml) {
if(!$xml) {
throw new WxPayException("xml数据异常!");
}
//将XML转为array
//禁止引用外部xml实体
libxml_disable_entity_loader(true); //这句导致出现上述问题
$this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $this->values;
}

问题就出现在libxml_disable_entity_loader(),它的作用是设置是否禁止从外部加载XML实体,设为true就是禁止。可以使用将代码改成以下内容进行解决:

public function FromXml($xml) {
if(!$xml) {
throw new WxPayException("xml数据异常!");
}
//将XML转为array
//禁止引用外部xml实体
$disableLibxmlEntityLoader = libxml_disable_entity_loader(true); //改为这句
$this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
libxml_disable_entity_loader($disableLibxmlEntityLoader); //添加这句
return $this->values;
}

本人也尝试过这样一个简单的方案,如下,直接屏蔽(在低版本PHP5.2中测试通过)libxml_disable_entity_loader():

public function FromXml($xml) {
if(!$xml) {
throw new WxPayException("xml数据异常!");
}
//将XML转为array
//禁止引用外部xml实体
//libxml_disable_entity_loader(true); //或者是把这句屏蔽
$this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $this->values;
}

把这个解决之后就OK了。

还有一个问题就是“curl出错,错误码:60”,这个错误是由于服务器和PHP版本导致的,最近一次出现是在一个PHP5.2版本的原生项目上。微信官方对支付的数据传输提出了三点建议:

◆ 使用HTTPS确保网络传输安全性。
◆ 禁用SSL等不安全协议和算法,建议使用TLS1.2。
◆ 不要轻易的尝试设计和实现自己的加密传输算法,几乎都会存在问题。

具体的错误信息在日志里面是这样的:Fatal error: Uncaught exception ‘WxPayException‘ with message ‘curl出错,错误码:60‘ in ,目前的解决方案如下:

最原始的3.0demo里面,找到WxPay.JsApiPay.php文件的99行:

curl_setopt($ch, CURLOP_TIMEOUT, 30); 

最早的example代码里少了一个“T”,这个问题官方已经解决,正确代码应该是如下的:

curl_setopt($ch, CURLOPT_TIMEOUT, 30);

还有就是安全校验的问题,在官方demo WxPay.Api.php 文件中找到如下代码:

curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验

将上述代码做出如下修改:

if(stripos($url,"https://")!==FALSE){
curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
} else {
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
}

这样的话,curl出错,错误码:60这个问题就可以解决了。

第二部分 微信H5支付

微信H5支付是微信官方2017年上半年刚刚对外开放的支付模式,它主要应用于在手机网站在移动浏览器(非微信环境)调用微信支付的场景。底层的技术以及支付链接本质上是财付通。

注意:微信H5支付需要在微信支付商户平台单独申请开通,否则无法使用。

微信H5支付的流程比较简单,就是拼接请求的xml数据,进行统一下单,获取到支付的mweb_url,然后请求这个url网址就行。请求使用curl函数,使用的时候需要注意设置header参数。

    $headers = array();
$headers[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
$headers[] = 'Connection: Keep-Alive';
$headers[] = 'Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3';
$headers[] = 'Accept-Encoding: gzip, deflate';
$headers[] = 'User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:22.0) Gecko/20100101 Firefox/22.0';

下面直接奉上整个流程源码。

$userip = $_SERVER["REMOTE_ADDR"]; //获得用户设备IP
$appid = "wx24d9dbdf00000";//微信
$mch_id = "888888888";//微信官方的
$key = "FSDFSD2356DSD00";//自己设置的微信商家key $nonce_str=MD5($out_trade_no);//随机字符串
$total_fee = $total_fee*100; //金额
$spbill_create_ip = $userip; //IP
$notify_url = "http://www.bojuwang.net/"; //回调地址
$trade_type = 'MWEB';//交易类型 具体看API 里面有详细介绍 $scene_info ='{"h5_info":{"type":"Wap","wap_url":"http://www.hnyjzpw.com","wap_name":"支付"}}';//场景信息 必要参数
$signA ="appid=$appid&body=$body&mch_id=$mch_id&nonce_str=$nonce_str&notify_url=$notify_url&out_trade_no=$out_trade_no&scene_info=$scene_info&spbill_create_ip=$spbill_create_ip&total_fee=$total_fee&trade_type=$trade_type"; $strSignTmp = $signA."&key=$key"; //拼接字符串 注意顺序微信有个测试网址 顺序按照他的来 直接点下面的校正测试 包括下面XML 是否正确
$sign = strtoupper(MD5($strSignTmp)); // MD5 后转换成大写 $post_data="<xml><appid>$appid</appid><body>$body</body><mch_id>$mch_id</mch_id><nonce_str>$nonce_str</nonce_str><notify_url>$notify_url</notify_url><out_trade_no>$out_trade_no</out_trade_no><scene_info>$scene_info</scene_info><spbill_create_ip>$spbill_create_ip</spbill_create_ip><total_fee>$total_fee</total_fee><trade_type>$trade_type</trade_type><sign>$sign</sign>
</xml>";//拼接成XML 格式 $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信传参地址 $dataxml = http_post($url,$post_data,$headers);
$objectxml = (array)simplexml_load_string($dataxml,'SimpleXMLElement',LIBXML_NOCDATA); //将微信返回的XML 转换成数组
function http_post($url='',$post_data=array(),$header=array(),$timeout=30) { $ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 从证书中检查SSL加密算法是否存在
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); $response = curl_exec($ch); curl_close($ch); return $response;
} if($objectxml['return_code'] == 'SUCCESS'){
$mweb_url= $objectxml['mweb_url'];
// header("Location:$mweb_url");
} $redirect_url = urlencode("http://www.bojuwang.net/");

H5支付的回调代码如下,注意xml数据的接收。这是一个很大的坑,PHP需要使用 $GLOBALS['HTTP_RAW_POST_DATA']解析微信支付结果返回的xml。

$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
$dataxml = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);//转成数组, if($dataxml['return_code'] == 'SUCCESS'){
//success
}

第三部分 微信扫码支付

微信扫码支付一般应用的场景是PC端电脑支付。微信扫码支付可分为两种模式,根据支付场景选择相应模式。一般情况下的PC端扫码支付选择的是模式二,需要注意的是模式二无回调函数。

【模式一】商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。

【模式二】商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。

微信扫码支付最友好的解决方案就是支付完成之后通过JS设置监听函数,通过该函数完成跳转。可参考的代码如下:

<script type="text/javascript">
$(function () {
sendPost(); //调用监听事件
});
//监听订单支付状态
function sendPost() {
//发送AJAX请求
$.ajax({
url: "listen.aspx",
type: "POST",
timeout: 30000,
data: { "order_no": "<%=order_no %>" },
dataType: "json",
success: function (data, type) {
if (data.status == 1) {
$("#tipshow").show();
setTimeout(function () {
location.href = data.url; //支付成功后跳转
}, 1000);
} else {
time();
}
}
});
}
function time() {
setTimeout(function () {
sendPost();
}, 5000);
}
</script>

第四部分 微信小程序支付

微信小程序支付是在小程序环境中使用的微信支付方式。

相对于上述几个支付方式,微信小程序支付则显得更简单一些,不涉及到异步通知。在微信小程序中通过官方提供的API wx.requestPayment(OBJECT)发起微信支付,示例代码如下:

wx.requestPayment({
'timeStamp': '',
'nonceStr': '',
'package': '',
'signType': 'MD5',
'paySign': '',
'success':function(res){
},
'fail':function(res){
}
})

通过上面我们可以看到,小程序支付的需要timeStamp、nonceStr、package、signType、paySign这五个参数。然后通过回调函数success或者是fail处理业务逻辑。

后台处理程序的第一步还是统一下单,通过统一下单拿到prepay_id,然后获取相关参数,通过接口(Ajax)传到小程序端发起微信支付。因为统一下单需要用到用户的OpenId,所以在发起统一下单之前要通过小程序的API  wx.login(OBJECT)调用接口获取登录凭证(code)进而换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的 会话密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成。主要是为了拿到OpenId!

涉及到是一个.NET项目,大致的.NET后台代码如下:

 /// <summary>
/// 获取支付的参数
/// </summary>
private void get_pay_params(HttpContext context)
{
string openId = BJRequest.GetFormString("openid");
int user_id = BJRequest.GetFormIntValue("user_id", ); string appId = MiniPayConfig.APPID;
string timeStamp = MiniPay.GenerateTimeStamp();
string nonceStr = MiniPay.GenerateNonceStr();
string signType = "MD5";
string outTradeNo=MiniPay.GenerateOutTradeNo(); //获取统一下单结果,主要是为了拿到prepay_id
MiniPay mnpay=new MiniPay();
MiniPayData unifiedOrderResult = mnpay.GetUnifiedOrderResult(openId,outTradeNo); //小程序支付需要的参数
MiniPayData data = new MiniPayData();
data.SetValue("appId", appId);
data.SetValue("timeStamp", timeStamp);
data.SetValue("nonceStr", nonceStr);
data.SetValue("package", "prepay_id=" + unifiedOrderResult.GetValue("prepay_id"));
data.SetValue("signType", "MD5");
data.SetValue("paySign", data.MakeSign()); StringBuilder strTxt = new StringBuilder(); strTxt.Append("{");
strTxt.Append("\"timeStamp\":\"" + timeStamp + "\"");
strTxt.Append(",\"nonceStr\":\"" + nonceStr + "\"");
strTxt.Append(",\"package\":\"" + data.GetValue("package") + "\"");
strTxt.Append(",\"signType\":\"" + signType + "\"");
strTxt.Append(",\"paySign\":\"" + data.GetValue("paySign") + "\"");
strTxt.Append("}"); context.Response.Write(strTxt.ToString());
}

微信小程序端拿到参数之后,发起微信支付请求,小程序端代码如下:

  //调起微信支付
wxpay: function(){
var that=this;
wx.request({
url: 'https://888.com/api/order.ashx?action=get_pay_params',
method: 'post',
data: {
openid: that.data.openId,
user_id: that.data.userId
},
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
success: function(res){
//if(res.data.status==1){
var order=res.data;
wx.requestPayment({
timeStamp: order.timeStamp,
nonceStr: order.nonceStr,
package: order.package,
signType: 'MD5',
paySign: order.paySign,
success: function(res){ //支付成功,处理相应的订单
wx.request({
url: 'https://8888.com/api/order.ashx?action=order_edit_pay',
method: 'post',
data: {
order_id: that.data.returnOrderId
},
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
success: function (res) {
var data = res.data;
if (data.status == 1) {
console.log("支付成功,处理订单:" + that.data.returnOrderId);
wx.showToast({
title: "订单支付并处理成功!",
duration: 1000,
});
setTimeout(function () {
wx.navigateTo({
url: '../user/dingdan?currentTab=2&otype=deliver',
});
}, 1500);
} else {
wx.showToast({
title: "订单处理失败!",
duration: 2500
});
}
},
fail: function (e) {
wx.showToast({
title: '处理订单网络异常!',
duration: 2000
});
}
});
},
fail: function(res) {
wx.showToast({
title:'支付失败',
duration:1000
});
wx.navigateTo({
url: '../user/dingdan?currentTab=0&otype=all',
});
}
})
},
fail: function() {
// fail
wx.showToast({
title: '支付时网络异常!',
duration: 2000
});
}
})
}

第五部分 微信APP支付

微信APP支付是在APP应用中使用的微信支付方式。

最后,总结一下上述几种支付方式需要注意的点。

1. 所有的支付参数都需要到微信支付商户平台(pay.weixin.qq.com)配置参数。

2. 微信公众号支付、微信扫码支付需要在微信公众号里面申请开通;APP支付需要在微信开放平台申请开通(open.weixin.qq.com);小程序支付需要在小程序平台申请开通。

3. 仅有公众号支付和扫码支付需配置支付域名,APP支付、刷卡支付无需配置域名。下图就是在微信支付商户平台配置授权域名的界面。

4. 所有使用JS API方式发起支付请求的链接地址,都必须在当前页面所配置的支付授权目录之下。下单前需要调用【网页授权获取用户信息】接口获取到用户的Openid。

5. 当公众平台接到扫码支付请求时,会回调当前页面所配置的支付回调链接传递订单信息。

微信公众号支付|微信H5支付|微信扫码支付|小程序支付|APP微信支付解决方案总结的更多相关文章

  1. Senparc.Weixin.MP SDK 微信公众平台开发教程(二十一):在小程序中使用 WebSocket (.NET Core)

    本文将介绍如何在 .NET Core 环境下,借助 SignalR 在小程序内使用 WebSocket.关于 WebSocket 和 SignalR 的基础理论知识不在这里展开,已经有足够的参考资料, ...

  2. 微信小程序扫码解析小程序码

    通过微信扫小程序码,跳转到应用小程序内, 如何解析小程序码的参数呢? 一般小程序码会跳转到设置的页面,如首页, 可以直接跳转到小程序首页,然后解析小程序携带的参数,再打开某个页面. (小程序码的路径要 ...

  3. Vue — 微信公众号内置h5支付相关

    首先,在公众号后台配置h5页面地址 开发流程 1.通过配置h5地址,获取code.再通过code,获取openid getOpenid(){ let url = 'https://open.weixi ...

  4. 微信公众号内唤起h5支付详解

    1.调用统一下单的接口URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder 2.调用统一下单必传参数: appid:需要进行支付功能的公众号的app ...

  5. 微信公众号开发之H5页面跳转到指定的小程序

    前言: 最近公司有一个这样的需要,需要从我们在现有的公众号H5页面中加一个跳转到第三方小程序的按钮.之前只知道小程序之间是可以相互跳转的,今天查阅了下微信开发文档原来现在H5网页也支持小程序之间的跳转 ...

  6. 微信公众号生成带参数的二维码asp源码下载

    晚上闲着没事,一个朋友联系,让帮忙写一个微信公众号利用asp生成带参数的二维码,别人扫了后如果已经关注过该公众号的,则直接进入公众号里,如果没关注则提示关注,关注后自动把该微信用户资料获取到并且保存入 ...

  7. thinkphp5.0 微信公众号接入支付宝支付

    ---恢复内容开始--- 真是无力吐槽这个需求了,想骂客户,好端端的非要在微信公众号接入支付宝,都知道微信公众号是拒绝支付宝的,屏蔽了支付宝,所以在微信公众号接入支付宝的话就必须手动复制链接跳出微信内 ...

  8. php 微信公众号接入支付宝支付

    真是无力吐槽这个需求了,好端端的非要在微信公众号接入支付宝,都知道微信公众号是拒绝支付宝的,屏蔽了支付宝,所以在微信公众号接入支付宝的话就必须手动复制链接跳出微信内置浏览器,强制性打开web浏览器完成 ...

  9. 微信公众号开发系列-13、基于RDIFramework.NET框架整合微信开发应用效果展示

    1.前言 通过前面一系列文章的学习,我们对微信公众号开发已经有了一个比较深入和全面的了解. 微信公众号开发为企业解决那些问题呢? 我们经常看到微信公众号定制开发.微信公众平台定制开发,都不知道这些能给 ...

  10. 微信公众号开发系统入门教程(公众号注册、开发环境搭建、access_token管理、Demo实现、natapp外网穿透)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/a1786223749/article/ ...

随机推荐

  1. 假设我的朋友账号分别是v{1,2,3,4,5},且这五人想要共享一个目录,因此应该加入同一个群组,假设这个群组为vbird,且这五个账号的密码均为password.那该如何建置这五个账号?

    假设我的朋友账号分别是v{1,2,3,4,5},且这五人想要共享一个目录,因此应该加入同一个群组,假设这个群组为vbird,且这五个账号的密码均为password.那该如何建置这五个账号?#!/bin ...

  2. JavaScript中的for in循环

    在学习AJAX的时候,发现JavaScript中for in循环,这种循环对于遍历JSON是很好用的.于是写下了这篇博文 作用 for in循环本质上是forEach循环,它主要有两个作用 遍历数组 ...

  3. geotrellis使用(三十四)矢量瓦片技术研究——矢栅一体化

    前言 本文所涉及技术与Geotrellis并无太大关系,仅是矢量瓦片前端渲染和加载技术,但是其实我这是在为Geotrellis的矢量瓦片做铺垫.很多人可能会说,Geotrellis为什么要搞矢量瓦片, ...

  4. vue webuploader 组件开发

    最近项目中需要用到百度的webuploader大文件的分片上传,对接后端的fastdfs,于是着手写了这个文件上传的小插件,步骤很简单,但是其中猜到的坑也不少,详细如下: 一.封装组件 引入百度提供的 ...

  5. (转)添加PROPAGATION_REQUIRES_NEW 事务没有产生作用

    最近在做事务添加时  发现自己的事务没有新建,上网查到   仅用作收藏. 其二  注意  事务的注解  应该在 内层的事务上面 一.描述 Spring遇到嵌套事务时,当被嵌套的事务被定义为" ...

  6. multimap 和priority_queue详解

    上一期是关于STL和并查集结合的例题,也附了STL中部分容器的使用摘要,由于是从网上东拼西凑的,感觉有的关键点还是没解释清楚,现在从其中摘出两个容器,用例题对它们的用法进行进一步解释. 以下是例题的介 ...

  7. 在0~N个数字中,取指定个数的不重复数字,要求这些数字的和为指定值,求所有结果

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cons ...

  8. 引入Log4j

    1. pom文件添加依赖 <!-- log start --> <dependency> <groupId>log4j</groupId> <ar ...

  9. python 脚本开发实战-当当亚马逊图书采集器转淘宝数据包

    开发环境python2.7.9 os:win-xp exe打包工具pyinstaller 界面tkinter ============================================= ...

  10. SQL Server 2016 Alwayson新增功能

    标签:SQL SERVER/MSSQL SERVER/数据库/DBA/ 概述 SQLServer2016发布版本到现在已有一年多的时间了,目前最新的稳定版本是SP1版本.接下来就开看看2016在Alw ...