上一篇讲到成功获取 openid,本篇要调用微信统一接口创建预支付交易单

除付款码支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。

第三步,调用微信统一下单接口创建预支付交易单

微信统一下单API是微信支付的一个“统一”处理入口,官方给出的地址是

https://api.mch.weixin.qq.com/pay/unifiedorder
https://api2.mch.weixin.qq.com/pay/unifiedorder(备用域名)

首先先按照 统一下单 接口一问中给的 请求参数 准备必要的参数,在本例(JSAPI)中至少需要参数如下

  • appid,公众号AppID
  • mch_id,商户号,一串10位数字
  • device_info,设备号,非必填但有一定作用,PC网页或公众号内支付可以传"WEB",本例中就设置为"WEB"
  • nonce_str,随机字符串,请求方提供的随机信息,主要保证签名不可预测
  • sign,通过签名算法计算得出的签名值,详见签名算法
  • sign_type,签名类型,截止编辑时支持(默认)"MD5"和"HMAC-SHA256",用哪种算法进行的签名要通过该字段表示
  • body,商品描述,商户按规范创建
  • out_trade_no,商户系统内部订单号,按微信的规则生成
  • total_fee,整型,单位分,订单总金额
  • spbill_create_ip,"用户的客户端IP",这个词儿是微信给出的截止到编辑时从字面上我并不确定指的是请求服务器的服务器地址还是访问商户服务器的某个用户"手机"的地址,但通过代码应该是后者,详见之后的代码示例
  • notify_url,异步接收微信支付结果通知的回调地址
  • trade_type,交易类型的标识,JSAPI-JSAPI支付,NATIVE-Native支付,APP-APP支付,MWEB-H5支付
  • openid,第一篇中来自微信用户的openid值

按照 签名算法 计算签名

  • 把所有要传递的参数键值对去掉值是空的,剩下的参数名ASCII码从小到大排序后,使用URL键值对格式(key1=value1&key2=value2...)拼接成StringA
  • 把StringA后面多加一组键值 &key=商户平台密钥 获得StringB
  • 把StringB按要求做"MD5"或"HMAC-SHA256"计算,并将结果字符转为大写
  • 微信甚至还提供了一个在线校验工具帮助开发者检查生成的签名是否正确,跳到工具。使用方法是选择好 签名类型,校验方式选择 XML (不是必须只是为了省事儿),XML源串 输入不带 sign 信息的部分如
<xml>
<appid>wxcxxxxx5bbf</appid>
<mch_id>15xxxxxxxxxxxx61</mch_id>
<device_info>WEB</device_info>
<nonce_str>6743482E92E61DE587B52D6C7DEBFE79</nonce_str>
<body>xx科技-测试</body>
<out_trade_no>TY-OUTTRADENO-TEST-1561440044</out_trade_no>
<total_fee>1</total_fee>
<spbill_create_ip>36.xx.xx.185</spbill_create_ip>
<notify_url>http://wxpay.txxxt.com/jsapi/unifiedOrderNotify.php</notify_url>
<trade_type>JSAPI</trade_type>
<openid>100003</openid>
</xml>

再输入正确的 商户密钥,点击按钮即可计算出结果,与自己计算的结果进行比对。

  • 本例中 Sign 方法接收两个参数,$arrayobj 为数组类型,保存了所有参数名和参数值组成的字符串数组,方法输出计算后的签名结果。ToUrlParams 方法将参数数组拼接为URL格式字符串。
     /**
* 生成签名
* @param 参与签名的内容组成的数组
* @param 是否使用 HMAC-SHA256算法,否则使用MD5
* @return 签名字符串
* @author 试试手气
* @version test pass
*/
public static function Sign($arrayobj, $hmacsha256 = true)
{
//步骤一:字典排序
ksort($arrayobj); //步骤二:在
$str = Util::ToUrlParams($arrayobj); //步骤三:在$str后面加KEY
$mchKey = "asdxxxxxxxxxxxxxxN82"; // 商户支付秘钥,生产代码中是不可能如此硬编码到这里的
$str .= "&key=" . $mchKey; //步骤四:MD5或HMAC-SHA256C加密
if ($hmacsha256) {
$str = hash_hmac("sha256", $str, $mchKey);
} else {
$str = md5($str);
} //步骤五:所有字符转大写
$result = strtoupper($str);
return $result;
} public static function ToUrlParams($arrayobj)
{
$buff = "";
foreach ($arrayobj as $k => $v) {
if ($k != "sign" && $v != "" && !is_array($v)) {
$buff .= $k . "=" . $v . "&";
}
} $buff = trim($buff, "&");
return $buff;
}

将签名结果作为最后的参数一起创建XML格式的POST数据

除 sign 签名外,其它的参数要么是现成的,要么是可以运行时获取到的如 spbill_create_ip。计算完签名,所有的参数就都准备好了,把它们加工成XML,参数顺序没有提到有限制。

function toXML($paramsobj)
{
$xml = "<xml>\n"; foreach ($paramsobj as $key => $value) {
$xml .= "<" . $key . ">" . $value . "</" . $key . ">\n";
} $xml .= "</xml>";
return $xml;
}

出来是这个样子的

<xml>
<appid>wxcxxxxx5bbf</appid>
<mch_id>15xxxxxxxxxxxx61</mch_id>
<device_info>WEB</device_info>
<nonce_str>6743482E92E61DE587B52D6C7DEBFE79</nonce_str>
<body>xx科技-测试</body>
<out_trade_no>TY-OUTTRADENO-TEST-1561440044</out_trade_no>
<total_fee>1</total_fee>
<spbill_create_ip>36.xx.xx.185</spbill_create_ip>
<notify_url>http://wxpay.txxxt.com/jsapi/unifiedOrderNotify.php</notify_url>
<trade_type>JSAPI</trade_type>
<openid>100003</openid>
<sign>42DDF6C558EC05E24AAD0Cxxxxxxxx1DC3A53C0053D4A6F42E31036132F74481</sign>
</xml>

向微信统一下单API提交XML

用POST方式向统一下单接口地址提交数据,本例中使用的是微信SDK中的 postXmlCurl 方法改造了一下,用的是 curl 访问网络

function postXmlCurl($config, $xml, $url, $useCert = false, $second = 30)
{
$ch = curl_init();
//$curlVersion = curl_version();
//$ua = "WXPaySDK/".self::$VERSION." (".PHP_OS.") PHP/".PHP_VERSION." CURL/".$curlVersion['version']." "
// .$config->GetMerchantId(); //设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second); // $proxyHost = "0.0.0.0";
// $proxyPort = 0;
// $config->GetProxy($proxyHost, $proxyPort);
// //如果有配置代理这里就设置代理
// if($proxyHost != "0.0.0.0" && $proxyPort != 0){
// curl_setopt($ch,CURLOPT_PROXY, $proxyHost);
// curl_setopt($ch,CURLOPT_PROXYPORT, $proxyPort);
// }
curl_setopt($ch, CURLOPT_URL, $url);
//curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,TRUE);
//curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验
//curl_setopt($ch,CURLOPT_USERAGENT, $ua);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); if ($useCert == true) {
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//证书文件请放入服务器的非web目录下
$sslCertPath = "";
$sslKeyPath = "";
$config->GetSSLCertPath($sslCertPath, $sslKeyPath);
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLCERT, $sslCertPath);
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLKEY, $sslKeyPath);
}
//试试手气新增,增加之后 curl 不报 60# 错误,可以请求到微信的响应
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //不验证 SSL 证书
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//不验证 SSL 证书域名
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
//返回结果
if ($data) {
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
curl_close($ch);
return "curl error, error code ".$error;
//throw new WxPayException("curl出错,错误码:$error");
}
}

没有删掉代码,不用的注释掉了,并增加了关于 CURLOPT_SSL_VERIFYPEER 和 CURLOPT_SSL_VERIFYHOST 的明确设置,在增加这两句之前curl本身会报60错误。

返回值

按微信文档,访问(非业务)成功将返回(至少)包含 return_code 和 return_msg 两个字段的XML文档,对照统一下单接口文档内的返回结果即可判断请求是否成功,如下例

<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wxccxxxxbbf]]></appid>
<mch_id><![CDATA[15xxx61]]></mch_id>
<device_info><![CDATA[WEB]]></device_info>
<nonce_str><![CDATA[SMIpZtHhPPyTezlY]]></nonce_str>
<sign><![CDATA[7215B1BAB1C08A4887AAB0ECB2A6044729FE8A8BC6E70591660A6BCC11D7DC9A]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx251xxxxxxxxxxxxxxxxxxxxxxxxxxxxx078700]]></prepay_id>
<trade_type><![CDATA[JSAPI]]></trade_type>
</xml>

很明显是访问也成功了,业务也成功了,也就是接口业务成功生成了 预支付交易会话 wx251xxxxxxxxxxxxxxxxxxxxxxxxxxxxx078700 ,后面就该使用该会话标识调起H5支付了。

#PHP#微信支付 第二篇 JSAPI 调用统一下单接口获取预支付交易数据的更多相关文章

  1. 微信支付 第一篇 JSAPI 支付配置与获取 OpenID

    开通微信支付支付产品 首先要在微信支付申请成为 微信支付商户. 选择开通具体的支付产品 成为微信支付商户后在管理后台选择微信支付中的具体支付产品并申请开通如 JSAPI . 将支付商户与公众号关联 这 ...

  2. 微信支付 第三篇 微信调用H5页面进行支付

    上一篇讲到拿到了 预支付交易标识 wx251xxxxxxxxxxxxxxxxxxxxxxxxxxxxx078700 第四步,是时候微信内H5调起支付了! 先准备网页端接口请求参数列表 微信文档中已经明 ...

  3. 在.net core上,Web网站调用微信支付-统一下单接口(xml传参)一直返回错误:mch_id参数格式错误

    这是 微信支付-统一下单 接口文档 一.问题描述 在调用统一下单接口时,报mch_id参数格式错误,但商户ID确实是10位数字正确的,可就是一直报这个错误 返回的错误xml如下: 二.排错过程 1.多 ...

  4. SpringBoot项目后台对接微信支付开发——微信统一下单接口开发

    开始没找到微信支付的sdk.自己根据官方给的接口文档纯手写,各种xml转JSON,JSON转xml,加密解密,签名....整个人都是崩溃的 开发的第三天,发现有官方的sdk.心情一下子豁然开朗,整个人 ...

  5. [88221008]调用新下单接口失败,result:162020004,resInfo

    [88221008]调用新下单接口失败,result:162020004,resInfo

  6. php微信支付(仅Jsapi支付)详细步骤.----仅适合第一次做微信开发的程序员

    本人最近做了微信支付开发,是第一次接触.其中走了很多弯路,遇到的问题也很多.为了让和我一样的新人不再遇到类似的问题,我把我的开发步骤和问题写出来,以供参考. 开发时间是2016/8/2,所以微信支付的 ...

  7. asp.net微信支付发起页面jsapi.aspx

    jsapi.aspx 后台核心代码 //创建支付应答对象 RequestHandler packageReqHandler = new RequestHandler(Context); //初始化 p ...

  8. 微信支付中的jsapi返回提示信息

    jsapi中跳转到微信支付中触发的方法是js中的getBrandWCPayRequest方法. 改方法中的返回结果msg提示信息如下: err_msg:get_brand_wcpay_request: ...

  9. 微信支付(公众号支付APIJS、app支付)服务端统一下单接口java版

    一.微信公众号支付APIJS: 要完整的实现微信支付功能,需要前后端一起实现,还需要微信商户平台的配置.这里只是涉及服务端的代码. jar包:pom.xml <!-- ↓↓↓↓↓↓↓↓ 支付相关 ...

随机推荐

  1. codeforces 126B

    Asterix, Obelix and their temporary buddies Suffix and Prefix has finally found the Harmony temple. ...

  2. gulp 实现 js、css,img 合并和压缩(转)

    前提条件,知道如何安装nodejs.gulp,这里不做介绍,可以自行google 实现此功能需要安装的gulp工具有如下 npm install gulp-htmlmin gulp-imagemin ...

  3. JAVA基础--数组的应用04

    一.数组的简单应用 1. 数组平均值 例子:求下列数字的平均值:1.0,2.4,3.5,4.3,5.1,6.8,7.2,8,9,10 package cn.haile.array; /** * 练习数 ...

  4. (水题)洛谷 - P1308 - 统计单词数

    https://www.luogu.org/problemnew/show/P1308 简单哈希一下判断,练练手. 注意fgets()的用法,第一个参数传存储位置,第二个参数传内存上限,第三个传std ...

  5. 51nod1266【水】

    最短,两半分开走 最长,一边走,比较一下两端就好了. #include <bits/stdc++.h> using namespace std; typedef long long LL; ...

  6. bzoj 3238: [Ahoi2013]差异【SAM+树形dp】

    首先只有lcp(i,j)需要考虑 因为SAM的parent树是后缀的前缀的最长公共后缀(--),所以把这个串倒过来建SAM,这样就变成了求两个前缀的最长公共后缀,长度就是这两个前缀在parent树上的 ...

  7. android 启动报错

    报错如下: AAPT err(Facade for 1532009679): libpng error: Read Error Error:Execution failed for task ':ap ...

  8. python之迷宫BFS

    # @File: maze_queue_bfs from collections import deque maze = [ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0 ...

  9. 2015 ACM-ICPC国际大学生程序设计竞赛北京赛区网络赛 1002 Mission Impossible 6

    题目链接: #1228 : Mission Impossible 6 解题思路: 认真读题,细心模拟,注意细节,就没有什么咯!写这个题解就是想记录一下rope的用法,以后忘记方便复习. rope(块状 ...

  10. SPRING-BOOT系列之Spring4快速入门

    上节 : spring boot简介 接着上章节的spring boot简介,我们会发现boot是基于spring的,其中最重要的就是spring容器了.那么本章着重介绍spring容器装配自定义be ...