正文
微信支付的下单操作分为了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();
}
}
}
}
}
————————————————————————————————
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
————————————————————————————————
版权声明:本文为CSDN博主「CV大魔王」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_45740561/article/details/128402504