klb 2019-06-30
获取到 prepay_id 后将参数再次签名传输给 APP 发起支付。
相信有不少同学因为看到统一下单返回的结果中有 sign 字段,会直接将结果返回给 APP 端,结果 APP 端没办法调起微支付。其实需要对 APP 端用到的字段数据按 “统一下单的签名方式” 签名后得到的 sign,才是 APP 端需要的 sign。
微信支付 App支付 在服务端调用统一下单接口后,服务端需要将返回的订单数据进行二次签名后才能返回给 App 端。开发文档中说的并不是很明确,因为统一下单的返回数据和二签的原数据上存在一些重叠。
微信支付服务端 sdk 提供了 WxPayResults 类,类中也的确提供了生成签名方法,即对结果集签名,源码如下:
以 PHP 版为例,其他语言自行对照。
class WxPayResults extends WxPayDataBase { /** * 生成签名 - 重写该方法 * @param WxPayConfigInterface $config 配置对象 * @param bool $needSignType 是否需要补signtype * @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值 */ public function MakeSign($config, $needSignType = false) { //签名步骤一:按字典序排序参数 ksort($this->values); $string = $this->ToUrlParams(); //签名步骤二:在string后加入KEY $string = $string . "&key=" . $config->GetKey(); //签名步骤三:MD5加密或者HMAC-SHA256 if (strlen($this->GetSign()) <= 32) { //如果签名小于等于32个,则使用md5验证 $string = md5($string); } else { //是用sha256校验 $string = hash_hmac("sha256", $string, $config->GetKey()); } //签名步骤四:所有字符转为大写 $result = strtoupper($string); return $result; } }
注意步骤三,是需要获取 sign 来判断使用什么方式生成 sign 的,是不是有种鸡生蛋,蛋生鸡的短路既视感。在 APP 端调起支付的参数列表的 sign 参数里有提示 “注意:签名方式一定要与统一下单接口使用的一致”,所以这里的逻辑是要你将统一下单返回的 sign 传递进来,以便于统一签名方式。签名后一定要用真的签名去覆盖用来传递签名方式的“签名”。
在统一下单接口中,生成签名的流程是 $obj->setSign() 调用 $obj->makeSign(),而后我们可以 $obj->getSign() 将签名加到请求数据中。但在结果集类中,makeSign 却直接调用了 getSign 来判断使用何种方式生成签名,所以对结果集签名时,需确保结果集中包含了同一下单返回的 sign 字段数据,这样结果集才能满足 “注意:签名方式一定要与统一下单接口使用的一致” 的要求。
所以这个类对签名进行了重写的目的,主要是为了保证二次签名的签名方式与统一下订单的签名方式一致,将统一下单的签名作为 sign 传递给
WxPayResults 然后调用 makeSign,makeSign 就能判断出统一下单的签名方式,与之保持一致。
$uniorder = array ( 'appid' => 'wxd930ea5d5a258f4f',//appid 'device_info' => 'WEB', 'mch_id' => '1900000109',// 商户id 'nonce_str' => 'g6OZoULWyliPmiPm', 'prepay_id' => 'wx12143635206473d0a53e80f14278847815', 'result_code' => 'SUCCESS', 'return_code' => 'SUCCESS', 'return_msg' => 'OK', 'sign' => 'E91035CA24EDF115374BD2B4C4F9B419',//统一下单的签名 'trade_type' => 'APP', )
文档地址:https://pay.weixin.qq.com/wik...
如果自己写,二不用 sdk 的话,我们需要对
<?php // 传递的数据 $app_result = array ( 'appid' => $uniorder['appid'],//从统一下单的结果中取 'partnerid' => $uniorder['mch_id'],//从统一下单的结果中取 'prepayid' => $uniorder['prepay_id'],//从统一下单的结果中取 'package' => 'Sign=WXPay',//自己写 'noncestr' => WxPayApi::getNonceStr();,//自己写 'timestamp ' => time(),//自己写 ); // 与统一下单的签名方式一致即可 $sign = signMethodConsistWithUniOrder($app_result); $app_result['sign'] = $sign; // 返回给 APP 端 return $$app_result;
如果用 sdk 的务必要将 统一下单返回的数据里的签名 sign 也传递给 WxPayResults 类,已使得保证签名方式一致
<?php // 传递的数据 // 传递的数据 $app_result = array ( 'appid' => $uniorder['appid'],//从统一下单的结果中取 'partnerid' => $uniorder['mch_id'],//从统一下单的结果中取 'prepayid' => $uniorder['prepay_id'],//从统一下单的结果中取 'sign' => $uniorder['sign'],//用来使结果集签名方式与统一下单签名方式一致 'package' => 'Sign=WXPay',//自己写 'noncestr' => WxPayApi::getNonceStr();,//自己写 'timestamp ' => time(),//自己写 ); $wxPayResults = new WxPayResults(); // 构建 WxPayResults 对象 $wxPayResults->FromArray($app_result); // 真正的返回数据的签名 覆盖用来统一签名方式的“签名” $app_result['sign'] = $wxPayResults->makeSign($wxPayConfig);//然后更新成二签后的sign // 返回给 APP 端 return $$app_result;
IWXAPI api; PayReq request = new PayReq(); request.appId = "wxd930ea5d5a258f4f"; request.partnerId = "1900000109"; request.prepayId= "1101000000140415649af9fc314aa427",; request.packageValue = "Sign=WXPay"; request.nonceStr= "1101000000140429eb40476f8896f4c9"; request.timeStamp= "1398746574"; request.sign= "7FFECB600D7157C5AA49810D2D8F28BC2811827B"; api.sendReq(request);
使用服务端提供的数据发起支付请求即可。