目录
开篇介绍
一、微信支付-Maven依赖加入和代码参数准备
二、商户私钥证书代码读取
三、微信订单支付系列接口URL配置
四、快速验证统一下单接口
五、查询订单支付状态验证
六、关闭订单状态验证
七、微信支付-SDK自动完成支付签名流程解读
八、微信支付的退款注意事项
九、微信支付退款验证
十、查询退款结果
本文介绍微信支付中的Native支付方式,版本是APIv3,其中Native和JSAPI的区别如下
Native支付:商家在系统中按微信支付协议生成支付二维码,用户扫码拉起微信收银台,确认并完成付款
JSAPI支付:商家张贴收款码物料,用户打开扫一扫,扫码后输入金额,完成付款
以下博文没有掺杂过多业务逻辑,对象数据都是固定写的,方便将注意力集中在微信支付的接口使用上。正式对接到项目里面,只需要改动支付的部分业务参数即可。
微信支付的文档还是很详细的,建议多看几遍,以下博文也都是根据官方文档的描述来操作的。
注意:微信支付个人无法对接操作,需要有公司商户账号,一般开发过程中是产品经理或者相关负责人提供。
微信支付接入指引 - 微信支付商户平台微信支付接入指引介绍了线下场所、公众号、小程序、PC网站、APP、企业微信等经营场景如何快速开通微信支付,实现商家在各类交易场景中用微信支付快速收款的需求。https://pay.weixin.qq.com/static/applyment_guide/applyment_index.shtml
第一步application.properties:
#商户号
pay.wechat.mch-id=160164xxxx
#公众号id 需要和商户号绑定
pay.wechat.wx-pay-appid=wx5beac15xxxxx
#商户证书序列号,需要和证书对应
pay.wechat.mch-serial-no=7064ADC5FE84CA2A3Dxxxxxxxx
#api密钥
pay.wechat.api-v3-key=peYcTwRF581UOdaUqoxxxxxxx
#商户私钥路径(微信服务端会根据证书序列号,找到证书获取公钥进行解密数据)
pay.wechat.private-key-path=classpath:/cert/apiclient_key.pem
#支付成功页面跳转
pay.wechat.success-return-url=https://baidu.com
#支付成功,回调通知
pay.wechat.callback-url=http://api.xxx.com/shop-server/api/callback/order/v1/wechat
第二步:证书配置加入
Maven依赖加入
微信支付API v3的Apache HttpClient扩展,实现了请求签名的生成和应答签名的验证。
使用说明 https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient
com.github.wechatpay-apiv3
wechatpay-apache-httpclient
0.3.0
第四步 第一步骤的配置读取:
package net.wnn.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "pay.wechat")
public class WechatPayConfig {
/**
* 商户号
*/
private String mchId;
/**
* 公众号id 需要和商户号绑定
*/
private String wxPayAppid;
/**
* 商户证书序列号,需要和证书对应
*/
private String mchSerialNo;
/**
* API V3密钥
*/
private String apiV3Key;
/**
* 商户私钥路径(微信服务端会根据证书序列号,找到证书获取公钥进行解密数据)
*/
private String privateKeyPath;
/**
* 支付成功页面跳转
*/
private String successReturnUrl;
/**
* 支付成功,回调通知
*/
private String callbackUrl;
}
java加载商户证书私钥 以下部分内容来自微信支付文档样例
https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient
商户申请商户API证书时,会生成商户私钥,并保存在本地证书文件夹的文件apiclient_key.pem中
商户开发者可以使用方法PemUtil.loadPrivateKey()加载证书
文档中样例:
# 示例:私钥存储在文件
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
new FileInputStream("/path/to/apiclient_key.pem"));
# 示例:私钥为String字符串
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
new ByteArrayInputStream(privateKey.getBytes("utf-8")));
代码加载读取秘钥/定时获取微信签名验证器/获取http请求对象,会自动的处理签名和验签:
package net.wnn.config;
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.ScheduledUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.stream.Collectors;
@Configuration
@Slf4j
public class PayBeanConfig {
@Autowired
private WechatPayConfig payConfig;
/**
* 加载秘钥
*
* @return
* @throws IOException
*/
public PrivateKey getPrivateKey() throws IOException {
InputStream inputStream = new ClassPathResource(payConfig.getPrivateKeyPath()
.replace("classpath:", "")).getInputStream();
String content = new BufferedReader(new InputStreamReader(inputStream))
.lines().collect(Collectors.joining(System.lineSeparator()));
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey finalPrivateKey = kf.generatePrivate(
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
return finalPrivateKey;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
/**
* 定时获取微信签名验证器,自动获取微信平台证书(证书里面包括微信平台公钥)
*
* @return
*/
@Bean
public ScheduledUpdateCertificatesVerifier getCertificatesVerifier() throws IOException {
// 使用定时更新的签名验证器,不需要传入证书
ScheduledUpdateCertificatesVerifier verifier = null;
verifier = new ScheduledUpdateCertificatesVerifier(
new WechatPay2Credentials(payConfig.getMchId(),
new PrivateKeySigner(payConfig.getMchSerialNo(),
getPrivateKey())),
payConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
return verifier;
}
/**
* 获取http请求对象,会自动的处理签名和验签,
* 并进行证书自动更新
*
* @return
*/
@Bean("wechatPayClient")
public CloseableHttpClient getWechatPayClient(ScheduledUpdateCertificatesVerifier verifier) throws IOException {
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(payConfig.getMchId(),payConfig.getMchSerialNo() , getPrivateKey())
.withValidator(new WechatPay2Validator(verifier));
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
CloseableHttpClient httpClient = builder.build();
return httpClient;
}
}
package net.wnn.config;
public class WechatPayApi {
/**
* 微信支付主机地址
*/
public static final String HOST = "https://api.mch.weixin.qq.com";
/**
* Native下单
*/
public static final String NATIVE_ORDER = HOST+ "/v3/pay/transactions/native";
/**
* Native订单状态查询, 根据商户订单号查询
*/
public static final String NATIVE_QUERY = HOST+ "/v3/pay/transactions/out-trade-no/%s?mchid=%s";
/**
* 关闭订单接口
*/
public static final String NATIVE_CLOSE_ORDER = HOST+ "/v3/pay/transactions/out-trade-no/%s/close";
/**
* 申请退款接口
*/
public static final String NATIVE_REFUND_ORDER = HOST+ "/v3/refund/domestic/refunds";
/**
* 退款状态查询接口
*/
public static final String NATIVE_REFUND_QUERY = HOST+ "/v3/refund/domestic/refunds/%s";
}
验证下单测试方法:
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.wnn.ShopApplication;
import net.wnn.config.PayBeanConfig;
import net.wnn.config.WechatPayApi;
import net.wnn.config.WechatPayConfig;
import net.wnn.util.CommonUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ShopApplication.class)
@Slf4j
public class WechatPayTest {
@Autowired
private PayBeanConfig payBeanConfig;
@Autowired
private WechatPayConfig payConfig;
@Autowired
private CloseableHttpClient wechatPayClient;
/**
* 快速验证统一下单接口
* @throws IOException
*/
@Test
public void testNativeOrder() throws IOException {
String outTradeNo = CommonUtil.getStringNumRandom(32);
/**
* {
* "mchid": "1900006XXX",
* "out_trade_no": "native12177525012014070332333",
* "appid": "wxdace645e0bc2cXXX",
* "description": "Image形象店",
* "notify_url": "https://weixin.qq.com/",
* "amount": {
* "total": 1,
* "currency": "CNY"
* }
* }
*/
JSONObject payObj = new JSONObject();
payObj.put("mchid",payConfig.getMchId());
payObj.put("out_trade_no",outTradeNo);
payObj.put("appid",payConfig.getWxPayAppid());
payObj.put("description","王师傅的红包");
payObj.put("notify_url",payConfig.getCallbackUrl());
//订单总金额,单位为分。
JSONObject amountObj = new JSONObject();
amountObj.put("total",100);
amountObj.put("currency","CNY");
payObj.put("amount",amountObj);
//附属参数,可以用在回调
payObj.put("attach","{\"accountNo\":"+888+"}");
String body = payObj.toJSONString();
log.info("请求参数:{}",body);
StringEntity entity = new StringEntity(body,"utf-8");
entity.setContentType("application/json");
HttpPost httpPost = new HttpPost(WechatPayApi.NATIVE_ORDER);
httpPost.setHeader("Accept","application/json");
httpPost.setEntity(entity);
try(CloseableHttpResponse response = wechatPayClient.execute(httpPost)){
//响应码
int statusCode = response.getStatusLine().getStatusCode();
//响应体
String responseStr = EntityUtils.toString(response.getEntity());
log.info("下单响应码:{},响应体:{}",statusCode,responseStr);
}catch (Exception e){
e.printStackTrace();
}
}
}
下单参数:
请求参数:{"amount":{"total":100,"currency":"CNY"},"mchid":"1601644*****","out_trade_no":"CikYMPk*****QILoJ4Ts3mjDA",
"appid":"wx5beac15ca20******","description":"王师傅的红包","attach":"{\"accountNo\":888}","notify_url":"http://api.*****.com/shop-server/api/callback/order/v1/wechat"}
下单返回结果:
下单响应码:200,响应体:{"code_url":"weixin://wxpay/bizpayurl?pr=JGRuZXAzz"}
返回的code_url是一个二维码地址,后端自测可以直接打开cli.im网址可以进入草料二维码页面,复制code_url右侧会出现二维码图像,打开手机微信扫一扫就能看到金额标题等信息。正式环境对接的时候前端有对应js获取后端返回code_url后展示支付二维码。
手机微信扫码后的结果:二维码有效时间2小时
这样就完成下单支付的验证啦
查询订单测试方法:
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.wnn.ShopApplication;
import net.wnn.config.PayBeanConfig;
import net.wnn.config.WechatPayApi;
import net.wnn.config.WechatPayConfig;
import net.wnn.util.CommonUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ShopApplication.class)
@Slf4j
public class WechatPayTest {
@Autowired
private PayBeanConfig payBeanConfig;
@Autowired
private WechatPayConfig payConfig;
@Autowired
private CloseableHttpClient wechatPayClient;
/**
* 根据商户号订单号查询订单支付状态
* 未支付的情况下返回的是如下json
* {"amount":{"payer_currency":"CNY","total":100},"appid":"wx5beacxxxxx",
* "mchid":"160164xxxx","out_trade_no":"fRAv2Ccpd8xxxxxx",
* "promotion_detail":[],"scene_info":{"device_id":""},
* "trade_state":"NOTPAY","trade_state_desc":"订单未支付"}
*
* @throws IOException
*/
@Test
public void testNativeQuery() throws IOException {
String outTradeNo = "CikYMPkEmRPQILoJ4Ts3xxxxxx";
String url = String.format(WechatPayApi.NATIVE_QUERY,outTradeNo,payConfig.getMchId());
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Accept","application/json");
try(CloseableHttpResponse response = wechatPayClient.execute(httpGet)){
//响应码
int statusCode = response.getStatusLine().getStatusCode();
//响应体
String responseStr = EntityUtils.toString(response.getEntity());
log.info("查询响应码:{},响应体:{}",statusCode,responseStr);
}catch (Exception e){
e.printStackTrace();
}
}
}
查询订单微信返回接口信息:
查询响应码:200,响应体:{"amount":{"currency":"CNY","payer_currency":"CNY","payer_total":100,"total":100},"appid":"wx5beac15caxxxxx",
"attach":"{\"accountNo\":888}","bank_type":"OTHERS","mchid":"1601x","outxxxx_trade_no":"CikYMPkEmRPQIxxxxxx",
"payer":{"openid":"oiNKG04YwwLSlcW_xxxxxx"},"promotion_detail":[],"success_time":"2022-02-15T22:00:35+08:00",
"trade_state":"SUCCESS","trade_state_desc":"支付成功","trade_type":"NATIVE","transaction_id":"420000139920220xxxxx"}
关闭订单测试方法:
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.wnn.ShopApplication;
import net.wnn.config.PayBeanConfig;
import net.wnn.config.WechatPayApi;
import net.wnn.config.WechatPayConfig;
import net.wnn.util.CommonUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ShopApplication.class)
@Slf4j
public class WechatPayTest {
@Autowired
private PayBeanConfig payBeanConfig;
@Autowired
private WechatPayConfig payConfig;
@Autowired
private CloseableHttpClient wechatPayClient;
@Test
public void testNativeCloseOrder() throws IOException {
String outTradeNo = "2p9tZ05bve1ZcdfcgZxxxx";
JSONObject payObj = new JSONObject();
payObj.put("mchid",payConfig.getMchId());
String body = payObj.toJSONString();
log.info("请求参数:{}",body);
//将请求参数设置到请求对象中
StringEntity entity = new StringEntity(body,"utf-8");
entity.setContentType("application/json");
String url = String.format(WechatPayApi.NATIVE_CLOSE_ORDER,outTradeNo);
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Accept","application/json");
httpPost.setEntity(entity);
try(CloseableHttpResponse response = wechatPayClient.execute(httpPost)){
//响应码
int statusCode = response.getStatusLine().getStatusCode();
log.info("关闭订单响应码:{},无响应体",statusCode);
}catch (Exception e){
e.printStackTrace();
}
}
}
关闭订单测试方法返回 结果:
请求参数:{"mchid":"16016xxxx"}
2022-02-15 22:16:44.085 INFO 91680 --- [ main] WechatPayTest : 关闭订单响应码:204,无响应体
再次使用商户订单号查询订单状态:
已成功关闭啦
查询响应码:200,响应体:{"appid":"wx5beac1xxxx","attach":"{\"accountNo\":888}","mchid":"1601644xxxx","out_trade_no":"2p9tZ05bve1Zcxxxx","payer":{},"promotion_detail":[],"trade_state":"CLOSED","trade_state_desc":"订单已关闭"}
微信支付过程中包含签名验证等过程,在V3版本当中是SDK自动完成支付签名的验证操作,详情查看以下文档,本博客以下单验证接口为例子,截取部分构造签名串和设置http头信息的日志表明自动支付签名的过程。
微信支付签名规则文档
https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml
构造签名串日志
设置Http头
1、交易时间超过一年的订单无法提交退款
2、微信支付退款支持单笔交易分多次退款(不超50次),多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号
3、错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次
4、每个支付订单的部分退款次数不能超过50次
5、如果同一个用户有多笔退款,建议分不同批次进行退款,避免并发退款导致退款失败
6、申请退款接口的返回仅代表业务的受理情况,具体退款是否成功,需要通过退款查询接口获取结果
7、一个月之前的订单申请退款频率限制为:5000/min
8、同一笔订单多次退款的请求需相隔1分钟
退款验证代码:
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.wnn.ShopApplication;
import net.wnn.config.PayBeanConfig;
import net.wnn.config.WechatPayApi;
import net.wnn.config.WechatPayConfig;
import net.wnn.util.CommonUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ShopApplication.class)
@Slf4j
public class WechatPayTest {
@Autowired
private PayBeanConfig payBeanConfig;
@Autowired
private WechatPayConfig payConfig;
@Autowired
private CloseableHttpClient wechatPayClient;
/**
* 订单退款操作
* @throws IOException
*/
@Test
public void testNativeRefundOrder() throws IOException {
String outTradeNo = "CikYMPkEmRPQILoxxxxxxxxxx";
String refundNo = CommonUtil.getStringNumRandom(32);
// 请求body参数
JSONObject refundObj = new JSONObject();
//订单号
refundObj.put("out_trade_no", outTradeNo);
//退款单编号,商户系统内部的退款单号,商户系统内部唯一,
// 只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔
refundObj.put("out_refund_no", refundNo);
refundObj.put("reason","商品已售完");
refundObj.put("notify_url", payConfig.getCallbackUrl());
JSONObject amountObj = new JSONObject();
//退款金额
amountObj.put("refund", 10);
//实际支付的总金额
amountObj.put("total", 100);
amountObj.put("currency", "CNY");
refundObj.put("amount", amountObj);
String body = refundObj.toJSONString();
log.info("请求参数:{}",body);
StringEntity entity = new StringEntity(body,"utf-8");
entity.setContentType("application/json");
HttpPost httpPost = new HttpPost(WechatPayApi.NATIVE_REFUND_ORDER);
httpPost.setHeader("Accept","application/json");
httpPost.setEntity(entity);
try(CloseableHttpResponse response = wechatPayClient.execute(httpPost)){
//响应码
int statusCode = response.getStatusLine().getStatusCode();
//响应体
String responseStr = EntityUtils.toString(response.getEntity());
log.info("申请订单退款响应码:{},响应体:{}",statusCode,responseStr);
}catch (Exception e){
e.printStackTrace();
}
}
}
退款验证请求参数:
请求参数:{"reason":"商品已售完","amount":{"total":100,"currency":"CNY","refund":10},"out_trade_no":"CikYMPkEmRPQxxx","out_refund_no":"jKgEPCt3GtWSTsxxxx","notify_url":"http://api.open1024.com/shop-server/api/callback/order/v1/wechat"}
退款返回结果:
申请订单退款响应码:200,响应体:{"amount":{"currency":"CNY","discount_refund":0,"from":[],"payer_refund":10,"payer_total":100,"refund":10,"settlement_refund":10,"settlement_total":100,"total":100},"channel":"ORIGINAL","create_time":"2022-02-15T22:51:14+08:00","funds_account":"AVAILABLE","out_refund_no":"jKgEPCt3GtWSTsxxxxxxxx","oxxxut_trade_no":"CikYMPkEmRPQILxxxx","promotion_detail":[],"refund_id":"5030220093202xxxxxxxx","status":"PROCESSING","transaction_id":"420000139920xxxx","user_receivexxxd_account":"支付用户零钱"}
怎么知道退款结果呢?微信回调通知和主动查询,本博客介绍主动查询
查询退款验证方法:
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.wnn.ShopApplication;
import net.wnn.config.PayBeanConfig;
import net.wnn.config.WechatPayApi;
import net.wnn.config.WechatPayConfig;
import net.wnn.util.CommonUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ShopApplication.class)
@Slf4j
public class WechatPayTest {
@Autowired
private PayBeanConfig payBeanConfig;
@Autowired
private WechatPayConfig payConfig;
@Autowired
private CloseableHttpClient wechatPayClient;
/**
查询退款结果
* * @throws IOException
*/
@Test
public void testNativeRefundQuery() throws IOException {
String refundNo = "jKgEPCt3GtWSTssEqzFBbUDVgM235WKY";
String url = String.format(WechatPayApi.NATIVE_REFUND_QUERY,refundNo);
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("Accept","application/json");
try(CloseableHttpResponse response = wechatPayClient.execute(httpGet)){
//响应码
int statusCode = response.getStatusLine().getStatusCode();
//响应体
String responseStr = EntityUtils.toString(response.getEntity());
log.info("查询订单退款 响应码:{},响应体:{}",statusCode,responseStr);
}catch (Exception e){
e.printStackTrace();
}
}
查询退款返回结果:
查询订单退款 响应码:200,响应体:{"amount":{"currency":"CNY","discount_refund":0,"from":[],"payer_refund":10,"payer_total":100,"refund":10,"settlement_refund":10,"settlement_total":100,"total":100},"channel":"ORIGINAL","create_time":"2022-02-15T22:51:14+08:00","funds_account":"AVAILABLE","out_refund_no":"jKgEPCt3GtWSTssxxxx","out_trade_no":"CikYMPkEmRPQILoJ4Txxxx","promotion_detail":[],"refund_id":"50302200932022xxxxx","status":"SUCCESS","success_time":"2022-02-15T22:51:21+08:00","transaction_id":"42000013992022xxxx","user_received_account":"支付用户零钱"}
到这微信Native支付方式的下单、查询、退款、关单操作就通过测试工具类验证完成啦,以上内容没有掺杂过多业务逻辑,对象数据都是固定写的,方便将注意力集中在微信支付的接口使用中。正式对接到项目里面,只需要改动支付的部分业务参数即可。