java集成APP微信支付V3

微信支付V3版本

关于微信支付V3版本和原来的微信支付有一定的区别,原来的微信支付采用的是MD5加密方式,并且可以使用微信支付证书,并限制了请求的服务器白名单(虽然可以手动设置请求服务器的ip地址)。
微信支付V3更改加密方式为RSA,不再限制请求服务器ip地址。对于数据采用加密和解密来实现安全。私钥在微信开放平台获取。

maven项目导入jar包

       <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.2.1</version>
        </dependency>

自己封装的工具类,用于业务

WxPayment.java

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;

import java.io.ByteArrayInputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.*;

import org.apache.http.util.EntityUtils;

/**
 * 微信支付 工具类,用于调用起微信支付
 */
@Slf4j
public class WxPayment {

    private static final String APP_ID = "wxdd45ca77ce3d8c2f";//微信应用ID
    private static final String MCH_ID = "1613773777";//商戶号
    public final static String API_KEYV3 = "9";//apiV3秘钥,从商户中设置获得
    public static final String NOTIFY_URL = "https:///callback/wxPayCallBack";//支付成功回调地址,要求必须是https
    public static final String SERIAL_NO = "6D85E039C59CFE1E0CE615EF146873DDDA341A7D";//api证书序列号,从api证书点击查看进去可以看到序列号
    public static final String PRIVATE_KEY = "";//证书私钥,32位设置的秘钥,从微信商户中设置后获得

    /**
     * 创建微信支付订单,不会直接支付,返回微信预支付ID
     *
     * @param orderNo 订单号
     * @param title   支付标题
     * @param money   支付金额,保留两位小数
     * @return 返回微信预支付ID
     */
    public static String createOrder(String orderNo, String title, BigDecimal money) {
        try {
            //请求URL
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/app");
            // 请求body参数
            SortedMap<Object, Object> parameters = new TreeMap<>();
            parameters.put("mchid", MCH_ID);
            parameters.put("out_trade_no", orderNo);
            parameters.put("appid", APP_ID);
            parameters.put("description", title);
            parameters.put("notify_url", NOTIFY_URL);
            parameters.put("attach", orderNo);//附加数据
            HashMap<String, Object> amount = new HashMap<>();
            amount.put("total", money.multiply(new BigDecimal("100")).intValue());//需要将输入的金额*100变成分
            amount.put("currency", "CNY");
            parameters.put("amount", amount);
            //将请求参数转换为json格式
            String data = new GsonBuilder().create().toJson(parameters);

            StringEntity entity = new StringEntity(data, StandardCharsets.UTF_8);
            entity.setContentType("application/json");
            httpPost.setEntity(entity);
            httpPost.setHeader("Accept", "application/json");
            // 获取私钥
            PrivateKey privateKey = getPrivateKey(PRIVATE_KEY);
            //加载平台证书
            AutoUpdateCertificatesVerifier verifier = getCertificate(MCH_ID, SERIAL_NO, privateKey, API_KEYV3);

            // 初始化httpClient
            CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(MCH_ID, SERIAL_NO, privateKey)
                    .withValidator(new WechatPay2Validator(verifier)).build();
            //完成签名并执行请求
            CloseableHttpResponse response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) { //处理成功
                System.out.println("下单成功:" + EntityUtils.toString(response.getEntity()));
                return new GsonBuilder().create().fromJson(EntityUtils.toString(response.getEntity()), Map.class).get("prepay_id").toString();
            } else {
                log.error("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
            }
            response.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 组装支付数据给app,用于发起微信支付,之所以要和下单接口分开,就是因为在支付过程中,有些已经有了预订单ID不需要再下单,直接传入预订单ID就可以发起支付了
     *
     * @param prepay_id 微信预支付ID
     * @return 返回Map集合对象
     */
    public static Map<String, Object> retPayStr(String prepay_id) {
        try {
            Map<String, Object> map = new HashMap<>();
            map.put("appid", APP_ID);
            map.put("partnerid", MCH_ID);
            map.put("prepayid", prepay_id);
            map.put("package", "Sign=WXPay");
            map.put("noncestr", createNoncestr(32));
            map.put("timestamp", Calendar.getInstance().getTimeInMillis());
            map.put("sign", sign(APP_ID + "\n" + Calendar.getInstance().getTimeInMillis() + "\n" + createNoncestr(16) + "\n" + prepay_id + "\n"));
            return map;
        } catch (Exception e) {
            log.error("组装微信支付数据失败!预支付ID为:{}", prepay_id);
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 微信回调解密 --->实际应用时,可以将整个回调通知body传入,修改本方法内的前置处理
     *
     * @param associated_data 附加数据
     * @param nonce           数据密文
     * @param ciphertext      加密使用的随机串
     * @return 返回解密后的Map参数集合
     */
    public static Map callbackDecryption(String associated_data, String nonce, String ciphertext) {
        try {
            byte[] key = API_KEYV3.getBytes(StandardCharsets.UTF_8);
            WxAPIV3AesUtil aesUtil = new WxAPIV3AesUtil(key);
            String decryptToString = aesUtil.decryptToString(associated_data.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
            return new Gson().fromJson(decryptToString, Map.class);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("微信回调解密失败!");
        }
        return null;
    }


    /**
     * 查询订单是否支付成功,用于订单超时检测,返回SUCCESS则支付成功
     *
     * @param outTradeNo 订单号
     * @return 返回交易状态 SUCCESS:支付成功     * REFUND:转入退款     * NOTPAY:未支付     * CLOSED:已关闭     * REVOKED:已撤销(仅付款码支付会返回)     * USERPAYING:用户支付中(仅付款码支付会返回)     * PAYERROR:支付失败(仅付款码支付会返回)
     */
    public static String selectOrderPay(String outTradeNo) {
        try {
            URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + outTradeNo + "?mchid=" + MCH_ID);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");
            httpGet.addHeader("Content-type", "application/json; charset=utf-8");

            // 初始化httpClient
            AutoUpdateCertificatesVerifier verifier = getCertificate(MCH_ID, SERIAL_NO, getPrivateKey(PRIVATE_KEY), API_KEYV3);
            CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(MCH_ID, SERIAL_NO, getPrivateKey(PRIVATE_KEY))
                    .withValidator(new WechatPay2Validator(verifier)).build();
            CloseableHttpResponse response = httpClient.execute(httpGet);
            return EntityUtils.toString(response.getEntity());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取随机字符串
     *
     * @param length 字符串长度
     * @return 返回随机生成的字符串
     */
    public static String createNoncestr(int length) {
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(str.length());
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }


    /**
     * 获取平台证书
     *
     * @param micId      商户ID
     * @param serialNo   证书序列号
     * @param privateKey 私钥
     * @param apiV3Key   apiV3秘钥
     * @return 返回证书信息
     */
    public static AutoUpdateCertificatesVerifier getCertificate(String micId, String serialNo, PrivateKey privateKey, String apiV3Key) {
        // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3秘钥)
        return new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(micId, new PrivateKeySigner(serialNo, privateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
    }


    /**
     * 对输入内容进行微信私钥签名,这里其实也是调用的jar包里面的加密,只是因为里面没有对外公开,所以封装了一个方法
     *
     * @param message 签名内容
     * @return 返回签名后的字符串
     */
    public static String sign(String message) throws Exception {
        Signature signature = Signature.getInstance("SHA256withRSA");
        //初始化签名
        signature.initSign(getPrivateKey(PRIVATE_KEY));
        byte[] bytes = message.getBytes();
        //将数据添加到签名
        signature.update(bytes);
        //计算签名
        return Base64.getEncoder().encodeToString(signature.sign());
    }


    /**
     * 获取私钥。
     *
     * @param privateKey 私钥字符串  (required)
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String privateKey) {
        // 加载商户私钥(privateKey:私钥字符串)
        return PemUtil
                .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)));
    }


}

截止微信支付就成功了,至于controller就不贴了,其实回调也很简单,只需要接收一个参数@RequestBody String body就可以了,得到的这个是字符串,需要手动json格式化,然后传入解密就ok了。
可能有部分类不可用,由于写这个文章时,在家,项目没有跑起来,有什么问题可以私聊:1126539036

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值