微信支付V3对接版

微信支付V3对接版

接入前准备工作

微信商户号、微信商户证书序列号、微信商户API秘钥、 微信商户证书私钥、 AppID

准备好以上需要用到的参数开始微信支付对接

接下来需要 导入微信支付的SDK

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

由于 Java 运行时环境的限制。当密钥大于128位的时候会抛出 java.security.InvalidKeyException: Illegal key size 异常

因此需要下载 jce补丁包 我用的是jdk8版本的 其他版本需要在网上自行下载

百度网盘链接:https://pan.baidu.com/s/1ilJu-X131sDO2n3JZdF_vw?pwd=6666 也可以自己去Oracle官网去下载 需要注册Oracle账号

将压缩包解压后会获得 local_policy.jar 和 US_export_policy.jar文件

打开电脑的环境变量查看 ==%JAVA_HOME%==的路径 打开该路径进入 /jre/lib/security 将压缩包解压的文 件进行替换即可

微信支付对接部分代码

//需要导入的依赖
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import
com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVe
rifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.impl.client.CloseableHttpClient;
//加载商家私钥 转换成私钥对象 在微信SDK中 PemUtil.loadPrivateKey(file); 方法有重载 参数可以直接传私钥字符串,也可以传由私钥文件的路径创建的输入流对象 这里采用字符串方式
/**
* 根据商户私钥获取 微信支付的私钥对象
*/
private static PrivateKey getPrivateKey(String fileContext){
    if (fileContext==null) {
    throw new RuntimeException("请配置商户私钥");
    }
        //PemUtil.loadPrivateKey(newFileInputStream(工具类名称.class.getResource("/").getPath()+"/apiclient_key.pem"));
	return PemUtil.loadPrivateKey(fileContext);     
}

//拿到私钥后获取微信签名验证器

/**
* 获取签名验证器
* 我的做法是把配置信息发在数据库,通过查询获取微信商家的配置信息,所以以参数的方式传递
* 也可以通过配置的方式读取 将该方法加入到spring容器中获取 减少该对象的生命周期产生的内存开销
* @param mchId 商户号
* @param apiV3Key V3的API私钥
* @param privateKeyString 商户证书私钥
* @param 商户证书序列号
* @return
*/

public static ScheduledUpdateCertificatesVerifier getVerifier(String mchId,String apiV3Key, String privateKeyString,String mchSerialNo){
    
    //获取商户私钥对象
    PrivateKey privateKey = getPrivateKey(privateKeyString);
    
    //私钥签名对象
    PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);
    //身份认证对象
    WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
    
    return new ScheduledUpdateCertificatesVerifier(wechatPay2Credentials,apiV3Key.getBytes(StandardCharsets.UTF_8));
}

//获取了微信签名后需要获得微信支付远程请求对象

/**
* 获取微信支付的远程请求对象
* @param verifier 微信签名验证器
* @param mchId 商户号
* @param privateKeyString 商户证书私钥
* @param mchSerialNo商户序列号
*/
public static CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier,String mchId,String privateKeyString,String mchSerialNo){
    //获取商户私钥对象
    PrivateKey privateKey = getPrivateKey(privateKeyString);
    
    WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
        .withMerchant(mchId, mchSerialNo, privateKey)
        .withValidator(new WechatPay2Validator(verifier));
    
    // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
    CloseableHttpClient httpClient = builder.build();
    return httpClient;
}

/**
     * 生成签名
     *
     * @param message     请求体
     * @param fileContext 商家证书私钥
     * @return 生成base64位签名信息
     * @throws Exception
     */
    public static String sign(String message, String fileContext) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(fileContext));
        sign.update(message.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

/**
     * 对称解密
     * @param bodyMap 微信请求返回的参数转集合
     * @param privateKeyString V3私钥 32位
     * @return
     */
    private String decryptFromResource(Map<String, Object> bodyMap,String privateKeyString) throws GeneralSecurityException {
        //通知数据
        Map<String, String> resourceMap = (Map) bodyMap.get("resource");
        //数据密文
        String ciphertext = resourceMap.get("ciphertext");
        //随机串
        String nonce = resourceMap.get("nonce");
        //附加数据
        String associatedData = resourceMap.get("associated_data");
        AesUtil aesUtil = new AesUtil(privateKeyString.getBytes(StandardCharsets.UTF_8));
        String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                ciphertext);

        return plainText;
    }

/**
     * 获取随机字符串 Nonce Str
     *
     * @return String 随机字符串
     */
    public static String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }

/**
	* 获取随机的订单号 也可使用雪花算法生成随机数
	*/
	public static String number(int length) {
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < length; i++) {
            if (i == 0)
                str.append(getRandom(49, 57));
            else
                str.append(getRandom(48, 57));
        }
        return str.toString();
    }

/**
     * 获取精确到秒的时间戳
     * @return
     */
    public static String getSecondTimestamp(Date date){
        if (null == date) {
            return "0";
        }
        String timestamp = String.valueOf(date.getTime());
        int length = timestamp.length();
        if (length > 3) {
            return timestamp.substring(0,length-3);
        } else {
            return "0";
        }
    }

//有了以上的远程请求对象就可以进行微信支付接口的对接了

//下面以V3 App下单 为例进行微信支付对接

// 这里作为测试返回string,并且直接填充参数 实际开发按照需修改即可
@SneakyThrows
public static String order(){
    //创建 ObjectMapper 对象
    ObjectMapper objectMapper = new ObjectMapper();
    
    //创建 ObjectNode 对象 使用节点的方式添加数据为 集合对象 时更简单
    ObjectNode param = objectMapper.createObjectNode();
    
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    
    param.put("appid", "APPID");
    param.put("mchid", "商户号");
    param.put("description", "商品说明");
    param.put("out_trade_no", number(15));
    param.put("notify_url", "微信回调地址");
    
    param.putObject("amount")
    .put("total", 1) //商品金额 单位为分
    .put("currency", "CNY");//支付货币类型
    
    objectMapper.writeValue(bos, param);
    
    // 创建 POST请求对象 传入微信下单的路径 GET请求创建HttpGet
    HttpPost httpPost = new HttpPost(WeChatPayConstants.V3_ORDERQUERY_URL);
    httpPost.addHeader("Accept", "application/json");
	httpPost.addHeader("Content-type","application/json; charset=utf-8");
    
    //创建 请求体 该请求头主要用于传递body的参数 并定义字符编码
    StringEntity entity = new StringEntity(bos.toString("UTF-8"),"utf-8");
    
    //设置请求报文格式
    entity.setContentType("application/json");
    
    //将请求报文放入请求对象
    httpPost.setEntity(entity);
    
    //设置响应报文格式
    httpPost.setHeader("Accept", "application/json");
    
    //获取微信的签名对象
    final ScheduledUpdateCertificatesVerifier verifier = WeChatPayUtil.getVerifier(MERCHANT_ID,MERCHANT_API_KEY,MERCHANT_PRIVATE_KEY,CERT_CONTENT,MERCHANT_NO);
    
    //获取微信的远程请求对象
    final CloseableHttpClient wxPayClient =
    WeChatPayUtil.getWxPayClient(verifier,MERCHANT_ID,MERCHANT_PRIVATE_KEY,CERT_CONTENT,MERCHANT_NO);
    
    //将POST请求对象传入 完成签名并执行请求,并完成验签
    CloseableHttpResponse response = wxPayClient.execute(httpPost);
    
    try {
        //获取请求中微信返回的响应状态码
        int statusCode = response.getStatusLine().getStatusCode();
        //获取请求中微信返回的响应体
        String bodyAsString = EntityUtils.toString(response.getEntity());
        String result = null;
        if (statusCode == 200) { //处理成功
            //参数对照微信支付文档的调起支付接口
            final String timestamp = DateFormatUtil.getSecondTimestamp(new Date());
            //使用 ObjectMapper对象将请求转换为map
            Map<String, String> resultMap = objectMapper.readValue(bodyAsString, Map.class);
            //再加上返回给前端的参数
            resultMap.put("appid", APPID);
            resultMap.put("package", "Sign=WXPay");
            //随机字符串
            resultMap.put("noncestr",generateNonceStr());
            //获取时间串
            resultMap.put("timestamp",timestamp);
            //将调起支付需要的参数转成JSON对象 和 商家证书私钥 传入进行 V3的验签 V3改为对称加密跟V2的非对称加密方式有差别
            String message =
                        APPID+ "\n" + timestamp + "\n" + resultMap.get("noncestr") + "\n" + resultMap.get("prepay_id") + "\n";
                final String sign = WeChatPayUtil.sign(message,vehcPayConfig.getMchPrivateKey());
            resultMap.put("sign",sign);
            result = resultMap.toString();
        } else {
            result = "请求失败,响应码:" + statusCode + ",响应体:" + bodyAsString;
        }
        //返回给前端调起支付
        return result;
    }catch (Exception e){
        e.printStackTrace();
        return "请求失败";
    }finally{
    	response.close();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值