admin管理员组

文章数量:1559067

目录

java微信支付v3系列——1.微信支付准备工作
java微信支付v3系列——2.微信支付基本配置
java微信支付v3系列——3.订单创建准备操作
java微信支付v3系列——4.创建订单的封装及使用
java微信支付v3系列——5.微信支付成功回调
java微信支付v3系列——6.微信支付查询订单api
java微信支付v3系列——7.微信支付之申请退款
java微信支付v3系列——8.微信支付之退款成功回调
java微信支付v3系列——9.微信支付之商家转账api

正文

微信支付的下单操作分为了5种,分别是jsapi、app、h5以及native支付及小程序支付,之所以将支付放在单独一个章节,而不是按照支付类型划分一个章节,是因为支付所传递的数据都是相似的,方便我们更好的封装。

本章节是支付编写支付前的准备操作,发送请求需要请求地址,用户支付成功后微信会通过我们传入的回调地址进行回调,这两个地址都通过枚举进行管理。

然后就是方法的封装,如果都写在一个方法里面,代码冗余,毕竟发送请求的代码都很相似。

1. 请求地址枚举类

为了防止微信支付的请求地址前缀发生变化,因此请求前缀存储在application.yml中,请求时进行拼接即可。

@allargsconstructor
@getter
public enum wxapitype {
	/**
	 * native下单
	 */
	native_pay("/v3/pay/transactions/native"),
	/**
	 * jsapi下单
	 */
	jsapi_pay("/v3/pay/transactions/jsapi"),
	/**
	 * jsapi下单
	 */
	h5_pay("/v3/pay/transactions/h5"),
	/**
	 * app下单
	 */
	app_pay("/v3/pay/transactions/app"),
	/**
	 * 查询订单
	 */
	order_query_by_no("/v3/pay/transactions/out-trade-no/%s"),
	/**
	 * 关闭订单
	 */
	close_order_by_no("/v3/pay/transactions/out-trade-no/%s/close"),
	/**
	 * 申请退款
	 */
	domestic_refunds("/v3/refund/domestic/refunds"),
	/**
	 * 查询单笔退款
	 */
	domestic_refunds_query("/v3/refund/domestic/refunds/%s"),
	/**
	 * 申请交易账单
	 */
	trade_bills("/v3/bill/tradebill"),
	/**
	 * 申请资金账单
	 */
	fund_flow_bills("/v3/bill/fundflowbill");
	/**
	 * 类型
	 */
	private final string type;
}

2. 回调地址枚举类

发生请求后微信官方会回调我们传递的地址,这里通过枚举统一管理我们的回调地址,回调地址由application.yml中的 weixin.notify-domain拼接组成。

/**
 * @author cv大魔王
 * @version 1.0
 * @description 微信支付基础请求数据对象
 * @date 2022/8/4
 */
@data
public class wechatbasepaydata {
    /**
     * 商品描述
     */
    private string title;
    /**
     * 商家订单号,对应 out_trade_no
     */
    private string orderid;
    /**
     * 订单金额
     */
    private bigdecimal price;
    /**
     * 回调地址
     */
    private wxnotifytype notify;
}

3. 微信支付基础请求数据对象

如下图所示的请求参数,各类九游会下载的支付方式的请求数据基本相似,我们提取出来公共的部分,整理成一个对象,方便后续调用。

/**
 * 微信回调地址,根据自己的项目来,不要直接照搬
 */
@getter
@allargsconstructor
public enum wxnotifytype {
	/**
	 * 课程支付通知
	 */
	course_native_notify("/api/v1/order/wx/course/native"),
	/**
	 * 文章支付通知
	 */
	article_native_notify("/api/v1/order/article/wx/callback/article"),
	/**
	 * 论文支付通知
	 */
	paper_native_notify("/api/v1/order/paper/wx/callback/paper"),
	/**
	 * 退款结果通知
	 */
	refund_notify("/api/wx-pay/refunds/notify");
	/**
	 * 类型
	 */
	private final string type;
}

4. 将请求参数封装成map集合

封装完枚举类后,首先就是请求参数的封装,支付类请求参数都非常相近,我们将都需要的参数提取出来以map的方式进行返回。这里的参数,指每个支付类请求都用到的参数,个别支付需要额外添加数据

@slf4j
public class wxpaycommon {
	/**
	  * 封装基础通用请求数据
	  * @param wxpayconfig 微信的配置文件
	  * @param basepaydata 微信支付基础请求数据
	  * @return 封装后的map对象
	  */
	private static map<string, object> getbasepayparams(wxpayconfig wxpayconfig, wechatbasepaydata basepaydata) {
	    map<string, object> paramsmap = new hashmap<>();
	    paramsmap.put("appid", wxpayconfig.getappid());
	    paramsmap.put("mchid", wxpayconfig.getmchid());
	    // 如果商品名称过长则截取
	    string title = basepaydata.gettitle().length() > 62 ? basepaydata.gettitle().substring(0, 62) : basepaydata.gettitle();
	    paramsmap.put("description",title);
	    paramsmap.put("out_trade_no", basepaydata.getorderid());
	    paramsmap.put("notify_url", wxpayconfig.getnotifydomain().concat(basepaydata.getnotify().gettype()));
	    map<string, integer> amountmap = new hashmap<>();
	    amountmap.put("total", basepaydata.getprice().multiply(new bigdecimal("100")).intvalue());
	    paramsmap.put("amount", amountmap);
	    return paramsmap;
	}
}

5. 获取请求对象

获取请求对象,用来发送请求,这里也单独封装成一个方法了,毕竟设置发送请求的类型、编码格式、请求头等信息都是一样的,没有必要每种请求都写一次。

@slf4j
public class wxpaycommon {
	/**
	  * 获取请求对象(post请求)
	  * @param wxpayconfig 微信配置类
	  * @param apitype 接口请求地址
	  * @param paramsmap 请求参数
	  * @return post请求对象
	  */
	private static httppost gethttppost(wxpayconfig wxpayconfig, wxapitype apitype, map<string, object> paramsmap) {
	    // 1.设置请求地址
	    httppost httppost = new httppost(wxpayconfig.getdomain().concat(apitype.gettype()));
	
	    // 2.设置请求数据
	    gson gson = new gson();
	    string jsonparams = gson.tojson(paramsmap);
	
	    // 3.设置请求信息
	    stringentity entity = new stringentity(jsonparams, "utf-8");
	    entity.setcontenttype("application/json");
	    httppost.setentity(entity);
	    httppost.setheader("accept", "application/json");
	    return httppost;
	}
}

6. 解析响应数据

请求发送后会有响应数据,不同的九游会下载的支付方式,返回的数据也不一样,那么我们封装的方法统一返回map,剩下的交给调用方自行处理。除此之外,最为重要的便是错误处理,例如系统错误、签名错误等,我们需要解析微信的错误数据并作出处理。

@slf4j
public class wxpaycommon {
	/**
	  * 解析响应数据
	  * @param response 发送请求成功后,返回的数据
	  * @return 微信返回的参数
	  */
	private static hashmap<string, string> resolverresponse(closeablehttpresponse response) {
	    try {
	        // 1.获取请求码
	        int statuscode = response.getstatusline().getstatuscode();
	        // 2.获取返回值 string 格式
	        final string bodyasstring = entityutils.tostring(response.getentity());
	
	        gson gson = new gson();
	        if (statuscode == 200) {
	            // 3.如果请求成功则解析成map对象返回
	            hashmap<string, string> resultmap = gson.fromjson(bodyasstring, hashmap.class);
	            return resultmap;
	        } else {
	            if (stringutils.isnoneblank(bodyasstring)) {
	                log.error("微信支付请求失败,提示信息:{}", bodyasstring);
	                // 4.请求码显示失败,则尝试获取提示信息
	                hashmap<string, string> resultmap = gson.fromjson(bodyasstring, hashmap.class);
	                throw new defaultexception(resultmap.get("message"));
	            }
	            log.error("微信支付请求失败,未查询到原因,提示信息:{}", response);
	            // 其他异常,微信也没有返回数据,这就需要具体排查了
	            throw new ioexception("request failed");
	        }
	    } catch (exception e) {
	        e.printstacktrace();
	        throw new defaultexception(e.getmessage());
	    } finally {
	        try {
	            response.close();
	        } catch (ioexception e) {
	            e.printstacktrace();
	        }
	    }
	}
}

7.微信支付回调将通知参数处理方法

工具类名建议自己定义,符合自身项目规范即可。

public class httputils {
    /**
     * 将通知参数转化为字符串
     * @param request
     * @return
     */
    public static string readdata(httpservletrequest request) {
        bufferedreader br = null;
        try {
            stringbuilder result = new stringbuilder();
            br = request.getreader();
            for (string line; (line = br.readline()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.tostring();
        } catch (ioexception e) {
            throw new runtimeexception(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }
            }
        }
    }
}

本文标签: