hello,大家好呀,这次想给大家分享一下近期学习研究的技术,那就是微信支付了。提到微信支付,大家肯定熟悉的不能再熟悉了,梦梦也就不多解释了。
由于工作原因,这两天就一直在学习微信支付这方面的知识,文档、视频、SDK真的是每一样都不落下,边看边动手操作,用视频上给的参数配置创建项目写接口、写笔记等等,下面就是学习过程中记录的笔记辽。
如果有需要笔记+代码的码友们,可以去梦梦空间搜 “微信支付(笔记和代码)”,也可以点击链接查看 微信支付(笔记+代码)
代码:(其中一部分哈)
/**
* @Decription 获取商户的私钥
* private 私有方法使私钥安全性提高
* @Param fileName 私钥文件的路径
* @Return java.security.PrivateKey
* @Author lmh
* @Date 2022/5/12 0:48
*/
private PrivateKey getPrivateKey(String fileName){
try {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
new FileInputStream(fileName));
return merchantPrivateKey;
} catch (FileNotFoundException e) {
//抛出异常,并把错误文件继续向上抛出
throw new RuntimeException("私钥文件不存在",e);
}
}
/**
* @Decription 获取签名验证器,会定时下载和更新商户对应的平台证书
* @Param null
* @Return com.wechat.pay.contrib.apache.httpclient.auth.Verifier
* @Author lmh
* @Date 2022/5/12 3:35
*/
@Bean //让这个方法可以自动执行,不执行多次,应用程序启动的时候执行一次即可
public Verifier getVerifier() {
log.info("获取签名验证器");
//1,获取商户私钥
PrivateKey merchantPrivateKey = this.getPrivateKey(privateKeyPath);
//2,获取私钥签名对象
PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, merchantPrivateKey);
//3,获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
//4,获取身份认证对象
WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
//5,向证书管理器增加需要自动更新平台证书的商户信息
try {
certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8));
// ... 若有多个商户号,可继续调用putMerchant添加商户信息
//6,从证书管理器中获取verifier
Verifier verifier =certificatesManager.getVerifier(mchId);
return verifier;
} catch (Exception e) {
throw new RuntimeException("获取失败",e);
}
}
/**
* @Decription 获取微信支付的远程请求对象 HttpClient请求对象
* @Param verifier 签名验证器
* @Return org.apache.http.impl.client.CloseableHttpClient
* @Author lmh
* @Date 2022/5/12 3:22
*/
@Bean(name = "wxPayClient") //配置类中加@bean 表示项目启动的时候就会加载bean
public CloseableHttpClient getWxPayClient(Verifier verifier){
log.info("获取httpclient对象");
//1,获取商户私钥
PrivateKey merchantPrivateKey = this.getPrivateKey(privateKeyPath);
//获取WechatPayHttpClientBuilder对象
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier));
// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
CloseableHttpClient httpClient = builder.build();
return httpClient;
}
/**
* 获取HttpClient,无需进行应答签名验证,跳过验签的流程
*/
@Bean(name = "wxPayNoSignClient")
public CloseableHttpClient getWxPayNoSignClient(){
//获取商户私钥
PrivateKey privateKey = getPrivateKey(privateKeyPath);
//用于构造HttpClient
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
//设置商户信息
.withMerchant(mchId, mchSerialNo, privateKey)
//无需进行签名验证、通过withValidator((response) -> true)实现
.withValidator((response) -> true);
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
CloseableHttpClient httpClient = builder.build();
log.info("== getWxPayNoSignClient END ==");
return httpClient;
}
/**
* @program: payment_demo
* @description: 微信支付
* @author: lmh
* @create: 2022-05-12 14:11
**/
@CrossOrigin //跨域
@RestController
@RequestMapping("/api/wx-pay")
@Api(tags = "网站微信支付API")
@Slf4j
public class WxPayController {
@Resource
private WxPayService wxPayService;
@Resource
private Verifier varifier;
/**
* @Decription 传入商品ID,调用Native下单接口,生成支付二维码
* API V3 版本要使用RESTful风格
* @Param null
* @Return com.payment.vo.R
* @Author lmh
* @Date 2022/5/12 14:18
*/
@ApiOperation(value = "调用Native下单接口,生成支付二维码")
@PostMapping("/native/{productId}")
public R nativePay(@PathVariable Long productId) throws Exception{
log.info("发起支付请求,支付订单号是:"+productId);
//1,返回支付二维码链接和订单号
Map<String,Object> map = wxPayService.nativePay(productId);
return R.ok().setData(map);
}
/**
* @Decription 接收通知
* 这个地址要和我们下单的时候设置的notify_url 地址是一样的,这样的话当我们调用下单接口给微信发送请求的时候,
* 微信是根据我们的notify_url参数中的地址给我们通知、应答的
* @Param null
* @Return java.lang.String
* @Author lmh
* @Date 2022/5/13 13:46
*/
@PostMapping("/native/notify")
public String nativeNotify(HttpServletRequest request, HttpServletResponse response){
Gson gson = new Gson();
Map<String,Object> bodyMap; //通知对象,JSON格式的
Map<String,Object> responseMap = new HashMap<>(); //给应答对象
try {
//1,处理通知参数,即处理微信给我们发出的请求
String body = HttpUtils.readData(request);
//2,将请求体转化为JSON
bodyMap = gson.fromJson(body, HashMap.class);
String requestId = (String) bodyMap.get("id");
log.info("支付通知的ID:{}",requestId);
log.info("支付通知的完整数据:{}",bodyMap);
//int i = 9/0;
//3,通知签名验证
//针对微信通知(给我们请求)的签名验证,需要自己编写;但针对微信应答(给我们响应)的签名验证,封装在SDK中,不需要我们编写
WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest = new WechatPay2ValidatorForRequest(varifier, body, requestId);
if (!wechatPay2ValidatorForRequest.validate(request)){
log.error("通知签名验证失败");
response.setStatus(500);
responseMap.put("code", "FAIL");
responseMap.put("message","通知签名验证失败");
return gson.toJson(responseMap);
}
log.info("验签成功");
//4,处理订单(订单状态,记录日志等等)
wxPayService.processOrder(bodyMap);
//模拟应答超时5秒,微信会按照频次重复的给我们发通知
//TimeUnit.SECONDS.sleep(5);
//发送成功应答
response.setStatus(200);
responseMap.put("code", "SUCCESS");
responseMap.put("message","成功");
return gson.toJson(responseMap);
} catch (Exception e) {
e.printStackTrace();
//发送失败应答
response.setStatus(500);
responseMap.put("code", "FAIL");
responseMap.put("message","失败");
return gson.toJson(responseMap);
}
}
/**
* @Decription 取消订单
* @Param null
* @Return com.payment.vo.R
* @Author lmh
* @Date 2022/5/14 23:59
*/
@ApiOperation(value = "用户取消订单")
@PostMapping("/cancel/{orderNo}")
public R cancel(@PathVariable String orderNo) throws Exception {
log.info("用户取消订单");
wxPayService.cancelOrder(orderNo);
return R.ok().setMessage("订单已经取消");
}
/**
* @Decription 查单接口 实际业务中不需要写controller,是写在定时任务中的,
* 写controller只是方便我们测试
* @Param null
* @Return com.payment.vo.R
* @Author lmh
* @Date 2022/5/15 0:40
*/
@ApiOperation(value = "查询订单 : 测试使用")
@GetMapping("/query/{orderNo}")
public R queryOrder(@PathVariable String orderNo) throws Exception {
log.info("查询订单");
String result = wxPayService.queryOrder(orderNo);
return R.ok().setMessage("查询成功").data("result",result);
}
/**
* @Decription 申请退款
* @Param orderNo :订单编号; reason : 退款原因
* @Return com.payment.vo.R
* @Author lmh
* @Date 2022/5/15 21:48
*/
@ApiOperation(value = "申请退款")
@ApiImplicitParams({@ApiImplicitParam(name = "orderNo",value = "订单编号"),
@ApiImplicitParam(name = "reason",value = "退款原因")})
@PostMapping("/refunds/{orderNo}/{reason}")
public R refunds(@PathVariable String orderNo,@PathVariable String reason) throws Exception{
log.info("申请退款");
wxPayService.refund(orderNo,reason);
return R.ok();
}
/**
* @Decription
* @Param request 微信通知的请求
* @param response 我们给微信的应答响应
* @Return java.lang.String
* @Author lmh
* @Date 2022/5/15 23:47
*/
@PostMapping("/refunds/notify")
public String refundsNotify(HttpServletRequest request,HttpServletResponse response){
log.info("退款通知");
Gson gson = new Gson();
Map<String,Object> bodyMap; //通知对象,JSON格式的
Map<String,String> responseMap = new HashMap<>(); //应答对象
try {
//1,处理通知参数
String body = HttpUtils.readData(request);
bodyMap = gson.fromJson(body,HashMap.class);
String requestId = (String) bodyMap.get("id"); //通知ID,唯一的
log.info("退款通知的id:{}",requestId);
log.info("退款通知完整的数据:{}",bodyMap);
//2,通知签名的验证
//针对微信通知(给我们请求)的签名验证,需要自己编写;但针对微信应答(给我们响应)的签名验证,封装在SDK中,不需要我们编写
WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest = new WechatPay2ValidatorForRequest(varifier, body, requestId);
if (!wechatPay2ValidatorForRequest.validate(request)){
log.error("退款通知验签失败");
//失败的应答
response.setStatus(500);
responseMap.put("code","FAIL");
responseMap.put("message","退款通知验签失败");
return gson.toJson(responseMap);
}
log.info("退款通知验签成功");
//3,处理退款订单
wxPayService.processRefund(bodyMap);
//4,成功应答
response.setStatus(200);
responseMap.put("code","SUCCESS");
responseMap.put("message","退款通知验签成功");
return gson.toJson(responseMap);
} catch (Exception e) {
e.printStackTrace();
//失败的应答
response.setStatus(500);
responseMap.put("code","FAIL");
responseMap.put("message","退款通知验签失败");
return gson.toJson(responseMap);
}
}
/**
* @Decription 实际业务中我们是不用对外提供接口的,写在定时任务中的
* @Param refundNo 退款单号
* @Return com.payment.vo.R
* @Author lmh
* @Date 2022/5/15 23:28
*/
@ApiOperation(value = "查询退款 : 测试使用")
@GetMapping("/query-refund/{refundNo}")
public R queryRefund(@PathVariable String refundNo) throws Exception{
log.info("查询退款");
String result = wxPayService.queryRefund(refundNo);
return R.ok().setMessage("查询成功").data("result",result);
}
/**
* @Decription 获取账单url
* @Param billDate 账单日期
* @param type 账单类型(交易账单 tradebill ; 资金账单 fundflowbill)
* @Return com.payment.vo.R
* @Author lmh
* @Date 2022/5/16 1:29
*/
@ApiOperation(value = "获取账单URL:测试使用")
@GetMapping("/querybill/{billDate}/{type}")
public R quryTradeBill(@PathVariable String billDate,@PathVariable String type) throws Exception{
log.info("获取账单url");
String downloadUrl = wxPayService.queryBill(billDate,type);
return R.ok().setMessage("获取账单url成功").data("downloadUrl",downloadUrl);
}
@ApiOperation("下载账单")
@GetMapping("/downloadbill/{billDate}/{type}")
public R downloadBill(@PathVariable String billDate, @PathVariable String type) throws Exception {
log.info("下载账单");
String result = wxPayService.downloadBill(billDate, type);
return R.ok().data("result", result);
}
}
如果有需要笔记+代码的码友们,可以去梦梦空间搜 “微信支付(笔记和代码)”,也可以点击链接查看 微信支付(笔记+代码)
好了,就不多聊了哈,现在已经很晚了,去碎觉了,拜拜