啰嗦几句:微信支付v3版接口麻烦吗?在对接微信支付v3接口时,本来是一件很简单的事情,其实微信支付v3接口并不是很复杂,但是微信团队的管理很混乱,给我们开发者带来了巨大的麻烦。
微信支付v3版接口对接麻烦-问题出在了哪?
- 其一:微信支付的版本较多,没有形成一个统一管理说明;
- 其二:微信v3支付,没有一个完整的说明文档,文档都很分散;
- 其三:微信支付官方文档看似很详细,其实很多关键点都没说明白,新手看着就很头疼;
下面详细的说一下微信支付v3接口的开发
这个版本整合微信官方文档,以微信小程序开发为基础,大家按步骤点开链接查看操作。
对接微信支付API v3
前提:商户号已注册好,准备就绪。
- 接入前准备
注意:操作过程中产生的APIv3秘钥记录下来,有的后面会用到。 例如 API v3秘钥:B3AQsC17C6UFooIRCAaXRUvaq8PInN60
微信商户平台接入说明文档
[微信商户平台](https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml)

说明:这个是小程序版,如果是其他平台可以在左侧找对应目录
注意:这个流程走下来你会拿到一个压缩文件类似下图

图:这个压缩文件里有三个证书(解压后如下图),三个证书文件拷贝到你的开发平台里,使用方式继续往下看

- 扫码进入商户平台的入口
微信商户平台
[微信商户平台](https://pay.weixin.qq.com)
说明:使用管理者的微信扫码进入,进入后台根据上面的说明进行操作。如果没有注册可以直接走注册流程。注册流程这里不讲解。
- 扫码小程序后台的入口
微信小程序后台
[微信小程序后台](https://mp.weixin.qq.com/)
注意:如果没有申请小程序可以进入这里看怎么申请
申请小程序说明
- 小程序后台开通微信支付并绑定商户平台说明
小程序后台开通微信支付并绑定商户平台说明
[小程序后台开通微信支付并绑定商户平台说明](https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml)
注意:查看说明中最后的【5.配置应用】进行操作
- 获取商户API证书序列号(merchantSerialNumber)
进入证书查看网站
[进入证书查看网站](https://myssl.com/cert_decode.html)

说明:上传证书文件:apiclient_cert.pem,点击查看证书,里面会有个序列号,复制出来。下载微信支付平台证书会用到。
例如:
证书序列号:54776TTTF8F77EXXX3641FAB5F940FII11C65347
- 下载微信支付平台证书方法
微信支付平台证书下载工具
[微信支付平台证书下载工具](https://github.com/wechatpay-apiv3/CertificateDownloader)
说明:在这里下载 CertificateDownloader.jar 文件,按照里面的文档操作即可
注意:我的操作说明,我是把下载好的 CertificateDownloader.jar 文件,放在了解压证书的目录里。这样我取mchPrivateKeyFilePath这个值的时候就可以直接写证书文件名,不用写路径了。
官方完整命令如:
|
java -jar CertificateDownloader.jar -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath} -c ${wechatpayCertificateFilePath} |
我的操作例如:
|
java –jar CertificateDownloader.jar -k B3AQsC17C6UFooIRCAaXRUvaq8PInN60 -m 1901174254 -f apiclient_key.pem -s 54776TTTF8F77EXXX3641FAB5F940FII11C65347 -o file |
注意:1.操作前 CertificateDownloader.jar 和 apiclient_key.pem 文件在同一个目录 。并且在这个目录中进入cmd命令工具。cmd命令工具中显示的目录就是CertificateDownloader.jar所在目录。
例如:
|
文件-CertificateDownloader.jar 路径:D:\WXCertUtil\cert\CertificateDownloader.jar |
|
文件-apiclient_key.pem 路径:D:\WXCertUtil\cert\apiclient_key.pem |
|
cmd中路径:D:\WXCertUtil\cert> |
注意:2.命令中 -o file 只是个文件夹,命令执行后会在下面路径中生成一个微信支付平台证书文件。把这个证书也复制到你的平台中,和上面三个证书放在一个位置方便管理。
例如:
D:\WXCertUtil\cert\file\wechatpay_3A4AF69999DF01F39BB08C21C1C29B6AA17C074N.pem
至此,所有微信支付v3的准备工作已就绪,接下来,使用微信官方SDK开发接入微信支付v3。
Java平台接入微信支付v3接口
更多平台看官方文档
微信支付 APIv3 Java SDK
微信支付 APIv3 Java SDK,里面有详细说明
github 地址:https://github.com/wechatpay-apiv3/wechatpay-java
注意:开发可以根据这里的说明操作就好了,下面我写一下可能大家不理解的点。
com.github.wechatpay-apiv3:wechatpay-java
代码中的配置-例如:
|
|
|
public static String merchantId = “1901174254”; |
|
|
|
public static String privateKeyPath = “apiclient_key.pem”; |
|
|
|
public static String merchantSerialNumber = “54776TTTF8F77EXXX3641FAB5F940FII11C65347”; |
|
|
|
public static String wechatPayCertificatePath = “wechatpay_3A4AF69999DF01F39BB08C21C1C29B6AA17C074N.pem”; |
|
|
|
|
|
public static String apiV3Key = “B3AQsC17C6UFooIRCAaXRUvaq8PInN60”; |
如果微信支付平台证书,已经下载好了,github里这个代码就不用看了。
|
package com.wechat.pay.java.service; |
|
|
|
import com.wechat.pay.java.core.Config; |
|
import com.wechat.pay.java.core.RSAConfig; |
|
import com.wechat.pay.java.service.certificate.CertificateService; |
|
import java.nio.charset.StandardCharsets; |
|
import java.security.cert.X509Certificate; |
|
import java.util.List; |
|
|
|
|
|
public class QuickStart { |
|
|
|
|
|
public static String merchantId = “”; |
|
|
|
public static String privateKeyPath = “”; |
|
|
|
public static String merchantSerialNumber = “”; |
|
|
|
public static String wechatPayCertificatePath = “”; |
|
|
|
public static String apiV3Key = “”; |
|
|
|
public static void main(String[] args) { |
|
Config config = |
|
new RSAConfig.Builder() |
|
.merchantId(merchantId) |
|
.privateKeyFromPath(privateKeyPath) |
|
.merchantSerialNumber(merchantSerialNumber) |
|
.wechatPayCertificatesFromPath(wechatPayCertificatePath) |
|
.build(); |
|
CertificateService certificateService = new CertificateService.Builder().config(config).build(); |
|
List<X509Certificate> certificates = |
|
certificateService.downloadCertificate(apiV3Key.getBytes(StandardCharsets.UTF_8)); |
|
} |
|
} |
支付的第一步:微信支付前需要拿到预支付id(prepayId),才能支付,所有支付的第一步是预支付
|
import com.wechat.pay.java.core.Config; |
|
import com.wechat.pay.java.core.RSAConfig; |
|
import com.wechat.pay.java.service.payments.jsapi.JsapiService; |
|
import com.wechat.pay.java.service.payments.jsapi.model.Amount; |
|
import com.wechat.pay.java.service.payments.jsapi.model.Payer; |
|
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest; |
|
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse; |
|
|
|
public class JsapiExample { |
|
public static void main(String[] args) { |
|
Config config = |
|
new RSAConfig.Builder() |
|
|
|
.merchantId(merchantId) |
|
|
|
.privateKeyFromPath(privateKeyPath) |
|
|
|
.merchantSerialNumber(merchantSerialNumber) |
|
|
|
.wechatPayCertificatesFromPath(wechatPayCertificatePath) |
|
.build(); |
|
|
|
JsapiService service = new JsapiService.Builder().config(config).build(); |
|
|
|
PrepayRequest request = new PrepayRequest(); |
|
Amount amount = new Amount(); |
|
amount.setTotal(100); |
|
request.setAmount(amount); |
|
request.setAppid(“wxa9d9651ae******”); |
|
request.setMchid(“190000****”); |
|
request.setDescription(“测试商品标题”); |
|
request.setNotifyUrl(“https://notify_url”); |
|
request.setOutTradeNo(“out_trade_no_001”); |
|
Payer payer = new Payer(); |
|
payer.setOpenid(“oLTPCuN5a-nBD4rAL_fa********”); |
|
request.setPayer(payer); |
|
|
|
PrepayResponse response = service.prepay(request); |
|
System.out.println(response.getPrepayId()); |
|
} |
|
} |
-
如果你的秘钥文件,跟我一样放在resources目录下,可以使用下面方法获取路径

|
|
|
import org.springframework.core.io.ClassPathResource; |
|
|
|
ClassPathResource resourcePrivateKey = new ClassPathResource(“apiclient_key.pem”); |
|
ClassPathResource resourceWeixinPayCert = new ClassPathResource(“wechatpay_3A4AF69999DF01F39BB08C21C1C29B6AA17C074N.pem”); |
|
String privateKeyFromPath = resourcePrivateKey.getFile().getPath(); |
|
String wechatPayCertificatesFromPath = resourceWeixinPayCert.getFile().getPath(); |
还需要知道的:微信平台给的用户openid,并不是一个用户的唯一ID,不能多个小程序同时使用。也就是说,同一个用户在小程序A中和在小程序B中拿到的用户openid 是不一样的。
微信小程序调起支付 wx.requestPayment
- 先看官方说明文档了解一下基本使用原理
官方说明文档
[官方说明文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml#menu1)
- 看完官方文档后知道了,想要成功拉起支付,还是需要一些操作的
1. 首先需要的就是签名,签名后得到的值参考字段paySign
2. 签名是在小程序里完成,还是在后台完成,这是第一个疑问
3. 看下例如对参数的介绍,理解一下每个参数怎么来的。
例如:
|
wx.requestPayment |
|
( |
|
{ |
|
|
|
“timeStamp”: “1414561699”, |
|
|
|
“nonceStr”: “5K8264ILTKCH16CQ2502SI8ZNMTM67VS”, |
|
|
|
“package”: “prepay_id=wx201410272009395522657a690389285100”, |
|
|
|
“signType”: “RSA”, |
|
|
|
“paySign”: “oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==”, |
|
“success”:function(res){}, |
|
“fail”:function(res){}, |
|
“complete”:function(res){} |
|
} |
|
) |
注意:从上面代码的解释中可以看出,下面的这几个字段都是从后台拿到的
|
|
|
“timeStamp”: “1414561699”, |
|
|
|
“nonceStr”: “5K8264ILTKCH16CQ2502SI8ZNMTM67VS”, |
|
|
|
“package”: “prepay_id=wx201410272009395522657a690389285100”, |
|
|
|
“paySign”: “oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==” |
-
后台生成微信小程序支付需要的支付签名paySign(Java 后台)
例如(这里使用的是微信支付官方Java SDK实现的):
微信支付官方Java SDK入口
[微信支付官方Java SDK入口](https://github.com/wechatpay-apiv3/wechatpay-java)
|
import com.wechat.pay.java.core.RSAConfig; |
|
import com.wechat.pay.java.core.util.IOUtil; |
|
import com.wechat.pay.java.core.util.PemUtil; |
|
import com.wechat.pay.java.core.cipher.RSASigner; |
|
import com.wechat.pay.java.core.cipher.SignatureResult; |
|
import com.wechat.pay.java.core.cipher.Signer; |
|
|
|
|
|
public SignInfo generateWeixinSigner(String prepayid){ |
|
String privateKeyStr = null; |
|
try { |
|
|
|
|
|
privateKeyStr = IOUtil.loadStringFromPath(privateKeyFromPath); |
|
} catch (IOException e) { |
|
e.printStackTrace(); |
|
} |
|
PrivateKey key = PemUtil.loadPrivateKeyFromString(privateKeyStr); |
|
|
|
|
|
|
|
|
|
|
|
String packageStr = String.format(“prepay_id=%s”, prepayid); |
|
SignInfo info = new SignInfo(); |
|
info.setAppId(appid); |
|
info.setTimeStamp(“” + System.currentTimeMillis()); |
|
info.setNonceStr(generateOrderNumber(30)); |
|
info.setPackageStr(packageStr); |
|
String str = info.toString(); |
|
|
|
Signer rsaSigner = new RSASigner(weixinMerchantSerialNumber, key); |
|
SignatureResult signatureResult = rsaSigner.sign(str); |
|
info.setPaySign(signatureResult.getSign()); |
|
return info; |
|
} |
|
|
|
import com.wechat.pay.java.core.util.StringUtil; |
|
import lombok.Data; |
|
|
|
@Data |
|
public class SignInfo { |
|
private String appId; |
|
private String timeStamp; |
|
private String nonceStr; |
|
private String packageStr; |
|
private String signType=“RSA”; |
|
private String paySign; |
|
|
|
@Override |
|
public String toString() { |
|
StringBuilder sb = new StringBuilder(); |
|
sb.append(StringUtil.toIndentedString(appId)).append(“\n”); |
|
sb.append(StringUtil.toIndentedString(timeStamp)).append(“\n”); |
|
sb.append(StringUtil.toIndentedString(nonceStr)).append(“\n”); |
|
sb.append(StringUtil.toIndentedString(packageStr)).append(“\n”); |
|
return sb.toString(); |
|
} |
|
} |
|
import org.apache.commons.lang3.RandomStringUtils; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public static String generateOrderNumber(int size) { |
|
String abc = “qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890”; |
|
|
|
|
|
String str = RandomStringUtils.random(size, abc); |
|
} |
-
如果你的秘钥文件,跟我一样放在resources目录下,可以使用下面方法获取路径
|
|
|
import org.springframework.core.io.ClassPathResource; |
|
|
|
ClassPathResource resourcePrivateKey = new ClassPathResource(“apiclient_key.pem”); |
|
ClassPathResource resourceWeixinPayCert = new ClassPathResource(“wechatpay_3A4AF69999DF01F39BB08C21C1C29B6AA17C074N.pem”); |
|
String privateKeyFromPath = resourcePrivateKey.getFile().getPath(); |
|
String wechatPayCertificatesFromPath = resourceWeixinPayCert.getFile().getPath(); |