springboot集成微信支付APIv3接口 实现小程序和公众号支付

前言:

网上有很多大佬们集成的综合支付包,做相应配置也很方便,我这个是基于微信官方文档流程做的直连模式,实现公众号和小程序支付(需要代码中正确使用appid),不得不吐槽微信的文档还是一如既往的。。。

 

微信官方支付文档链接:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_0.shtml,各种证书配置就按照文档上说的来就行了,下边直接上代码。

1.引入maven依赖

        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.5</version>
        </dependency>

2.yaml配置

wx:
    pay:
        mchId: 16126xxxx #微信支付商户号
        mchSerialNo: 60B04XXXXXXXXX #证书序列号
        v3Secret: 5ovxxxxxxxxxxxxxxxx  #密钥
        p12Path:/usr/local/apiclient_cert.p12 #证书绝对路径地址
        keyPass:16126xxxx  #证书密码 默认微信商户号
        privateKey: | #这个符号一定注意(回车键旁边) 是yaml中保持文本换行读取的写法 防止证书读出来加载失败
            -----BEGIN PRIVATE KEY-----
            xxxx
            -----END PRIVATE KEY----- #私钥
        certificate: |
            -----BEGIN CERTIFICATE-----
            xxxx
            -----END CERTIFICATE----- #平台证书

特别提示:

  yaml配置中的certificate这个证书,需要自己生成,https://developers.weixin.qq.com/community/pay/doc/000e4a0d5dc1486acc19c6fd15bc00,这个链接中网友果果璐璐和萧的回答给出了答案

3.读取yaml内容的Properties类

@Data
@Component
@ConfigurationProperties(prefix = "wx.pay")
public class WeChatPayProperties {

    /**
     * 商户id
     */
    private String mchId;

    /**
     * 商户证书序列号
     */
    private String mchSerialNo;

    /**
     * apiV3密钥
     */
    private String v3Secret;

    /**
     * p12证书文件位置
     */
    private String p12Path;

    /**
     * 证书密码
     */
    private String keyPass;

    /**
     * 商户私钥
     */
    private String privateKey;

    /**
     * 微信支付平台证书 jar包生产的
     */
    private String certificate;

}

4.启动配置类 

@Configuration
public class WxPayV3Config {

    @Autowired
    WeChatPayProperties weChatPayProperties;

    @Bean
    public CloseableHttpClient wxV3Init() {
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(weChatPayProperties.getPrivateKey());
        X509Certificate wechatPayCertificate = PemUtil.loadCertificate(
                new ByteArrayInputStream(weChatPayProperties.getCertificate().getBytes(StandardCharsets.UTF_8)));

        ArrayList<X509Certificate> listCertificates = new ArrayList<>();
        listCertificates.add(wechatPayCertificate);

        return WechatPayHttpClientBuilder.create()
                .withMerchant(weChatPayProperties.getMchId(), weChatPayProperties.getMchSerialNo(), merchantPrivateKey)
                .withWechatPay(listCertificates)
                .build();
    }
}

5.下单api

@Slf4j
@Component
public class WeChatPayApi {

    @Autowired
    WeChatPayProperties weChatPayProperties;
    @Autowired
    WeChatAppletProperties weChatAppletProperties;
    @Autowired
    CloseableHttpClient httpClient;

    /**
     * 微信小程序下单的请求地址
     */
    private static final String applet_req_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";/**
     * 创建微信小程序订单
     *
     * @param orderNo   订单号
     * @param amount    单位 分
     * @param openid    小程序的openid
     * @param goodsName 订单名称
     * @param notify    通知地址
     * @throws IOException
     */
    public String createWxAppletOrder(String orderNo, Integer amount, String openid, String goodsName, String notify) throws IOException {// 请求body参数
        JSONObject paramObject = new JSONObject();
        JSONObject amountObject = new JSONObject();
        amountObject.put("total", amount);
        amountObject.put("currency", "CNY");
        paramObject.put("amount", amountObject);
        paramObject.put("mchid", weChatPayProperties.getMchId());
        paramObject.put("description", goodsName);
        paramObject.put("notify_url", notify);

        JSONObject payerObject = new JSONObject();
        payerObject.put("openid", openid);
        paramObject.put("payer", payerObject);
        paramObject.put("out_trade_no", orderNo);
        paramObject.put("appid", "小程序appid");

        HttpPost httpPost = new HttpPost(applet_req_url);
        httpPost.setHeader("Accept", "application/json");
        StringEntity entity = new StringEntity(paramObject.toJSONString(), "utf-8");
        entity.setContentType("application/json");
        log.info("[微信下单请求参数列表]=" + paramObject);
        httpPost.setEntity(entity);

        //完成签名并执行请求
        CloseableHttpResponse response = httpClient.execute(httpPost);
        Map<String, Object> resMap = new HashMap<>();
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            Assert.isTrue(statusCode == 200, "微信下单请求失败");
            JSONObject jsonObject = JSON.parseObject(EntityUtils.toString(response.getEntity()));
            String prepayId = jsonObject.getString("prepay_id");
            Assert.isTrue(StringUtils.isNotBlank(prepayId), "下单获取参数失败");

            String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
            String nonceStr = RandomUtil.randomString(32).toUpperCase();
            String packagep = "prepay_id=" + prepayId;


            SortedMap<Object, Object> params = new TreeMap<>();
            params.put("appId", weChatAppletProperties.getAppId());
            params.put("timeStamp", timeStamp);
            params.put("nonceStr", nonceStr);
            params.put("package", packagep);
            params.put("signType", "RSA");

            resMap.put("appId", "小程序appid");
            resMap.put("timeStamp", timeStamp);
            resMap.put("nonceStr", nonceStr);
            resMap.put("package", packagep);
            resMap.put("signType", "RSA");
            resMap.put("paySign", WechatPayUtils.createSign(params, weChatPayProperties.getP12Path(), weChatPayProperties.getKeyPass()));
            log.info("[微信支付] 支付参数:" + JSON.toJSONString(resMap));
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        } finally {
            response.close();
        }

        return JSON.toJSONString(resMap);
    }

6.微信paySign签名生成 WechatPayUtils类,KeyPairFactory类,WechatRSAUtils类

public class WechatPayUtils {

    /**
     * sign签名
     *
     * @param map
     * @return
     */
    public static String createSign(SortedMap<Object, Object> map, String certPath, String keyPass) throws Exception {
        String signatureStr = Stream.of(
                String.valueOf(map.get("appId"))
                , String.valueOf(map.get("timeStamp"))
                , String.valueOf(map.get("nonceStr"))
                , String.valueOf(map.get("package"))
        ).collect(Collectors.joining("\n", "", "\n"));
        KeyPair keyPair = KeyPairFactory.createPKCS12(certPath, "Tenpay Certificate", keyPass);
        return WechatRSAUtils.payRequestSign(signatureStr, keyPair);  
}
public class KeyPairFactory {

    private static KeyStore store;

    private static final Object lock = new Object();

    /**
     * 获取公私钥.
     *
     * @param keyPath  the key path
     * @param keyAlias the key alias
     * @param keyPass  password
     * @return the key pair
     */
    public static KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) throws Exception {
//        ClassPathResource resource = new ClassPathResource(keyPath); //喜欢用相对路径的同学使用这一行
        PathResource resource = new PathResource(keyPath);

        char[] pem = keyPass.toCharArray();
        synchronized (lock) {
            if (store == null) {
                synchronized (lock) {
                    store = KeyStore.getInstance("PKCS12");
                    store.load(resource.getInputStream(), pem);
                }
            }
        }
        X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias);
        certificate.checkValidity();
        // 证书的序列号 也有用
        String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
        // 证书的 公钥
        PublicKey publicKey = certificate.getPublicKey();
        // 证书的私钥
        PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem);
        return new KeyPair(publicKey, storeKey);
    }
}
public class WechatRSAUtils {
  /**
     * 生成支付签名
     *
     * @param signStr
     * @param keyPair
     * @return
     **/
    @SneakyThrows
    public static String payRequestSign(String signStr, KeyPair keyPair) {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(keyPair.getPrivate());
        sign.update(signStr.getBytes(StandardCharsets.UTF_8));
        return Base64Utils.encodeToString(sign.sign());
    }     
}

 

上一篇:Java面向对象的三大性质


下一篇:Android自定义相机,预览区域裁剪