记一次接入微信支付中服务端开发所踩得坑

记一次接入微信支付中服务端开发所踩得坑

因为业务需要,项目中需要对接微信支付,在此记录下在接入微信支付中所踩得坑

首先吐槽一波,讲道理微信支付的开发文档确实不如支付宝的文档整理的好,封装也确实要差点,所以开发中踩了不少坑。也可能是本人比较菜 @_@

废话不多说,详细的文档请移步链接: 微信支付开发者文档

新版本的微信支付已经从原来的版本升级为V3,请求从原来的xml格式转为了json格式。网上很多的教程和文章大部分都是老版本的微信支付,但是差别不是太大。

下面正式开始:
和老版本一样还是要申请商户号,详细流程不必细说,按照官方文档上的步骤一步步来就好了。

商户号等等乱七八糟的申请完后我们就可以拿到商户证书,除了商户证书我们还需要自己设置一个V3秘钥,这个也是在官网自己设置的32位字符串

主要参数

参数描述
AppID手机程序的appid,这个找安卓开发去要
privateKeyPath私钥的存储地址
merchantID商户号,通过注册商户号后拿到的证书解析获得
merchantSerialNO商户序列号,通过注册商户号后拿到的证书解析获得
V3Key自己设置的V3Key
notifyURL执行后的异步回调地址

秉着能用封装过的jdk就不自己造轮子的理念,我们先下为敬 开发工具传送门

虽然已经有了官方帮忙封装后的工具类,在使用过程中我还是进行了简单的封装,方便开发中使用

注意:在加载私钥时,给的是一个inputSteam流,所以我们要设置一个秘钥文件的地址,用于获取、解析秘钥,当然中间这些繁琐的步骤微信提供的jdk已经帮我们封装好了,包括解析私钥,请求平台证书,解析平台证书、签名、验签

package org.jeecg.modules.kzw.pay.factory;

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
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 lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.jeecg.modules.kzw.constant.PayConstant;

import java.io.*;
import java.security.PrivateKey;

/**
 * 微信支付工具类<br>
 * 微信整个支付流程统一由微信管理<br>
 * 我们只需要配置好httpClient,每次获取Client发起对应的请求就好了<br>
 */
@Slf4j
public class WxPayHttpClientFactory {
    //商户id
    private String merchantId;
    //商户证书序列号
    private String merchantSerialNo;
    //V3秘钥
    private String apiV3Key;


    public WxPayHttpClientFactory() {
        initFactory(PayConstant.WX_MERCHANT_ID, PayConstant.WX_MERCHANT_SERIAL_NO, PayConstant.WX_V3_PRIVATE_KEY);
    }

    /**
     * 初始化工厂类参数
     *
     * @param merchantId       商户id
     * @param merchantSerialNo 商户证书序列号
     * @param apiV3Key         V3秘钥
     */
    private void initFactory(String merchantId, String merchantSerialNo, String apiV3Key) {
        this.merchantId = merchantId;
        this.merchantSerialNo = merchantSerialNo;
        this.apiV3Key = apiV3Key;
    }


    /**
     * 获取微信支付httpClient
     *
     * @return httpClient
     */
    public CloseableHttpClient getHttpClient() {
        return initHttpClient();
    }

    /**
     * 初始化客户端
     *
     * @return httpClient
     */
    public CloseableHttpClient initHttpClient() {
        CloseableHttpClient httpClient = null;
        try {
            // 加载商户私钥(privateKey:私钥字符串)
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(PayConstant.WX_PRIVATE_KEY_PATH));
            // 加载平台证书(merchantId:商户号,merchantSerialNo:商户证书序列号,apiV3Key:V3秘钥)
            AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(merchantId, new PrivateKeySigner(merchantSerialNo, merchantPrivateKey)), apiV3Key.getBytes("utf-8"));
            //httpClient构造器,可以继续通过builder构造其他参数
            WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                    .withMerchant(merchantId, merchantSerialNo, merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier));
//                    .withValidator(response -> true);
            // 初始化httpClient
            httpClient = builder.build();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("初始化微信支付httpClient失败!");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            log.error("加载秘钥文件失败!请检查秘钥文件是否存在!");
        }
        return httpClient;
    }


}

经过封装后,只用在使用到的地方通过这个工厂类获取httpClient就好了

注意:每一个请求都要单独创建一个client,不可重复使用!

这只是拿到了httpclient,我又简单封装了一个WxPayUtil类,方便开发中直接调用相关接口
就拿关闭订单来说吧

	/**
     * 关闭订单
     *
     * @param orderId 订单id
     * @return 成功[true],失败[false]
     */
    public static boolean appCloseOrder(String orderId) {
        CloseableHttpClient httpClient = factory.getHttpClient();
        HttpPost post = new HttpPost(PayConstant.getWxAppCloseOrderUrl(orderId));
        boolean result = false;
        //封装传递参数
        JSONObject requestJson = new JSONObject();
        requestJson.put("mchid", PayConstant.WX_MERCHANT_ID);       //商户号
        StringEntity entity = new StringEntity(requestJson.toString(), "UTF-8");
        //设置请求参数和请求头
        entity.setContentType("application/json");
        entity.setContentEncoding("UTF-8");
        setHeaders(post);
        post.setEntity(entity);
        try {
            CloseableHttpResponse response = httpClient.execute(post);
            if (isSuccess(response)) {
                result = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("微信支付-关闭下单-失败!");
        } finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

自己定义的一个和支付相关的常量类,用于存储支付请求的接口和参数,因为部分接口是RestFul类型,所以会用到地址拼接
微信支付接口文档
如:https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}/close

所以我们采用了下面这种方式

	/**
     * app关闭订单接口地址 POST方式
     *
     * @param outTradeNo 商户订单号
     * @return app关闭订单接口地址
     */
    public static URI getWxAppCloseOrderUrl(String outTradeNo) {
        return getUri(WX_APP_CLOSE_ORDER_URL + outTradeNo + "/close");
    }

注意:因为new HttpPost()的时候里面的参数要么是String类型要么是URI类型,但是为了避免get请求参数使用拼接的方式,所以写了一个String转URI的方法

通过使用URL将字符串间接转为URI的方法

	/**
     * 将地址变成URI形式
     *
     * @param path 请求地址
     * @return uri
     */
    private static URI getUri(String path) {
        URI uri = null;
        try {
            URL url = new URL(path);
            uri = new URI(url.getProtocol(), url.getHost(), url.getPath(), url.getQuery(), null);
        } catch (MalformedURLException | URISyntaxException e) {
            e.printStackTrace();
        }
        return uri;
    }

因为请求都是json格式,所以在每个请求都要设置header信息
为了省事,又封装了个方法 -_-

	/**
     * 设置请求头 设置Content-Type 和 Accept 为 application/json 格式
     *
     * @param httpRequest 请求
     */
    private static void setHeaders(HttpRequestBase httpRequest) {
        httpRequest.setHeader("Content-Type", "application/json;utf-8");
        httpRequest.setHeader("Accept", "application/json");
    }

到这里大概就已经完成了微信支付的一个简单封装。
整体的一个流程还是比较复杂的,但是还是要硬着头皮去仔细研究。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值