m.put(“nonce_str”, WXPayUtil.generateNonceStr());
//商品描述 例如:天天爱消除-游戏充值
m.put(“body”, “换芯易-” + order.getGoodsModel() + “购买”);
//订单号
m.put(“out_trade_no”, order.getOrderSn());
m.put(“total_fee”, order.getActualPrice().multiply(new BigDecimal(“100”)).longValue() + “”);
m.put(“spbill_create_ip”, clientIp);
m.put(“notify_url”, OrderUtils.NOTIFY_URL);
m.put(“trade_type”, “NATIVE”);
//trade_type=NATIVE时,此参数必传
m.put(“product_id”, order.getId());
//生成签名
m.put(“sign”, WXPayUtil.generateSignature(m, OrderUtils.APP_KEY));
//发送请求,请求路径:UNIFIED_ORDER_URL = “https://api.mch.weixin.qq.com/pay/unifiedorder”
HttpClient client = new HttpClient(OrderUtils.UNIFIED_ORDER_URL);
//设置xml格式的参数,要把map转为xml
client.setXmlParam(WXPayUtil.mapToXml(m));
//设置支持https
client.setHttps(true);
//执行请求发送
client.post();
//xml转为map接受结果
Map<String, String> response = WXPayUtil.xmlToMap(client.getContent());
long end = System.currentTimeMillis();
System.out.println(“请求https://api.mch.weixin.qq.com/pay/unifiedorder耗时:” + (end - begin) + “ms”);
System.out.println(“请求结果:” + JSON.toJSONString(response));
if (“SUCCESS”.equals(response.get(“return_code”)) && “SUCCESS”.equals(response.get(“result_code”))) {
Map<String, String> param = new HashMap<>();
//二维码地址
param.put(“code_url”, response.get(“code_url”));
param.put(“order_sn”, order.getOrderSn());
param.put(“order_id”, order.getId());
param.put(“total_fee”, order.getActualPrice().multiply(new BigDecimal(“100”)).longValue() + “”);
return param;
}
//为空代表下单失败
return null;
}
上面代码中最重要的返回结果就是code_url。即生成的支付二维码地址,然后用微信扫码并付款即可。
4.APP统一下单
应用场景是APP端调用微信支付,对接uniapp开发的app
归纳如下:
appid | APP的app_id |
mch_id | 商户号 |
nonce_str | 随机字符串,可以调用 WXPayUtil下 generateNonceStr方法生成 |
sign | 可以调用 WXPayUtil下 generateSignature方法生成,这个方法需要用到准备工作中的商户API密钥(APP_KEY)来加密。 |
body | 商品描述,建议软件名字+产品操作,例如天天爱消除-游戏充值 |
out_trade_no | 我们自己生成的订单号,保证同一商号下唯一即可 |
total_fee | 金额,注意单位是分 |
spbill_create_ip | 用户客户端ip |
notify_url | 支付结果通知回调地址 |
trade_type | 交易类型,我们这里填APP |
但是下完单之后,怎么样才能让前端调用微信支付呢?这里需要查看uniapp 官方文档 微信小程序支付:
我们可以看到常规的比如 appid、商户号 需要,充要的是预支付id prepayid和签名 。
代码实现如下:
public Map<String, String> unifiedOrderByApp(Order order, String clientIp) throws Exception {
long begin = System.currentTimeMillis();
//使用map封装微信支付需要的固定参数
Map<String, String> m = new HashMap<>();
//1、设置支付参数
m.put(“appid”, OrderUtils.APP_APP_ID);
m.put(“mch_id”, OrderUtils.MCH_ID);
m.put(“nonce_str”, WXPayUtil.generateNonceStr());
//商品描述 例如:天天爱消除-游戏充值
m.put(“body”, “换芯易-” + order.getGoodsModel() + “购买”);
//订单号
m.put(“out_trade_no”, order.getOrderSn());
m.put(“total_fee”, order.getActualPrice().multiply(new BigDecimal(“100”)).longValue() + “”);
m.put(“spbill_create_ip”, clientIp);
m.put(“notify_url”, OrderUtils.NOTIFY_URL);
m.put(“trade_type”, “APP”);
//生成签名
m.put(“sign”, WXPayUtil.generateSignature(m, OrderUtils.APP_KEY));
//发送请求,请求路径:UNIFIED_ORDER_URL = “https://api.mch.weixin.qq.com/pay/unifiedorder”
HttpClient client = new HttpClient(OrderUtils.UNIFIED_ORDER_URL);
//设置xml格式的参数,要把map转为xml
client.setXmlParam(WXPayUtil.mapToXml(m));
//设置支持https
client.setHttps(true);
//执行请求发送
client.post();
//xml转为map接受结果
Map<String, String> response = WXPayUtil.xmlToMap(client.getContent());
long end = System.currentTimeMillis();
System.out.println(“请求https://api.mch.weixin.qq.com/pay/unifiedorder耗时:” + (end - begin) + “ms”);
System.out.println(“请求结果:” + JSON.toJSONString(response));
if (“SUCCESS”.equals(response.get(“return_code”)) && “SUCCESS”.equals(response.get(“result_code”))) {
Map<String, String> param = new HashMap<>();
param.put(“appid”, OrderUtils.APP_APP_ID);
//随机字符串
param.put(“noncestr”, WXPayUtil.generateNonceStr());
//固定值
param.put(“package”, “Sign=WXPay”);
param.put(“partnerid”, OrderUtils.MCH_ID);
param.put(“prepayid”, response.get(“prepay_id”));
//时间戳,但是单位为s,不是毫秒
param.put(“timestamp”, String.valueOf(System.currentTimeMillis() / 1000));
//param.put(“package”, response.get(“sign”));
param.put(“sign”, WXPayUtil.generateSignature(param, OrderUtils.APP_KEY));
return param;
}
//为空代表下单失败
return null;
}
按照uniapp官方要求的格式封装返回结果给前端。
5.三种支付方式返回结果
对于上面三种支付方式,他们的返回结果我都没有细细分析,因为官方文档写得很详细,我们非常需要关注的也就预支付id这个返回结果。所以贴一下官方文档,有兴趣可以仔细看看(重要的我会标注)
共有
其中APP和JSAPI得着重关注 预支付id
native扫码支付主要关注 code_url
6.查单
我们实际业务中,过于依赖微信支付的回调结果来判断订单状态显然是不太合适的,所以自己去查单这种操作十分常见。查单需要的必填参数归纳如下:
appid | app_id |
mch_id | 商户号 |
nonce_str | 随机字符串,可以调用 WXPayUtil下 generateNonceStr方法生成 |
sign | 可以调用 WXPayUtil下 generateSignature方法生成,这个方法需要用到准备工作中的商户API密钥(APP_KEY)来加密。 |
out_trade_no | 我们自己生成的订单号,保证同一商号下唯一即可,这个订单号需要和我们当时调用统一下单传过去的单号一致 |
ps:这里的out_trade_no可以用transaction_id代替,但是一般我们查单的时候可能还没有transaction_id,所以只能用我们自己程序业务订单号out_trade_no去查,因为transaction_id需要调查单接口才能得到,也就是说如果我们以后"二次查单"可以用这个参数。
代码实现入下:
public Map<String, String> queryOrderStatus(String orderSn) throws Exception {
long beginTime = System.currentTimeMillis();
//1、封装参数
Map<String, String> m = new HashMap<>();
m.put(“appid”, OrderUtils.APP_APP_ID);
m.put(“mch_id”, OrderUtils.MCH_ID);
m.put(“out_trade_no”, orderSn);
m.put(“nonce_str”, WXPayUtil.generateNonceStr());
//生成签名
m.put(“sign”, WXPayUtil.generateSignature(m, OrderUtils.APP_KEY));
//发送请求,请求路径:UNIFIED_ORDER_URL = “https://api.mch.weixin.qq.com/pay/orderquery”
HttpClient client = new HttpClient(OrderUtils.ORDER_QUERY_URL);
client.setXmlParam(WXPayUtil.mapToXml(m));
client.setHttps(true);
client.post();
//3、返回第三方的数据
Map<String, String> result = WXPayUtil.xmlToMap(client.getContent());
long endTime = System.currentTimeMillis();
System.out.println(“请求https://api.mch.weixin.qq.com/pay/orderquery耗时:” + (endTime - beginTime) + “ms”);
System.out.println(“请求结果:” + JSON.toJSONString(result));
return result;
}
返回结果参数部分如下,重要的标注起来:
一般情况下主要是利用trade_state来判断用户是否支付成功从而更新订单或记录交易成功物流等业务操作。
Map<String, String> map = orderDao.queryOrderStatus(orderSn);
if (map == null) {
return ApiResponse.createApiResponse(ApiResponse.HTTP_STATE_400_ERROR_10001, “微信支付出错”);
}
//微信
if (“SUCCESS”.equals(map.get(“trade_state”))) {
//更新订单或记录交易成功物流等业务操作
}
7.退款
言简意赅,就是方便( rnm,退钱 )
重要参数
appid | app_id |
mch_id | 商户号 |
nonce_str | 随机字符串,可以调用 WXPayUtil下 generateNonceStr方法生成 |
sign | 可以调用 WXPayUtil下 generateSignature方法生成,这个方法需要用到准备工作中的商户API密钥(APP_KEY)来加密。 |
out_trade_no | 我们自己生成的订单号,保证同一商号下唯一即可,这个订单号需要和我们当时调用统一下单传过去的单号一致 |
transaction_id | 和上面的out_trade_no 二选一 ,官方推荐transaction_id |
out_refund_no | 其实和统一下单的时候的订单号原理差不多,我们自己随机生成一个,保证同一商户系统下唯一即可 |
total_fee | 要退款的订单的金额 |
refund_fee | 实际要退款的金额 |
代码实现如下:
public Map<String, String> refundOrder(RefundTrace refundTrace, Order order) throws Exception {
long begin = System.currentTimeMillis();
//使用map封装微信支付需要的固定参数
Map<String, String> m = new HashMap<>();
//1、设置支付参数
m.put(“appid”, OrderUtils.WX_APP_ID);
m.put(“mch_id”, OrderUtils.MCH_ID);
m.put(“nonce_str”, WXPayUtil.generateNonceStr());
//微信支付订单号
m.put(“transaction_id”, order.getPayId());
//商户退款单号
m.put(“out_refund_no”, refundTrace.getRefundSn());
//订单金额
m.put(“total_fee”, order.getActualPrice().multiply(new BigDecimal(“100”)).longValue() + “”);
//退款金额 即实际退款金额
m.put(“refund_fee”, refundTrace.getRefundAmount().multiply(new BigDecimal(“100”)).longValue() + “”);
//退款原因
m.put(“refund_desc”, refundTrace.getRefundReason());
m.put(“notify_url”, OrderUtils.NOTIFY_URL);
//生成签名
m.put(“sign”, WXPayUtil.generateSignature(m, OrderUtils.APP_KEY));
发送请求,请求路径:ORDER_REFUND_URL = “https://api.mch.weixin.qq.com/secapi/pay/refund”
String content = HttpRequestUtils.refundRequest(WXPayUtil.mapToXml(m));
//xml转为map接受结果
Map<String, String> response = WXPayUtil.xmlToMap(content);
long end = System.currentTimeMillis();
System.out.println(“请求https://api.mch.weixin.qq.com/secapi/pay/refund耗时:” + (end - begin) + “ms”);
System.out.println(“请求结果:” + JSON.toJSONString(response));
if (“SUCCESS”.equals(response.get(“return_code”)) && “SUCCESS”.equals(response.get(“result_code”))) {
Map<String, String> param = new HashMap<>();
param.put(“refund_id”, response.get(“refund_id”));
param.put(“refund_fee”, response.get(“refund_fee”));
return param;
}
//为空代表退款失败
return null;
}
这里注意一下!!!! 因为操作涉及到把商户方的钱转回到买方的操作,所以安全系数比较高,调用退款api官方要求证书
证书申请途径如下:
微信商户平台(pay.weixin.qq.com)–>账户中心–>账户设置–>API安全–>申请API证书
申请到证书,需要放到项目下:
那么我们这里的http请求就不能用之前的了,需要配置该证书
上面代码中使用的refundRequest函数细节如下:
public static String refundRequest(String order) throws Exception {
try {
KeyStore clientStore = KeyStore.getInstance(“PKCS12”);
// 读取项目存放的PKCS12证书文件
FileInputStream instream = new FileInputStream(“apiclient_cert.p12”);
try {
// 指定PKCS12的密码(商户ID)
clientStore.load(instream, OrderUtils.MCH_ID.toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(clientStore, OrderUtils.MCH_ID.toCharArray()).build();
// 指定TLS版本
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{“TLSv1”}, null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
// 设置httpclient的SSLSocketFactory
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
try {
HttpPost httpost = new HttpPost(OrderUtils.ORDER_REFUND_URL); // 设置响应头信息
// httpost.addHeader(“Connection”, “keep-alive”);
// httpost.addHeader(“Accept”, “*/*”);
// httpost.addHeader(“Content-Type”, CONTENT_TYPE_FORM.toString());
// httpost.addHeader(“X-Requested-With”, “XMLHttpRequest”);
// httpost.addHeader(“Cache-Control”, “max-age=0”);
// httpost.addHeader(“User-Agent”, DEFAULT_USER_AGENT);
httpost.setEntity(new StringEntity(order, “UTF-8”));
CloseableHttpResponse response = httpclient.execute(httpost);
try {
HttpEntity entity = response.getEntity();
String jsonStr = EntityUtils.toString(response.getEntity(), “UTF-8”);
EntityUtils.consume(entity);
return jsonStr;
} finally {
response.close();
}
} finally {
httpclient.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
8.补充
我所使用的HttpClient工具类源码,我这里使用主要用于发送带xml参数的post请求
public class HttpClient {
private String url;
private Map<String, String> param;
private int statusCode;
private String content;
private String xmlParam;
private boolean isHttps;
public boolean isHttps() {
return isHttps;
}
public void setHttps(boolean isHttps) {
this.isHttps = isHttps;
}
public String getXmlParam() {
return xmlParam;
}
public void setXmlParam(String xmlParam) {
this.xmlParam = xmlParam;
}
public HttpClient(String url, Map<String, String> param) {
this.url = url;
this.param = param;
}
public HttpClient(String url) {
this.url = url;
}
public void setParameter(Map<String, String> map) {
param = map;
}
public void addParameter(String key, String value) {
if (param == null) {
param = new HashMap<String, String>();
}
param.put(key, value);
}
public void post() throws ClientProtocolException, IOException {
HttpPost http = new HttpPost(url);
setEntity(http);
execute(http);
}
public void put() throws ClientProtocolException, IOException {
HttpPut http = new HttpPut(url);
最后
Java架构学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。
还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
l;
}
public void setParameter(Map<String, String> map) {
param = map;
}
public void addParameter(String key, String value) {
if (param == null) {
param = new HashMap<String, String>();
}
param.put(key, value);
}
public void post() throws ClientProtocolException, IOException {
HttpPost http = new HttpPost(url);
setEntity(http);
execute(http);
}
public void put() throws ClientProtocolException, IOException {
HttpPut http = new HttpPut(url);
最后
Java架构学习技术内容包含有:Spring,Dubbo,MyBatis, RPC, 源码分析,高并发、高性能、分布式,性能优化,微服务 高级架构开发等等。
还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。
[外链图片转存中…(img-sc5a2irU-1714345485546)]