JAVA 特约商户进件对接

需要配置如下:
1.微信证书私钥路径(账户中心-》API安全-》申请API证书-》API证书管理)
2.微信商户证书路径(账户中心-》API安全-》申请API证书-》API证书管理)
3.APIv3秘钥(账户中心-》API安全-》设置APIv3秘钥)
4.商户号

需要的pom,有时候会出现不能导入需要自己导入本地

 <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-rs-client</artifactId>
            <version>3.1.11</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>3.1.11</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxrs</artifactId>
            <version>3.1.11</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-rs-extension-providers</artifactId>
            <version>3.1.11</version>
        </dependency>

        <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>3.14.9</version>
        </dependency>
        <dependency>
        <groupId>com.squareup.okio</groupId>
        <artifactId>okio</artifactId>
        <version>1.13.0</version>
        </dependency>

代码:请求接口

 
    @ApiOperation(value = "审核")
    @ApiResponses(value = {
            @ApiResponse(code = 0000, message = "请求已完成"),
            @ApiResponse(code = 0001, message = "操作失败"),
    })
    @ApiImplicitParams({
            @ApiImplicitParam(value = "(必传)token", name = "token", dataType = "String", required = true, paramType = "header"),
    })
    @PostMapping("/updateIspState")
    @ResponseBody
    public void updateIspState(   ) throws Exception {
        //需要提交的参数
        String sub = UploadServiceImpl.applyment4sub("");
        //返回提交申请单 微信支付申请单号
        String applyment_id =  V3Http.sendPost(sub);


    }

工具类


import com.alibaba.fastjson.JSON;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;

import javax.crypto.*;
import java.io.*;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.*;

/**
 * @program: UploadServiceImpl
 * @description:
 * @author: 云晓得峰
 * @create: 2022/9/21 16:27
 */

public class UploadServiceImpl {

    /**
     * 微信特约服务商 提交申请
     * @param str
     */
    public static String applyment4sub( String str) throws Exception {
        HashMap<String, Object> map = new HashMap<>();
        HashMap<String, Object> contactMap = new HashMap<>();
        // 下面所有加密参数需要的对象
        String certString = V3Http.getCertStr();
        ByteArrayInputStream stringStream = new ByteArrayInputStream(certString.getBytes());
        // 下面所有加密参数需要的对象
        X509Certificate certx = PemUtil.loadCertificate(stringStream);
        /**
         * 提交的参数自行根据需求拼接,这里做个示例 加密参数
         */
        //微信支付平台证书公钥加密
        contactMap.put("contact_name",  rsaEncryptOAEP(str, certx));
        map.put("contact_info",contactMap);
        return  JSON.toJSONString(map);
    }



    /**
     * 加密
     *
     * @param message
     * @param certificate
     * @return
     * @throws IllegalBlockSizeException
     * @throws IOException
     */
    public static String rsaEncryptOAEP(String message, X509Certificate certificate) throws IllegalBlockSizeException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());

            byte[] data = message.getBytes("utf-8");
            byte[] cipherdata = cipher.doFinal(data);
            return Base64.getEncoder().encodeToString(cipherdata);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的证书", e);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
        }
    }

    /**
     * 解密
     *
     * @param ciphertext
     * @param privateKey
     * @return
     * @throws BadPaddingException
     * @throws IOException
     */
    public static String rsaDecryptOAEP(String ciphertext, PrivateKey privateKey) throws BadPaddingException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);

            byte[] data = Base64.getDecoder().decode(ciphertext);
            return new String(cipher.doFinal(data), "utf-8");
        } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的私钥", e);
        } catch (BadPaddingException | IllegalBlockSizeException e) {
            throw new BadPaddingException("解密失败");
        }
    }
}

import com.alibaba.fastjson.JSONObject;
import net.sf.json.JSONArray;
import okhttp3.HttpUrl;
import org.apache.cxf.jaxrs.client.WebClient;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.UUID;

/**
 * @program: V3Http
 * @description:
 * @author: 云晓得峰
 * @create: 2022/9/25 10:12
 */
public class V3Http {


    /**
     *  【需要设置】
     * 微信支付商户号
     */
    public static String merchantId ="XXXXXX";
    /**
     * 【需要设置】
     登录微信商户平台
     账户中心-》API安全-》设置APIv3秘钥
     */
    public static byte[] aesKey = "XXXXXXXXXXXXXXXX".getBytes();
    /**
     * 【需要设置】
     * 微信证书私钥路径(从微信商户平台下载,保存在本地)
     */
    private final static String privateKeyFile = "C:/cert/apiclient_key.pem";


    public static String SCHEMA = "WECHATPAY2-SHA256-RSA2048";
    public static String POST = "POST";
    public static String GET = "GET";
    public static String host = "https://api.mch.weixin.qq.com";
    public static String APPLY_PATH = "/v3/applyment4sub/applyment/"; // 申请单url
    public static String CERT_PATH = "/v3/certificates"; // 获取微信平台证书url
    public static String APPLY_QUERY_PATH = "/v3/applyment4sub/applyment/applyment_id/"; // 查询申请状态

    static final int KEY_LENGTH_BYTE = 32;
    static final int TAG_LENGTH_BIT = 128;

    public V3Http(byte[] key) {
        if (key.length != KEY_LENGTH_BYTE) {
            throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
        }
        this.aesKey = key;
    }


    /**
     * 微信支付平台证书
     * @return
     * @throws Exception
     */
    public static String getCertStr() throws Exception {
        try {
            String str =  sendGet();
            net.sf.json.JSONObject json = net.sf.json.JSONObject.fromObject(str);
            JSONArray jsonArray = JSONArray.fromObject(json.optString("data"));
            net.sf.json.JSONObject jsonObject = jsonArray.getJSONObject(0);
            net.sf.json.JSONObject jsonCert = net.sf.json.JSONObject.fromObject(jsonObject.optString("encrypt_certificate"));
            String certKeyString =   decryptToString1(jsonCert.getString("associated_data").getBytes(),
                    jsonCert.getString("nonce").getBytes(), jsonCert.getString("ciphertext"));
            return certKeyString;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    public static String decryptToString1(byte[] associatedData, byte[] nonce, String ciphertext) throws Exception {
        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);
        }
    }


    /**
     * POST请求
     */
    public static String sendPost(String body) {
        String url = host + APPLY_PATH;
        try {
            /**
             * 获取HTTP请求头中Wechatpay-Serial
             证书序列号: 需要注意不是 账户中心-》API安全-》申请API证书 里面的证书序列号。需要通过接口获取
             */
            JSONObject jsonObject = JSONObject.parseObject(sendGet());
            com.alibaba.fastjson.JSONArray objects = com.alibaba.fastjson.JSONArray.parseArray(jsonObject.get("data").toString());
            Object str = objects.get(0);
            Object serial_no = JSONObject.parseObject(str.toString()).get("serial_no");

            String authorization = getToken(POST, url, body);
            WebClient client = WebClient.create(host);
            client.reset();
            client.header("Content-Type", "application/json; charset=UTF-8");
            client.header("Accept", "application/json");
            client.header("user-agent", "application/json");
            client.header("Wechatpay-Serial",serial_no);
            client.header("Authorization", authorization);
            client.path(APPLY_PATH);
            Response r = client.post(body);
            return r.readEntity(String.class);
        } catch (Exception e) {
            return null;
        }
    }



    /**
     * get请求 获取HTTP请求头中Wechatpay-Serial
     * json中返回的serial_no 为需要的数据
     */
    public static String sendGet() {
        // 请求URL
        String url = host + CERT_PATH;
        try {
            String authorization = getToken(GET, url, "");
            WebClient client = WebClient.create(host);
            client.reset();
            client.header("Content-Type", "application/json; charset=UTF-8");
            client.header("Accept", "application/json");
            client.header("User-Agent", "application/json");
            client.header("Authorization", authorization);
            client.path(CERT_PATH);
            Response r = client.get();
            return r.readEntity(String.class);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * get请求
     */
    public static String sendGet(String applymentId) {
        // 请求URL
        String url = host + APPLY_QUERY_PATH + applymentId;
        try {
            String authorization = getToken(GET, url, "");
            WebClient client = WebClient.create(host);
            client.reset();
            client.header("Content-Type", "application/json; charset=UTF-8");
            client.header("Accept", "application/json");
            client.header("User-Agent", "application/json");
            client.header("Authorization", authorization);
            client.path(APPLY_QUERY_PATH + applymentId);
            Response r = client.get();
            if(r.getStatus()==200){
                return r.readEntity(String.class);
            }else {
                return null;
            }

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取加密串
     *
     * @param method
     * @param url
     * @param body
     * @return
     */
    public static String getToken(String method, String url, String body) {
        String nonceStr = String.valueOf(UUID.randomUUID());
        long timestamp = System.currentTimeMillis() / 1000;
        HttpUrl httpUrl = HttpUrl.parse(url);
        String message = buildMessage(method, httpUrl, timestamp, nonceStr, body);
        String signature = null;
        String certificateSerialNo = null;
        try {
            signature = sign(message.getBytes("utf-8"));
            /**
             * 此处是证书编号
             * apiclient_cert.pem文件中获取
             */
            certificateSerialNo = CertUtil.getSerialNo();//证书序列号
        } catch (Exception e) {
            e.printStackTrace();
        }

        return SCHEMA + " mchid=\"" + merchantId + "\"," + "nonce_str=\"" + nonceStr + "\"," + "timestamp=\"" + timestamp + "\"," + "serial_no=\""
                + certificateSerialNo + "\"," + "signature=\"" + signature + "\"";
    }

    /**
     * 得到签名字符串
     */
    public static String sign(byte[] message) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        PrivateKey privateKey = getPrivateKey(privateKeyFile);
        sign.initSign(privateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }



    /**
     * 获取私钥。
     *
     * @param filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String filename) throws IOException {

        String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");

            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }
    public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }
        return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
    }
}

import org.apache.commons.codec.binary.Base64;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

/**
 * 证书工具类
 */
public class CertUtil {
    /**
     * 【需要设置】
     * 微信证书私钥路径(从微信商户平台下载,保存在本地)
     */
    public static String APICLIENT_KEY = "C:/cert/apiclient_key.pem";

    /**【需要设置】
     *  微信商户证书路径(从微信商户平台下载,保存在本地)
     */
    public static String APICLIENT_CERT = "C:/cert/apiclient_cert.pem";

    /**
     * 获取私钥。
     *
     * @param
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey() throws IOException {
        String content = new String(Files.readAllBytes(Paths.get(APICLIENT_KEY)), StandardCharsets.UTF_8);
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }


    /**
     * 获取商户证书。
     *
     * @param  // 证书文件路径 (required)
     * @return X509证书
     */
    public static X509Certificate getCertificate() throws IOException {
        InputStream fis = new FileInputStream(APICLIENT_CERT);
        try (BufferedInputStream bis = new BufferedInputStream(fis)) {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书文件", e);
        }
    }
    public static X509Certificate getCertificate(String path) throws IOException {
        InputStream fis = new FileInputStream(path);
        try (BufferedInputStream bis = new BufferedInputStream(fis)) {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书文件", e);
        }
    }

    /**
     * 获取商户证书序列号
     *
     * @param   //获取商户证书序列号 传递商号证书路径 apiclient_cert
     * @return
     * @throws IOException
     */
    public static String getSerialNo() throws IOException {
        X509Certificate certificate = getCertificate();
        return certificate.getSerialNumber().toString(16).toUpperCase();
    }



}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白菜S

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值