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);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章