1、怎么判断当前请求的API是否属于请求参数里带Body参数(包体参数)?
你可以点击查看一个API接口说明文档,如JSAPI下单接口,如下图,可以看到该接口请求参数部分带有Body参数,因为不同的参数类型构造请求签名略有不同,
为了方便大家理解,我们将使用一对测试的公私钥,通过命令行形式调用JSAPI下单接口,一步一步介绍请求参数里带body参,如何计算签名。强烈建议你使用测试的公私钥,按照本文实际操作一下,如果算出来的签名和文档的一致则说明计算无误。实际业务中请替换为你的真实的参数。
假设你的body参数如下,接下来将演示一步步如何构造请求签名

1appid = "wxd678efh567hg6787"
2mchid = "1900007291"
3description = "Image形象店-深圳腾大-QQ公仔"
4out_trade_no = "1217752501201407033233368018"
5notify_url = "https://www.weixin.qq.com/wxpay/pay.php"
6amount = {"total":100,"currency":"CNY"}
7payer = {"openid":"oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"}
2. 如何构造请求签名
1. 获取商户API证书
此处我们已经拿到了商户API证书,并且将对应的商户API私钥apiclient_key.pem,保存在了本地
证书序列号:408B07E79B8269FEC3D5D3E6AB8ED163A6A380DB
API证书私钥:你需要将以下文件保存为pem格式,为了避免大家和自己的真实的公私钥混淆,此处将其保存为了apiclient_test_key.pem
| 注意: 以下只是一个示例密钥,实际上并不可用,且示例的密钥是以-----BEGIN RSA PRIVATE KEY-----,实际你获取到的商户API私钥是以-----BEGIN PRIVATE KEY----- |
|

1-----BEGIN PRIVATE KEY-----
2MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCm2mb6q8gMKH/3
3CNTbpJAIrbqiBiQGEOtjGcBrDYltsGynWgNscqT7WvfzU14FQbYcQUC5T4Wvva7m
4i3fIp3OgX8VqMDNA0qebnr38Pe6kqiLyZgFpJPXlSKDyPyqhRbVTbXssvSMQeVKc
5dXeVxoNNeoOlNFHgF/P0io6AmAVnz+hN8SiZKuOsth5/zUTLGvtkgxBcQooQrtXh
6RcpLT798OyIb9xeJ2HO3xRtMv2+perEzb4gMibI74UBz+2QEbnkubPE+2jU2rRZu
7dnNEz/BPOt3Qj/w2V6/G0VumGDh6+UeMU0jv4aupHztWITC4Akn0l7lBCNy3lgl8
8VFaJnkIxAgMBAAECggEAYGL8aESB7NwciDWW2UdoWUsa7GxFtSdjAz2mFXGdeTsY
9mVh7b9OOkRGM+Qio4LqEHDBp1mMk5E/cUJwy1zw8pGGO5nfvs7u9TT3XnHaefIs4
10YvUgTYAneIuLRkXNN5rQU+CD7mVYczTSz0Vgjqo9wa1LjUz7G0xbBmJgTdMEFGJs
11eJjy6AbJo0CGIwp6HJbTm4CmOUgXnnDAIbEGTIRImkZFH/rzneIeR7oZ77FVwxr1
12CZB2gfRCov/yRPbw8vnryYkmvQ7D/ze3j5097vRg/MoDGBSdoOwcmo75vyofr0AS
13zytMjmHYyifqkf5slPropSiJeGf4p/7gtKyF6dE/XQKBgQDVAlJ+4U5ZVGOuDc3+
14sAhz8CTzgFNlq9vKuSoFK6hOz2L+cwj+E7NXGkOe2DsHHZNy2Xqxk7caKhPEp1z9
15hhpMpyLVMoFt6CKemyoRBWDCQwLLwem9SZF/IAyovBkLiH36P42Jm26gUkNMKC/5
16Zhtqxf6RZgRQzbVudJi47vIRCwKBgQDIh0+v27Oo+DM3fhObH4I1NrXpWOEGH7OQ
17G1dEsMuFYF4hjGhg0kBEP3w9vVdl2+mRllZKTsx9oqjb8OibPLLIH8xsdbAB0WLf
18JvjLu4wl/ILUzN1RI03dWnnv2EnEeQn6c3hizvrJ9wR5U4ue9RPVnQooJ0hZF1PU
19uCL5fWK3MwKBgElReU/PAYbh80WP3t3Rfbdaa32dKBeQ5iCLR5lsA4zM+YgX1HqQ
20EWTj126vgvHaDkyz6vWAoL/Sx+cirHFfXWIRDX5Q2hgYlQH+6qXdMgbrxeSYpHnQ
21/tHBGFpkFELSAnrGsVMyOwvYBO4LzyeLK9i+ufcWJFoj1FVmsMLHDG8tAoGARdbi
22iQQCoYG4DMarO2aQ6cmhN6EN1h0qY7EyBqlwaIZ0okiNfdMcMOjPc41DKCWcRmlO
23qlihXcxN9TQFPzO3rH1urAOdBjUPs1qWYhZyrDQyuLyVBBJApyxAtajloDjrob+f
24mQIvVDHk7ACN6xG+E7K6+9salnTKbJapD618uQMCgYBNy6XUvzLkP/A1U/UZdtcx
25l8GwU/dturLxz4CyGbqDw4ubaYY2e13lnqHUqQgPtiSyH51nq3tdo8G0YAJdfkSv
26KvnfslW91fyEBUKnkdW1o3/1UFU/wprZ7ixVL/F42A4xDu7OFE8EnweJOZ0jWceE
27OdhCkaIGBCfRnlECRK8UyQ==
28-----END PRIVATE KEY-----
2. 构造签名串
我们希望商户的技术开发人员按照当前文档约定的规则构造签名串。微信支付会使用同样的方式构造签名串。如果商户构造签名串的方式错误,将导致签名验证不通过。下面先说明签名串的具体格式。
签名串一共有5个部分,每一行为一个参数。结尾以\n(换行符,ASCII编码值为0x0A)结束,包括最后一行。如果参数本身以\n结束,也需要附加一个\n。

1HTTP请求方法\n
2URL\n
3请求时间戳\n
4请求随机串\n
5请求报文主体\n

1POST
第二步,获取请求的绝对URL,请注意需要去除域名部分。

1/v3/pay/transactions/jsapi
第三步,获取发起请求时的系统当前时间戳,即格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数,作为请求时间戳。微信支付会拒绝处理很久之前发起的请求,请商户保持自身系统的时间准确。

1$ date +%s
21554208460
第四步,生成一个请求随机串,我们推荐生成随机数算法如下:调用随机数函数生成,将得到的值转换为字符串。这里,我们使用命令行直接生成一个。

1$ hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random
2593BEC0C930BF1AFEB40B4A08C8FB242
第五步,获取请求中的请求报文主体(request body)。
你可以将所有参数放在一行,对应的发起签名请求的时候body参数也应该放在一行。
你也可以将参数以多行计算签名,对应发起请求的时候body参数也要是多行。即你计算签名时body是怎么样的,你发起请求时body就应该是怎么样的。
这里以将所有参数放到一行做演示,如果你想了解将参数放在多行可以参考文末说明

1{"appid":"wxd678efh567hg6787","mchid":"1900007291","description":"Image形象店-深圳腾大-QQ公仔","out_trade_no":"1217752501201407033233368018","notify_url":"https://www.weixin.qq.com/wxpay/pay.php","amount":{"total":100,"currency":"CNY"},"payer":{"openid":"oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"}}

1POST\n
2/v3/pay/transactions/jsapi\n
31554208460\n
4593BEC0C930BF1AFEB40B4A08C8FB242\n
5{"appid":"wxd678efh567hg6787","mchid":"1900007291","description":"Image形象店-深圳腾大-QQ公仔","out_trade_no":"1217752501201407033233368018","notify_url":"https://www.weixin.qq.com/wxpay/pay.php","amount":{"total":100,"currency":"CNY"},"payer":{"openid":"oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"}}\n
| 注意: 当请求报文主体是一个空串时,只需要附加一个\n即可,例如对于平台证书下载证书的接口,你构造的签名串如下: |
|

1GET\n
2/v3/certificates\n
31554208460\n
4593BEC0C930BF1AFEB40B4A08C8FB242\n
5\n
3. 计算签名值
绝大多数编程语言提供的签名函数支持对签名数据进行签名。强烈建议商户调用该类函数,使用商户私钥对待签名串进行SHA256 with RSA签名,并对签名结果进行Base64编码得到签名值。
请注意处理单双引号转义问题,第二行的外层是单引号,则里面的参数不需要转义,如果第二行最外层使用了双引号,则body参数的双引号都需要转义

1$ echo -n -e \
2 'POST\n/v3/pay/transactions/jsapi\n1554208460\n593BEC0C930BF1AFEB40B4A08C8FB242\n{"appid":"wxd678efh567hg6787","mchid":"1900007291","description":"Image形象店-深圳腾大-QQ公仔","out_trade_no":"1217752501201407033233368018","notify_url":"https://www.weixin.qq.com/wxpay/pay.php","amount":{"total":100,"currency":"CNY"},"payer":{"openid":"oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"}}\n' \
3 | openssl dgst -sha256 -sign apiclient_test_key.pem \
4 | openssl base64 -A
得出的签名值如下,你可以用一些校验工具校验你通过命令行或者你的代码得到的签名值是否和以下值一样,一样则表示计算过程无误,如果不一致则请检查签名的参数是否一致、签名串是否有严格换行等

1jnks4dlrPw3ZX+ozVvSK39oa0t7OMBsg83BHAwd8BRdUFiVaQNTLTvci+wURgP1OQBbKYhFGvt7iqYpDSTQkp7Uq1sltaQKyncCyrA1g88m5bsKERQfPyT0ahSwKTYJ1CAn9QiJuSJRq1QsQs07eehbU/k9BCS51jTyc1Jpsi2H77HF9f/BnjXAOP3/sPObg6V5Ee4EzwLox684hhuMuIwHo7D8KFk3LIHOKDcNI4It1aCXydFWNpNK+SG86VUDe5kwoDpw4Ulqfu9z8OFDGbDs9TCxEv8iqQzbpxOlEVoOe2kalSYM5kApQb3nZcxdUtoE0liJGW3RGUNE0t4v01A==
4. 设置HTTP头
微信支付商户API v3要求请求通过HTTP Authorization头来传递签名。Authorization由认证类型和签名信息两个部分组成。

1Authorization: 认证类型 签名信息
认证类型,目前为WECHATPAY2-SHA256-RSA2048
签名信息
发起请求的商户(包括直连商户、服务商或渠道商)的商户号mchid
商户API证书序列号serial_no,用于声明所使用的证书
请求随机串nonce_str,和你上面构造签名串的随机串要保持一致
时间戳timestamp,和你上面构造签名串的时间戳要保持一致
签名值signature,上面算出来的签名值
Authorization头的示例如下:(注意,示例因为排版可能存在换行,实际数据应在一行)

1Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1900007291",nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242",signature="jnks4dlrPw3ZX+ozVvSK39oa0t7OMBsg83BHAwd8BRdUFiVaQNTLTvci+wURgP1OQBbKYhFGvt7iqYpDSTQkp7Uq1sltaQKyncCyrA1g88m5bsKERQfPyT0ahSwKTYJ1CAn9QiJuSJRq1QsQs07eehbU/k9BCS51jTyc1Jpsi2H77HF9f/BnjXAOP3/sPObg6V5Ee4EzwLox684hhuMuIwHo7D8KFk3LIHOKDcNI4It1aCXydFWNpNK+SG86VUDe5kwoDpw4Ulqfu9z8OFDGbDs9TCxEv8iqQzbpxOlEVoOe2kalSYM5kApQb3nZcxdUtoE0liJGW3RGUNE0t4v01A==",timestamp="1554208460",serial_no="408B07E79B8269FEC3D5D3E6AB8ED163A6A380DB"

最终我们可以组一个包含了签名的HTTP请求了。
(1)请注意第六行的body参数必须在一行,不能换行,因为签名计算签名时body就是以一行来计算签名的,这里发起请求时需要和签名时的保持一致
(2)请注意Authorization的timestamp="1554208460",serial_no="408B07E79B8269FEC3D5D3E6AB8ED163A6A380DB"必须和你计算签名是传入的值是一致的
(3)此处只是一个示例,用于大家参考计算的格式,由于示例密钥本身是不可用的,因此以下请求并不真正可用

1curl -X POST \
2 https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi \
3 -H 'Authorization: WECHATPAY2-SHA256-RSA2048 mchid="1900007291",nonce_str="593BEC0C930BF1AFEB40B4A08C8FB242",signature="jnks4dlrPw3ZX+ozVvSK39oa0t7OMBsg83BHAwd8BRdUFiVaQNTLTvci+wURgP1OQBbKYhFGvt7iqYpDSTQkp7Uq1sltaQKyncCyrA1g88m5bsKERQfPyT0ahSwKTYJ1CAn9QiJuSJRq1QsQs07eehbU/k9BCS51jTyc1Jpsi2H77HF9f/BnjXAOP3/sPObg6V5Ee4EzwLox684hhuMuIwHo7D8KFk3LIHOKDcNI4It1aCXydFWNpNK+SG86VUDe5kwoDpw4Ulqfu9z8OFDGbDs9TCxEv8iqQzbpxOlEVoOe2kalSYM5kApQb3nZcxdUtoE0liJGW3RGUNE0t4v01A==",timestamp="1554208460",serial_no="408B07E79B8269FEC3D5D3E6AB8ED163A6A380DB"' \
4 -H 'Accept: application/json' \
5 -H 'Content-Type: application/json' \
6 -d '{"appid":"wxd678efh567hg6787","mchid":"1900007291","description":"Image形象店-深圳腾大-QQ公仔","out_trade_no":"1217752501201407033233368018","notify_url":"https://www.weixin.qq.com/wxpay/pay.php","amount":{"total":100,"currency":"CNY"},"payer":{"openid":"oUpF8uMuAJO_M2pxb1Q9zNjWeS6o"}}'
5. 演示代码
SDK自带自动构造签名功能,开发者可以查看SDK相关章节,获取对应语言的库。