我自己琢磨出来的,只不过我用的是微信wechatpay-apache-httpclient 0.4.4版本 1.常见术语说明 - 证书:也就是常规意义上的rsa证书,包括公钥和私钥,一般敏感信息(姓名、身份证、手机)等都是通过rsa进行加密解密 - 秘钥:在微信官方文档叫做秘钥,在微信官方sdk里面叫做aesKey或apiKey,其实就是用于aes解密的密钥,一般的AEAD_AES_256_GCM都是通过aes加密解密 - 商户API证书:商户从[微信支付](https://pay.weixin.qq.com/)申请的证书,在【账户中心】->【API安全】->【申请API证书】菜单申请,申请的是rsa证书(一般会有3个文件,第一个p12后缀文件,这个一般用不到,另外两个pem后缀的文件,是商户的rsa公钥和私钥),一般商户证书有5年有效期 - 商户API V3秘钥:注意不是上面商户API证书的私钥,而是商户在【账户中心】->【API安全】->【设置APIv3密钥 】菜单设置的秘钥,这个是32 byte的字符串(ascii字符就行,不建议使用特殊字符) - 平台证书:在微信官方文档也叫做“微信支付平台证书”,也就是微信官方使用的rsa公钥(私钥是微信官方持有),这个公钥比较特殊,需要我们使用接口下载,而且这个rsa公钥可能会到期,需要我们定期下载,目前微信官方的(wechatpay-apache-httpclient v0.4.4)内置了定期更新微信支付平台证书的功能,所以我们一般不需要考虑微信支付平台证书过期的问题 - 签名:微信支付的api在请求或者响应里面一般都需要进行签名,目前微信官方的(wechatpay-apache-httpclient v0.4.4)自动集成了签名的功能,我们直接使用即可 2.快速接入开发 2.1 商户配置类 注意下方CertificatesManager如果需要注册多个商户号,则需要使用putMerchant方法,下面因为只有一个商户号,所以直接在里面putMerchant,如果有多个商户号,则需要针对每个不同商户号的证书、密钥做对应处理 @Component @Slf4j public class WechatPayApiV3Config { /** * 商户号(我们自己的商户号,在微信支付服务商平台里面申请的商户号) */ @Value("${wechat.merchant.id}") private String mchId; /** * apiv3证书密钥,(微信支付平台 -【账户中心】-【api安全】-【设置APIv3密钥 】这里设置的密钥) */ @Value("${wechat.merchant.api.v3.key}") private String apiV3Key; /** * 商户api证书序列号 (微信支付平台 -【账户中心】-【api安全】-【申请API证书】这里申请的证书序列号) */ @Value("${wechat.merchant.certificate.serial}") private String mchSerialNo; /** * 商户rsa私钥文本,就是微信支付平台 -【账户中心】-【api安全】-【申请API证书】最终下载下来的rsa证书私钥文件的内容 */ private static String privateKey; /** * 商户rsa私钥文本转成PrivateKey对象 */ public static PrivateKey merchantPrivateKey; static { try (InputStream in = WechatPayApiV3Config.class.getClassLoader().getResourceAsStream("wechatpayapiv3/apiclient_key.pem")) { byte[] bytes = in.readAllBytes(); privateKey = new String(bytes, StandardCharsets.UTF_8); merchantPrivateKey = PemUtil.loadPrivateKey(privateKey); } catch (IOException e) { log.error("load api client key error: {}", ExceptionUtil.getMessage(e)); } } /** * CertificatesManager利用了ConcurrentHashMap存储多个商户的证书和密钥信息,所以支持多个商户号使用 * * @return * @throws GeneralSecurityException * @throws IOException * @throws HttpCodeException */ @Bean(destroyMethod = "stop") public CertificatesManager certificatesManager() throws GeneralSecurityException, IOException, HttpCodeException { CertificatesManager certificatesManager = CertificatesManager.getInstance(); certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8)); return certificatesManager; } /** * Verifier是通过CertificatesManager导出的,是严格关联到某一个商户号的,所以不同的商户号需要对应不同的Verifier,如果生产环境有多个商户号, * 则建议将不同商户号的Verifier缓存到Map里面,因为certificatesManager.getVerifier(mchId);每次都会创建一个新的Verifier对象,频繁创建对象不好 * * @param certificatesManager * @return * @throws NotFoundException */ @Bean public Verifier verifier(CertificatesManager certificatesManager) throws NotFoundException { return certificatesManager.getVerifier(mchId); } /** * CloseableHttpClient是微信支付的client,内置了http请求的签名以及签名解密验证功能,所以说我们一般无需手工处理签名的加密、解密、验证, * 而且CloseableHttpClient也是严格关联到某一个商户号的,所以不同的商户号需要对应不同的CloseableHttpClient,如果生产环境有多个商户号, * 则建议将不同商户号的CloseableHttpClient缓存到Map里面 * * @param verifier * @return */ @Bean public CloseableHttpClient wechatPayApiV3HttpClient(Verifier verifier) { return WechatPayHttpClientBuilder.create() .withMerchant(mchId, mchSerialNo, merchantPrivateKey) .withValidator(new WechatPay2Validator(verifier)) .build(); } } 2.2 手工生成signature签名 & 手工验证签名 我们发送请求给微信,都是需要对请求参数、url等进行签名,前面提到了微信的CloseableHttpClient内置了请求自动签名,响应签名解密验证,如果某些特殊情况下需要手工验证签名(例如:微信回调我们的服务) 注意:微信支付的签名都是使用rsa加密解密的。 2.2.1 商户侧生成签名 商户侧生成签名是需要使用商户自己的私钥进行加密,详情可以参考(wechatpay-apache-httpclient v0.4.4)WechatPay2Credentials的getToken方法,在这个方法中会使用到PrivateKeySigner对请求信息进行签名,一般通过微信的CloseableHttpClient发送的请求,内部自动做好了请求签名、响应签名验证,所以无需我们手工操作签名 2.2.2 商户侧验证签名 只有那些微信主动回调我们接口的地方,这时候就需要我们主动验证签名的有效性,防止黑客伪装签名数据,微信的响应或者回调,都会在http头部增加这么几个属性Wechatpay-Serial、Wechatpay-Signature、Wechatpay-Timestamp、Wechatpay-Nonce,我们拿到请求或响应body内容,接着通过verify进行验证签名 /** * 验证签名 * * @param body * @param nonce * @param serial * @param timestamp * @param signature * @return */ public boolean verify(String body, String nonce, String serial, String timestamp, String signature) { NotificationRequest request = new NotificationRequest.Builder().withSerialNumber(serial) .withNonce(nonce) .withTimestamp(timestamp) .withSignature(signature) .withBody(body) .build(); return verifier.verify(request.getSerialNumber(), request.getMessage(), request.getSignature()); } 2.3 数据加密解密 2.3.3 AEAD_AES_256_GCM解密 /** * aes解密 * * @param nonce * @param associatedData * @param ciphertext * @return */ public String aesDecrypt(String nonce, String associatedData, String ciphertext) throws GeneralSecurityException { AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(UTF_8)); return aesUtil.decryptToString(associatedData.getBytes(UTF_8), nonce.getBytes(UTF_8), ciphertext); } 2.3.3 敏感信息加密 发送给微信的敏感信息,需要使用微信支付平台证书(rsa公钥)进行加密,加密之后,还需要在请求头增加一个Wechatpay-Serial的头部,这个头部就是微信支付平台证书的序列号(如何拿到序列号参考下列代码) //微信支付平台证书(rsa公钥),在CertificatesManager底层会定期更新的 X509Certificate certificate = verifier.getValidCertificate(); //拿到证书序列号(10进制的内容) BigInteger serialNumber = certificate.getSerialNumber(); //转成16进制字符串 String wechatPaySerial = serialNumber.toString(16).toUpperCase(); //发送给微信的敏感信息加密 String encrypt = RsaCryptoUtil.encryptOAEP("明文内容", certificate); HttpPost post = new HttpPost(url); String requestBody = JSONObject.toJSONString(request); StringEntity entity = new StringEntity(requestBody, APPLICATION_JSON); post.setEntity(entity); //设置请求头部Wechatpay-Serial,注意这里是微信支付平台证书序列号,不是商户证书序列号,如果用了商户证书序列号则微信会返回{"code":"PARAM_ERROR","message":"平台证书序列号Wechatpay-Serial错误"} post.addHeader(WechatPayHttpHeaders.WECHAT_PAY_SERIAL, wechatPaySerial); //设置请求头部ACCEPT,需要设置accept请求头,否则微信api会报错 {"code":"INVALID_REQUEST","message":"头部信息不完整"} post.addHeader(ACCEPT, APPLICATION_JSON.toString()); CloseableHttpResponse response = wechatPayApiV3HttpClient.execute(post); int statusCode = response.getStatusLine().getStatusCode(); byte[] bytes = response.getEntity().getContent().readAllBytes(); String content = new String(bytes, UTF_8); 2.3.4 敏感信息解密 微信返回响应或者回调我们接口的敏感信息,微信使用了商户的公钥进行加密,所以我们自己需要用商户私钥解密 //解密密文信息 String message = RsaCryptoUtil.decryptOAEP("密文信息", WechatPayApiV3Config.merchantPrivateKey)
v3接口规则的回调验签怎么知道使用的哪个平台证书?看官方给的范例 回调通知是这个验签使用的verifier怎么获取呢?回调是全是密文的 如果要通过获取平台证书接口 是需要商户号的呀!![图片]
2022-04-22也没有收到告警,我设置的是30分钟内1次推送失败就告警 [图片]
从昨天开始一直收不到component_verify_ticket?从昨天上午09:56:24之后就一直没收到component_verify_ticket了,麻烦官方看一下 我们第三方平台appId: wxc5ef34cabc1d5587
2021-12-10我们的小程序也卡在审核了,都已经两天过去了还没审核
配置小程序用户隐私保护指引后,小程序审核时间变长?配置小程序用户隐私保护指引后,小程序审核时间变长 第三方平台:wx3b20a7ccedfab76c 小程序appid:wx6fe6a3bffc31120c
2021-11-18公网域名做一个nginx映射就行了
如何本地开发端调试接收component_verify_ticket ?如何本地开发端调试接收component_verify_ticket
2021-10-20返回xml是不是没有加密
小程序转发客服消息提示 该小程序提供的服务出现故障,什么原因导致的?服务器内日志已经返回了xml,还是提示服务出现故障,contentType也是text/xml;charset=utf-8,这是什么原因? <xml> <ToUserName><![CDATA[XXXX]]></ToUserName> <FromUserName><![CDATA[XXXX]]></FromUserName> <CreateTime>1634713576</CreateTime> <MsgType><![CDATA[transfer_customer_service]]></MsgType> </xml>
2021-10-20其实素材不是必填项,可以不填素材直接提交审核
提交审核报错,invalid preview_info formathttps://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/submit_audit.html 请求参数: {"version_desc":"品牌馆接口替换","preview_info":{"video_id_list":["I3TWSsv6JoAqql6PTdP4PlEfhJCuGhb46an5Ypr4hO0NXVzhM9yVx64xGZj97Nen"],"pic_id_list":["vcKwhO5kyQqzT-zyOfeae7pdyZ8NR-aj5SZ9uIrbdhcMCbkVVAb33GJFpDzmGyqL"]}} 报错: {"errcode":85092,"errmsg":"invalid preview_info format rid: 61682b4b-0a9a1e8f-46f546f3"} 重新上传素材也不行,真是没辙了,救救孩子吧
2021-10-15公众号好像是个人版和企业版有区别,有一些功能只有企业版才能用
微信第三方平台微信公众号授权后,调用公众号接口出现48001或61016错误?公众号已经授权,并且也拿到了授权集,获取授权信息也能获取到,但是就是调取微信的接口出现48001或61016,如自定义菜单、素材管理等都出现这个问题 自定义菜单接口{"errcode":48001,"errmsg":"api unauthorized rid: 61667509-11c48347-2fccb5d3"} 素材列表[61016]function category of API need be confirmed by component rid: 61668a9a-02b7ae9d-73fa1d76 第三方平台appid:wxce45fbb786234187 授权后的公众号appid:wx442303fecdc68544
2021-10-13授权成功之后,微信会推送一个authorized事件到你的服务(xml格式),另外,你本地PC端页面会自动进入你的callbackUrl里面,你需要在callbackUrl的处理逻辑里面返回一个redirect指令,控制PC端跳转到你想要的页面
公众号授权给第三方平台,扫码授权成功后无法跳转到redirect_uri?管理员扫码授权,确认后不能自动跳转,请问如何解决? [图片] [图片]
2021-09-26不可能吧,你自己看看网关日志或者nginx日志,是不是被网关或者nginx拦截了
第三方平台快速注册企业小程序没有获取到注册审核通知怎么办?发起快速注册后,法人已经完成了人脸识别并注册成功了小程序,第三方平台服务端接收到了授权事件authorized,但没有接收到notify_third_fasteregister事件推送,导致无法将appid绑定到对应的企业信息上。 快速注册的appid:wxca69838e994250ee 第三方平台appid:wx8f9ab98d290d2d58 这怎么办呢?
2021-09-26你的https证书签发的域名有没有带 *号,例如 *.xxx.com这种,我们之前遇到这种情况也是出现你这种问题
第三方平台在编辑开发资料时,提示访问不到校验文件?浏览器与CURL都试过能正常访问。 请解决一下。 操作时间:此时及之前1小时 APPID: wx0397cc6ae5e008e7
2021-09-24