数据签名 
微信支付目前现行两大类数据签名方法,对称密钥算法基于MD5及HMAC-SHA256数据摘要算法,非对称密钥算法基于RSA-OAEP(模数2048)算法。
对称密钥算法 
签名生成的通用步骤如下:
第一步:
设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
特别注意以下重要规则:
- 参数名ASCII码从小到大排序(字典序);
 - 如果参数的值为空不参与签名;
 - 参数名区分大小写;
 - 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验;
 - 微信支付接口可能增加字段,验证签名时必须支持增加的扩展字段;
 
第二步:
在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5/HMAC-SHA256运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
特别注意:
- 密钥key的长度为32个字节;
 - key设置路径:微信商户平台 -> 账户中心 -> 账户设置 -> API安全 -> 设置API密钥;
 
微信支付API接口协议中包含字段nonce_str,主要保证签名不可预测。微信支付官方推荐生成随机数算法如下:调用随机数函数生成,将得到的值转换为字符串。
以官方文档举的例子,用PHP实现:
后台数据交换 
设传送的参数 集合 $collection 如下:
$collection = [
  'appid' => 'wxd930ea5d5a258f4f',
  'mch_id' => '10000100',
  'device_info' => '1000',
  'body' => 'test',
  'nonce_str' => 'ibuaiVcKdpRxkhJA'
];第一步:按照参数名ASCII字典序并对参数按照URL键值对的格式拼接如下:
$stringA = 'appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA';第二步:拼接API密钥key:
use WeChatPay\Crypto\Hash;
//注:key为商户平台设置的密钥key
$stringSignTemp = $stringA . '&key=192006250b4c09247ec02edce69f6a2d';
//注:MD5签名方式
$sign = \strtoupper(Hash::md5($stringSignTemp));
//样本值='9A0A8659F005D6984697E2CA0A9CF3B7'
//注:HMAC-SHA256签名方式,部分语言的hmac方法生成结果二进制结果,需要调对应函数转化为十六进制字符串。
$sign = \strtoupper(Hash::hmac($stringA, $key, 'sha256'));
//样本值='6A9AE1657590FD6257D693A078E1C3E4BB6BA4DC30B23E0EE2496E54170DACD6'则传输的数据即为:
$collection = [
  'appid' => $appId,
  'mch_id' => $mchId,
  'device_info' => '1000',
  'body' => 'test',
  'nonce_str' => 'ibuaiVcKdpRxkhJA',
  //以MD5为例
  'sign' => '9A0A8659F005D6984697E2CA0A9CF3B7'
];APIv2是以XML格式作为数据交换方式,最终传输时需转换上述数据为XML文本格式如下:
<xml>
  <appid>wxd930ea5d5a258f4f</appid>
  <mch_id>10000100</mch_id>
  <device_info>1000</device_info>
  <body>test</body>
  <nonce_str>ibuaiVcKdpRxkhJA</nonce_str>
  <sign>9A0A8659F005D6984697E2CA0A9CF3B7</sign>
</xml>以上签名及数据格式转换过程,用本开发包即如下:
// 对集合M按参数名字典序排列 -> URL风格拼接数据 -> 数据签名并把值大写
use WeChatPay\Formatter;
use WeChatPay\Transformer;
use WeChatPay\Crypto\Hash;
// 计算签名并把签名值大写
$collection['sign'] = \strtoupper(Hash::md5(
  // URL风格拼接数据
  Formatter::queryStringLike(
    // 按参数名字典序排列
    Formatter::ksort($collection)
  ),
  $key
));
// 或者使用封装的`Hash::sign`方法简化如下
$collection['sign'] = Hash::sign(Hash::ALGO_MD5, $collection, $key);
// 转换为`XML`文本
$xml = Transformer::toXml($collection);前台数据交换 
此种方式也遵循 通用步骤 原则,只是 集合 $collection 按业务表现不同而不同,归纳如下:
现金支付 
JSAPI 唤起微信支付收银台场景
use WeChatPay\Formatter;
use WeChatPay\Crypto\Hash;
$nonceStr = Formatter::nonce();
$timeStamp = (string) Formatter::timestamp();
$package = 'prepay_id=' . $prepayId;
// previousSignType 即后台调用 unifiedorder 接口时的签名方法
$signType = $previousSignType ?? Hash::ALGO_MD5;
$collection = [
   'appId' => $appId,
   'timeStamp' => $timeStamp,
   'nonceStr' => $nonceStr,
   'package' => $package,
   'signType' => $signType
 ];
$collection['paySign'] = Hash::sign($signType, $collection, $key);
echo \json_encode($collection);APP 唤起微信支付收银台场景
use WeChatPay\Formatter;
use WeChatPay\Crypto\Hash;
$nonceStr = Formatter::nonce();
$timeStamp = (string) Formatter::timestamp();
$package = 'Sign=WXPay';
// previousSignType 即后台调用 unifiedorder 接口时的签名方法
$signType = $previousSignType ?? Hash::ALGO_MD5;
$collection = [
   'appid' => $appId,
   'partnerid' => $mchId,
   'timestamp' => $timeStamp,
   'noncestr' => $nonceStr,
   'package' => $package,
   'prepayid' => $prepayId
 ];
$collection['sign'] = Hash::sign($signType, $collection, $key);
echo \json_encode($collection);小程序红包 特殊规则 
微信小程序 唤起发放小程序红包场景
use WeChatPay\Formatter;
use WeChatPay\Crypto\Hash;
$nonceStr = Formatter::nonce();
$timeStamp = (string) Formatter::timestamp();
// 签名方法为固定值
$signType = Hash::ALGO_MD5;
$collection = [
  'appId' => $appId,
  'timeStamp' => $timeStamp,
  'nonceStr' => $nonceStr,
  // $package 即后台调用 sendminiprogramhb 接口的 package 返回字符串
  'package' => \urlencode($package)
 ];
$collection['paySign'] = Hash::md5(
  Formatter::queryStringLike(
    Formatter::ksort($collection)
 ), $key
);
$collection['signType'] = $signType;
echo \json_encode($collection);代金券/商家券 
小程序发券插件
use WeChatPay\Crypto\Hash;
$sendCouponParams = [[
  'stock_id' => '123',
  'out_request_no' => 'R123'
], [
  'stock_id' => '456',
  'out_request_no' => 'R456'
]];
$collection = ['send_coupon_merchant' => $sendCouponMerchant];
foreach ($sendCouponParams as $index => $coupon) {
   foreach ($coupon as $key => $value) {
     $collection[$key . $index] = $value;
   }
}
// 签名方法为固定值
$signType = Hash::ALGO_HMAC_SHA256;
$collection['sign'] = Hash::sign($signType, $collection, $key); 
echo \json_encode($collection);通过Url跳转,由商户H5重定向至指定微信支付H5页面
use WeChatPay\Crypto\Hash;
use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\Psr7\Query;
// 签名方法为固定值
$signType = Hash::ALGO_HMAC_SHA256;
$collection = [
  'stock_id' => $stockId,
  'out_request_no' => $outRequestNo,
  'send_coupon_merchant' => $sendCouponMerchant,
  'open_id' => $openId,
  'coupon_code' => $couponCode
];
$collection['sign'] = Hash::sign($signType, $collection, $key); 
$actionUrl = 'https://action.weixin.qq.com/busifavor/getcouponinfo';
$redirectUrl = (string) (new Uri($actionUrl))
 ->withQuery(Query::build($collection))
 ->withFragment('wechat_pay&wechat_redirect');微信支付分 
APP 唤起微信支付分小程序确认订单场景
use WeChatPay\Formatter;
use WeChatPay\Crypto\Hash;
use GuzzleHttp\Psr7\Query;
$nonceStr = Formatter::nonce();
$timeStamp = (string) Formatter::timestamp();
// 签名方法为固定值
$signType = Hash::ALGO_HMAC_SHA256;
$collection = [
  'mch_id' => $mchId,
  'package' => $package,
  'timestamp' => $timeStamp,
  'nonce_str' => $nonceStr,
  'sign_type' => $signType
];
$collection['sign'] = Hash::sign($signType, $collection, $key); 
$data = [
  'businessType' => 'wxpayScoreUse',
  'query' => Query::build($collection),
  'extInfo' => [
    'miniProgramType' => 0
  ]
];
echo \json_encode($data);APP 唤起微信支付分小程序订单详情场景
use WeChatPay\Formatter;
use WeChatPay\Crypto\Hash;
use GuzzleHttp\Psr7\Query;
$nonceStr = Formatter::nonce();
$timeStamp = (string) Formatter::timestamp();
// 签名方法为固定值
$signType = Hash::ALGO_HMAC_SHA256;
$collection = [
  'mch_id' => $mchId,
  'service_id' => $serviceId,
  'out_order_no' => $outOrderNo,
  'timestamp' => $timeStamp,
  'nonce_str' => $nonceStr,
  'sign_type' => $signType
];
$collection['sign'] = Hash::sign($signType, $collection, $key); 
$data = [
  'businessType' => 'wxpayScoreDetail',
  'query' => Query::build($collection),
  'extInfo' => [
    'miniProgramType' => 0
  ]
];
echo \json_encode($data);JSAPI 唤起微信支付分小程序确认订单场景
use WeChatPay\Formatter;
use WeChatPay\Crypto\Hash;
use GuzzleHttp\Psr7\Query;
$nonceStr = Formatter::nonce();
$timeStamp = (string) Formatter::timestamp();
// 签名方法为固定值
$signType = Hash::ALGO_HMAC_SHA256;
$collection = [
  'mch_id' => $mchId,
  'package' => $serviceId,
  'timestamp' => $timeStamp,
  'nonce_str' => $nonceStr,
  'sign_type' => $signType
];
$collection['sign'] = Hash::sign($signType, $collection, $key); 
$data = [
  'businessType' => 'wxpayScoreUse',
  'queryString' => Query::build($collection)
];
echo \json_encode($data);JSAPI 唤起微信支付分小程序订单详情场景
use WeChatPay\Formatter;
use WeChatPay\Crypto\Hash;
use GuzzleHttp\Psr7\Query;
$nonceStr = Formatter::nonce();
$timeStamp = (string) Formatter::timestamp();
// 签名方法为固定值
$signType = Hash::ALGO_HMAC_SHA256;
$collection = [
  'mch_id' => $mchId,
  'service_id' => $serviceId,
  'out_order_no' => $outOrderNo,
  'timestamp' => $timeStamp,
  'nonce_str' => $nonceStr,
  'sign_type' => $signType
];
$collection['sign'] = Hash::sign($signType, $collection, $key); 
$data = [
  'businessType' => 'wxpayScoreDetail',
  'queryString' => Query::build($collection)
];
echo \json_encode($data);微信小程序 唤起微信支付分小程序确认订单场景
use WeChatPay\Formatter;
use WeChatPay\Crypto\Hash;
$nonceStr = Formatter::nonce();
$timeStamp = (string) Formatter::timestamp();
// 签名方法为固定值
$signType = Hash::ALGO_HMAC_SHA256;
$collection = [
  'mch_id' => $mchId,
  'package' => $serviceId,
  'timestamp' => $timeStamp,
  'nonce_str' => $nonceStr,
  'sign_type' => $signType
];
$collection['sign'] = Hash::sign($signType, $collection, $key); 
$data = [
  'businessType' => 'wxpayScoreUse',
  'extraData' => $collection
];
echo \json_encode($data);微信小程序 唤起微信支付分小程序订单详情场景
use WeChatPay\Formatter;
use WeChatPay\Crypto\Hash;
$nonceStr = Formatter::nonce();
$timeStamp = (string) Formatter::timestamp();
// 签名方法为固定值
$signType = Hash::ALGO_HMAC_SHA256;
$collection = [
  'mch_id' => $mchId,
  'service_id' => $serviceId,
  'out_order_no' => $outOrderNo,
  'timestamp' => $timeStamp,
  'nonce_str' => $nonceStr,
  'sign_type' => $signType
];
$collection['sign'] = Hash::sign($signType, $collection, $key); 
$data = [
  'businessType' => 'wxpayScoreDetail',
  'extraData' => $collection
];
echo \json_encode($data);非对称密钥算法 
此种签名方法,官方文档介绍 已经很明晰,这里不再细述规则,仅做实现介绍如下:
后台数据交换 
请求数据签名,用本开发包如下:
use WeChatPay\Formatter;
use WeChatPay\Crypto\Rsa;
/** @var string 'GET'|'POST'|'PUT'|'PATCH'|'DELETE' */
$httpMethod;
/** @var string */
$pathname;
/** @var string */
$body;
/** @var \OpenSSLAsymmetricKey|resource|string 商户API私钥实例 */
$merchantPrivateKeyInstance;
Rsa::sign(
  Formatter::request(
     $httpMethod,
     $pathname,
     (string) Formatter::timestamp(),
     Formatter::nonce(),
     $body
  ),
  $merchantPrivateKeyInstance
);返回值数据验签,用本开发包如下:
use WeChatPay\Formatter;
use WeChatPay\Crypto\Rsa;
/** @var string 请求返回的头'wechatpay-serial'值 */
$serial;
/** @var string 请求返回的头'wechatpay-timestamp'值 */
$timestamp;
/** @var string 请求返回的头'wechatpay-nonce'值 */
$nonce;
/** @var string 请求返回的头'wechatpay-signature'值 */
$signature;
/** @var string 请求返回的body */
$body;
/** @var array 微信支付平台证书/平台公钥键值对 */
$platformPublickKeyMap;
Rsa::verify(
  Formatter::response(
     $timestamp,
     $nonce,
     $body
  ),
  $signature,
  $platformPublickKeyMap[$serial]
);前台数据交换 
JSAPI 唤起微信支付收银台场景
use WeChatPay\Formatter;
use WeChatPay\Crypto\Rsa;
/** @var \OpenSSLAsymmetricKey|resource|string 商户API私钥实例 */
$merchantPrivateKeyInstance;
$nonceStr = Formatter::nonce();
$timeStamp = (string) Formatter::timestamp();
$extra = $usedWeixinCreditPaySubsidy ? "&subsidy_period_type=PERIOD&selected_installment_number={$num}" : ''; //微信分付(商户补贴)额外传参
$package = 'prepay_id=' . $prepayId . $exta;
$data = [
  'appId' => $appId,
  'timeStamp' => $timeStamp,
  'nonceStr' => $nonceStr,
  'package' => $package,
  'signType' => 'RSA',
  'paySign' => Rsa::sign( 
    Formatter::joinedByLineFeed($appId, $timeStamp, $nonceStr, $package),
    $merchantPrivateKeyInstance
  )
];
echo \json_encode($data);APP 唤起微信支付收银台场景
use WeChatPay\Formatter;
use WeChatPay\Crypto\Rsa;
/** @var \OpenSSLAsymmetricKey|resource|string 商户API私钥实例 */
$merchantPrivateKeyInstance;
$noncestr = Formatter::nonce();
$timestamp = (string) Formatter::timestamp();
$extra = $usedWeixinCreditPaySubsidy ? "&subsidy_period_type=PERIOD&selected_installment_number={$num}" : ''; //微信分付(商户补贴)额外传参
$data = {
  'appid' => $appId,
  'partnerid' => $mchId,
  'prepayid' => $prepayId,
  'package' => 'Sign=WXPay' . $extra,
  'timestamp' => $timestamp,
  'noncestr' => $noncestr,
  'sign' => Rsa::sign( 
    Formatter::joinedByLineFeed($appId, $timestamp, $noncestr, $prepayid),
    $merchantPrivateKeyInstance
  )
};
echo \json_encode($data);