获取到 prepay_id 后将参数再次签名传输给 APP 发起支付

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...

获取到 prepay_id 后将参数再次签名传输给 APP 发起支付

  • package 暂填写固定值Sign=WXPay
  • noncestr 并不一定要统一下单返回的 nonce_str,自己生成 32位 的也可以
  • timestamp 自己生成即可
  • sign 传递统一下单返回的签名,以使得结果集签名和统一下单签名方式一致(或者你清楚的知道你对结果集签名的方式同下单的一致)

如果自己写,二不用 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;

App 端调用微信支付的方式为

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);

使用服务端提供的数据发起支付请求即可。

相关推荐