WechatPay-API-v3接口规则

                    WechatPay-API-v3接口规则

目录

2、下面是Java 接入过程:

3、敏感信息加解密

4、demo参考

5、回调通知解密算法(AEAD_AES_256_GCM)



1、官方文档

 https://wechatpay-api.gitbook.io/wechatpay-api-v3/
 

2、下面是Java 接入过程:

第一步: 阅读微信支付分给的接口规则 说明: https://wechatpay-api.gitbook.io/wechatpay-api-v3/

第二步: 微信支付API v3要用第三方CA的证书 所以涉及到 API证书升级

新接入商户请参考什么是API证书?如何获取API证书?

已经接入并使用微信支付颁发证书的商户请参考微信支付API证书升级指引(技术人员)。
API v3已不支持使用微信支付颁发的证书。
商户升级API证书时,需要完成三个步骤:

①:商户号的超级管理员到商户平台升级证书,获取到权威CA颁发的API证书。 (查看指引)

②:超级管理员将权威CA颁发的API证书(共包含三个文件: 证书pkcs12格式、证书pem格式、证书密钥pem格式)转交给技术人员。

③:技术人员用新证书文件替换服务器上原微信支付颁发的API证书,无需对现有系统进行代码修改。

(注意)这里升级API证书不影响原有的 API 密钥 代码不需要做改动直接替换 apiclient_cert.p12文件即可

第三步: 拿到API证书和密钥文件.

第四步: 引入微信支付API v3的Apache HttpClient装饰器: GitHub 地址

​ 注意: 微信支付API v3的Apache HttpClient扩展,实现了请求签名的生成和应答签名的验证。如不想使用次封装客户端 可自己实现 签名和应答解密过程.

相关maven依赖

<dependency>
    <groupId>com.xiaomi.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.1.6</version>            
</dependency>

第五步: 请求微信支付分API前准备

   //微信支付商户开通后 微信会提供appid
    public String appId;
    
    //微信支付商户开通后 微信会提供appSecret
    public String appSecret;
    
    //商户号
    public String mchId; 
    
    //32位的api密钥,微信商户平台-账户设置-安全设置-api安全 密钥 用于拉起支付签名
    public String partnerkey;
    
    //openId 是微信用户针对公众号的标识,授权的部分这里不解释
    public String openId;
    
    //微信支付成功后异步通知地址 必须要求80端口并且地址不能带参数
    public String notifyUrl;
    
    //微信支付成功后同步通知地址 必须要求80端口并且地址不能带参数
    public String returnUrl;
    
    //证书apiclient_cert.p12文件位置 可加载
    public String certPath;
    
    //微信支付分 分配的服务 ID
    public String serviceId;
    
    //v3接口 CA证书 apiclient_key.pem私钥内容 
    public String privateKey;
    
    //v3接口 CA证书 apiclient_cert.pem证书内容
    public String certificate;
    
    // APIv3密钥 32 位
    public String AES_KEY = "xxx"; 
    
    //商户证书序列号 CA证书 可查看微信商户平台-账户设置-安全设置-api安全密钥
    public String  MC_HSERIAL_NO = "xxxxx"; 


3、敏感信息加解密

https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/min-gan-xin-xi-jia-mi

使用AES-256-GCM,对回调中的关键信息进行加密保护

建议从Verifier中获得微信支付平台证书,或使用预先下载到本地的平台证书文件中

X509Certificate wechatpayCertificate = verifier.getValidCertificate();


privateKey 为 v3接口 CA证书 apiclient_key.pem私钥内容
将String类型的privateKey 转化为PrivateKey类型

PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));

4、demo参考

构建httpClient,需要设置微信支付平台证书。

CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
        .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
        .withWechatpay(wechatpayCertificates) // 加载证书
        .build();


在第一次下载平台证书时,按照下述方法临时"跳过”应答签名的验证 

CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
  .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
  .withValidator(response -> true) // NOTE: 设置一个空的应答签名验证器,**不要**用在业务请求
  .build();


get请求

URIBuilder uriBuilder =   new URIBuilder(USER_SERVICE_STATE_URL);
                        uriBuilder.setParameter("service_id", yourServiceIdxxx);
                        uriBuilder.setParameter("appid", yourAppIdxxx);
                        uriBuilder.setParameter("openid", userOpenIdxxx);
CloseableHttpResponse response=null;
        try {
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");
            // NOTE: 建议指定charset=utf-8。低于4.4.6版本的HttpCore,不能正确的设置字符集,可能导致签名错误
            response = getHttpDefaultClient().execute(httpGet);
            if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                String result = EntityUtils.toString(response.getEntity());// 返回json格式:
                return JSONObject.parseObject(result);
            }else {
                String result = EntityUtils.toString(response.getEntity());// 返回json格式:
                log.info("微信支付V3 url={} result={} responseEntity={}",uriBuilder.build(), result,JSON.toJSONString(response.getEntity()));
            }
        } catch (Exception e) {
            log.error("微信支付V3 请求url={}异常 ",uriBuilder.build());
        }finally {
            if(null!=response){
                response.close();
            }
        }


post请求

HttpPost httpPost = new HttpPost(PAYSCORE_PAYAFTER_ORDERS_URL);
        StringEntity reqEntity = new StringEntity(JSONObject.toJSONString(payAfterOrdersModel), ContentType.create("application/json", "utf-8"));
        httpPost.setEntity(reqEntity);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-Type", "application/json");
        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                String result = EntityUtils.toString(response.getEntity());// 返回json格式:
                log.info("微信支付V3 url={} result={} ",url,result);
                return JSONObject.parseObject(result);
            }else {
                String result = EntityUtils.toString(response.getEntity());// 返回json格式:
                log.info("微信支付V3 url={} result={} response.getEntity()={}",url,result,JSON.toJSONString(response.getEntity()));
            }
        } catch (Exception e) {
            log.error("微信支付V3 请求url={} 参数={} 异常 e={}",url, JSON.toJSONString(json),e.getMessage());
        }finally {
            response.close();
        }

 官方DEMO参考: https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient/blob/master/src/test/java/com/wechat/pay/contrib/apache/httpclient/HttpClientBuilderTest.java


5、回调通知解密算法(AEAD_AES_256_GCM)

 我们已一个实际接口为例,进行讲解:

 下面我们对resource部分进行解密,解密算法如下:

package com.wsw.sdk.utils;
 
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
 
public class WxAPIV3AesUtil {
 
    static final int KEY_LENGTH_BYTE = 32;
    static final int TAG_LENGTH_BIT = 128;
    private final byte[] aesKey;
 
    public WxAPIV3AesUtil(byte[] key) {
        if (key.length != KEY_LENGTH_BYTE) {
            throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
        }
        this.aesKey = key;
    }
 
    public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
            throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
 
            SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
 
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);
 
            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }
}

调用示例:

//解密回调信息
        byte[] key = SystemConst.WX_KEY.getBytes("UTF-8");
        WxAPIV3AesUtil aesUtil = new WxAPIV3AesUtil(key);
        String decryptToString = aesUtil.decryptToString(assc.getBytes("UTF-8"),noce.getBytes("UTF-8"),cip);

猜你喜欢

转载自blog.csdn.net/xiao__jia__jia/article/details/106871545