| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- package com.ylx.web.controller.massage;
- import cn.hutool.core.date.DatePattern;
- import cn.hutool.core.date.DateUtil;
- import cn.hutool.core.util.ObjectUtil;
- import cn.hutool.core.util.StrUtil;
- import cn.hutool.http.ContentType;
- import cn.hutool.json.JSONObject;
- import cn.hutool.json.JSONUtil;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
- import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
- import com.github.binarywang.wxpay.exception.WxPayException;
- import com.github.binarywang.wxpay.service.WxPayService;
- import com.ijpay.core.IJPayHttpResponse;
- import com.ijpay.core.enums.RequestMethodEnum;
- import com.ijpay.core.kit.HttpKit;
- import com.ijpay.core.kit.PayKit;
- import com.ijpay.core.kit.WxPayKit;
- import com.ijpay.wxpay.WxPayApi;
- import com.ijpay.wxpay.enums.WxDomainEnum;
- import com.ijpay.wxpay.enums.v3.TransferApiEnum;
- import com.ijpay.wxpay.model.v3.BatchTransferModel;
- import com.ijpay.wxpay.model.v3.TransferDetailInput;
- import com.ylx.common.config.WxPayConfig;
- import com.ylx.common.core.domain.R;
- import com.ylx.common.weixinPay.enums.WxPayTypeEnum;
- import com.ylx.giftCard.domain.GiftCardOrder;
- import com.ylx.giftCard.enums.GiftCardOrderStatusEnum;
- import com.ylx.giftCard.service.IGiftCardOrderService;
- import com.ylx.massage.domain.TRecharge;
- import com.ylx.massage.domain.TWxUser;
- import com.ylx.massage.enums.BillTypeEnum;
- import com.ylx.massage.service.RefundVoucherService;
- import com.ylx.massage.service.TRechargeService;
- import com.ylx.massage.service.TWxUserService;
- import com.ylx.order.domain.TOrder;
- import com.ylx.order.enums.OrderStatusEnum;
- import com.ylx.order.service.TOrderService;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.web.bind.annotation.*;
- import javax.annotation.Resource;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.math.BigDecimal;
- import java.nio.charset.StandardCharsets;
- import java.security.cert.X509Certificate;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Map;
- import static com.ylx.common.constant.HttpStatus.SUCCESS;
- /**
- * @author jianlong
- * @date 2024-04-03 15:27
- */
- @RestController
- @Slf4j
- @RequestMapping("/wx/pay")
- @Api(tags = {"微信支付"})
- public class PayController {
- @Resource
- private WxPayConfig wxPayProperties;
- @Resource
- private TRechargeService rechargeService;
- @Resource
- private RefundVoucherService refundVoucherService;
- @Resource
- private TWxUserService wxUserService;
- String serialNo;
- @Resource
- private WxPayService wxPayService;
- @Resource
- private IGiftCardOrderService giftCardOrderService;
- @Resource
- private TOrderService orderService;
- /**
- * 小程序微信支付的第一步,统一下单
- */
- @PostMapping("/pay")
- @ApiOperation("AIPV3微信支付充值")
- public R createUnifiedOrder(@RequestBody TRecharge recharge) throws Exception {
- TRecharge rechargeResp = rechargeService.recharge(recharge);
- return rechargeService.getPay(rechargeResp.getRechargeNo(), recharge.getdMoney(), recharge.getcOpenId(), BillTypeEnum.RECHARGE.getInfo(), BillTypeEnum.RECHARGE.getCode().toString());
- }
- /**
- * 获取商户API证书序列号
- *
- * @return String 证书序列号
- */
- private String getSerialNumber() {
- if (StrUtil.isEmpty(serialNo)) {
- // 获取证书序列号
- X509Certificate certificate = PayKit.getCertificate(wxPayProperties.getCertPath());
- if (null != certificate) {
- serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
- // 提前两天检查证书是否有效
- boolean isValid = PayKit.checkCertificateIsValid(certificate, wxPayProperties.getMchId(), -2);
- log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
- }
- }
- System.out.println("serialNo:" + serialNo);
- return serialNo;
- }
- // /**
- // * 微信支付回调接口
- // *
- // * @param request
- // * @param response
- // */
- // @RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
- // @ResponseBody
- // @ApiOperation("微信支付回调接口")
- // public void payNotify(HttpServletRequest request, HttpServletResponse response) {
- // log.info("微信支付回调接口====================================>>>>微信支付回调接口");
- // Map<String, String> map = new HashMap<>(12);
- // try {
- // String timestamp = request.getHeader("Wechatpay-Timestamp");
- // String nonce = request.getHeader("Wechatpay-Nonce");
- // String serialNo = request.getHeader("Wechatpay-Serial");
- // String signature = request.getHeader("Wechatpay-Signature");
- //
- // log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
- // String result = HttpKit.readData(request);
- // log.info("支付通知密文 {}", result);
- //
- // // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
- // String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
- // wxPayProperties.getMchKey(), wxPayProperties.getPlatFormPath());
- //
- // log.info("支付通知明文 {}", plainText);
- //
- // if (StrUtil.isNotEmpty(plainText)) {
- // response.setStatus(200);
- // map.put("code", "SUCCESS");
- // map.put("message", "SUCCESS");
- // // 处理业务逻辑
- // JSONObject jsonObject = new JSONObject(plainText);
- // if (jsonObject.get("attach").equals(BillTypeEnum.WX_PAY.getCode().toString())) {
- // // 服务订单支付成功
- // orderService.payNotifyOrder(jsonObject.get("out_trade_no").toString());
- // } else if (jsonObject.get("attach").equals(PayTypeEnum.WX_PAY.getCode().toString())) {
- // // 商品订单支付成功
- // String productOrderNo = jsonObject.get("out_trade_no").toString();
- // log.info("商品订单支付回调开始处理,订单号:{}", productOrderNo);
- // productOrderInfoService.handleWxPayCallback(productOrderNo);
- // log.info("商品订单支付回调处理完成,订单号:{}", productOrderNo);
- // } else {
- // TRecharge outTradeNo = rechargeService.increaseAmount(jsonObject.get("out_trade_no").toString());
- // }
- // } else {
- // response.setStatus(500);
- // map.put("code", "ERROR");
- // map.put("message", "签名错误");
- // }
- // response.setHeader("Content-type", ContentType.JSON.toString());
- // response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
- // response.flushBuffer();
- // } catch (Exception e) {
- // log.error("系统异常", e);
- // }
- // }
- @PostMapping("/payNotify")
- public Map<String, String> handlePayNotify(@RequestBody String notifyData, @RequestHeader("Wechatpay-Signature") SignatureHeader signature) {
- Map<String, String> resp = new HashMap<>();
- try {
- // 1. SDK验签+解密报文
- WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayService.parseOrderNotifyV3Result(notifyData, signature).getResult();
- String outTradeNo = result.getOutTradeNo();
- String tradeState = result.getTradeState();
- log.info("微信V3回调解密成功,商户订单号:{},微信单号:{},交易状态:{}",
- outTradeNo, result.getTransactionId(), tradeState);
- // 2. 仅SUCCESS才处理订单
- if ("SUCCESS".equals(tradeState)) {
- // 2. 获取我们在下单时传入的 attach
- String attach = result.getAttach();
- String openid = result.getPayer().getOpenid();
- if (StrUtil.isEmpty(openid)) {
- log.error("openid不存在");
- resp.put("code", "FAIL");
- resp.put("message", "openid不存在");
- return resp;
- }
- log.info("支付成功用户openId,openId: {}", openid);
- TWxUser wxUser = wxUserService.getByOpenId(openid);
- if (ObjectUtil.isNull(wxUser)) {
- log.error("支付成功用户不存在,openId: {}", openid);
- resp.put("code", "FAIL");
- resp.put("message", "支付成功用户不存在");
- return resp;
- }
- // 3. 根据 attach 判断商品类型并进行不同的业务处理
- if (WxPayTypeEnum.GIFT_CARD.getCode().equals(attach)) {
- log.info("检测到购物卡支付成功,订单号: {}", outTradeNo);
- // 3.1 更新订单支付状态
- LambdaQueryWrapper<GiftCardOrder> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(GiftCardOrder::getOrderNo, outTradeNo);
- GiftCardOrder cardOrder = this.giftCardOrderService.getOne(wrapper);
- if (ObjectUtil.isNull(cardOrder)) {
- log.error("订单不存在,订单号: {}", outTradeNo);
- resp.put("code", "FAIL");
- resp.put("message", "订单不存在");
- return resp;
- }
- // 3.2 检查是否已处理
- if (ObjectUtil.equals(GiftCardOrderStatusEnum.PAID.getCode(), cardOrder.getStatus())) {
- log.warn("订单已处理过:{}", outTradeNo);
- resp.put("code", "SUCCESS");
- resp.put("message", "OK");
- return resp;
- }
- // 3.3 处理订单相关数据
- this.giftCardOrderService.processGiftCardPayment(result, wxUser, cardOrder);
- } else if (WxPayTypeEnum.EMOTION_GOODS.getCode().equals(attach)) {
- log.info("检测到情感服务商品支付成功,订单号: {}", outTradeNo);
- // 3.1 更新订单支付状态
- LambdaQueryWrapper<TOrder> wrapper = new LambdaQueryWrapper<>();
- wrapper.eq(TOrder::getOrderNo, outTradeNo);
- TOrder order = this.orderService.getOne(wrapper);
- if (ObjectUtil.isNull(order)) {
- log.error("订单不存在,订单号: {}", outTradeNo);
- resp.put("code", "FAIL");
- resp.put("message", "订单不存在");
- return resp;
- }
- // 3.2 检查是否已处理
- if (ObjectUtil.equals(OrderStatusEnum.PENDING_DISPATCH.getCode(), order.getStatus())) {
- log.warn("订单已处理过:{}", outTradeNo);
- resp.put("code", "SUCCESS");
- resp.put("message", "OK");
- return resp;
- }
- // 3.3 处理订单相关数据
- this.orderService.processOrderPayment(result, wxUser, order);
- }
- }
- //3. 返回成功
- resp.put("code", "SUCCESS");
- resp.put("message", "OK");
- } catch (WxPayException e) {
- log.error("微信支付回调异常:{}", e.getMessage(), e);
- resp.put("code", "FAIL");
- resp.put("message", e.getMessage());
- } catch (Exception e) {
- log.error("支付回调处理异常:{}", e.getMessage(), e);
- resp.put("code", "FAIL");
- resp.put("message", "系统异常");
- }
- return resp;
- }
- /**
- * 微信批量提现
- *
- * @param openId
- * @return String
- */
- @RequestMapping("/batchTransfer")
- @ApiOperation("微信批量提现")
- @ResponseBody
- public String batchTransfer(@RequestParam(value = "openId", required = false, defaultValue = "o-_-itxuXeGW3O1cxJ7FXNmq8Wf8") String openId) {
- try {
- BatchTransferModel batchTransferModel = new BatchTransferModel()
- .setAppid(wxPayProperties.getAppId())
- .setOut_batch_no(PayKit.generateStr())
- .setBatch_name("IJPay 测试微信转账到零钱")
- .setBatch_remark("IJPay 测试微信转账到零钱")
- .setTotal_amount(1)
- .setTotal_num(1)
- .setTransfer_detail_list(Collections.singletonList(
- new TransferDetailInput()
- .setOut_detail_no(PayKit.generateStr())
- .setTransfer_amount(1)
- .setTransfer_remark("IJPay 测试微信转账到零钱")
- .setOpenid(openId)));
- log.info("发起商家转账请求参数 {}", JSONUtil.toJsonStr(batchTransferModel));
- IJPayHttpResponse response = WxPayApi.v3(
- RequestMethodEnum.POST,
- WxDomainEnum.CHINA.toString(),
- TransferApiEnum.TRANSFER_BATCHES.toString(),
- wxPayProperties.getMchId(),
- getSerialNumber(),
- null,
- wxPayProperties.getCertKeyPath(),
- JSONUtil.toJsonStr(batchTransferModel)
- );
- log.info("发起商家转账响应 {}", response);
- // 根据证书序列号查询对应的证书来验证签名结果
- boolean verifySignature = WxPayKit.verifySignature(response, wxPayProperties.getPlatFormPath());
- log.info("verifySignature: {}", verifySignature);
- if (response.getStatus() == SUCCESS && verifySignature) {
- return response.getBody();
- }
- return JSONUtil.toJsonStr(response);
- } catch (Exception e) {
- log.error("系统异常", e);
- return e.getMessage();
- }
- }
- /**
- * 退款
- *
- * @param outRefundNo 退款订单号
- * @param amount 退款金额
- * @param transactionId 微信支付订单号
- * @param outTradeNo 商户订单号
- * @return String 退款结果
- */
- @RequestMapping("/refund")
- @ResponseBody
- public String refund(@RequestParam(required = false) String outRefundNo, @RequestParam(required = false) BigDecimal amount, @RequestParam(required = false) String transactionId, @RequestParam(required = false) String outTradeNo) {
- try {
- return rechargeService.refund(outRefundNo, transactionId, outTradeNo, amount);
- } catch (Exception e) {
- log.error("退款异常", e);
- throw new RuntimeException(e);
- }
- }
- /**
- * 微信退款回调接口
- *
- * @param request
- * @param response
- */
- @ApiOperation("微信退款回调接口")
- @RequestMapping(value = "/refundNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
- public void refundWechatCallback(HttpServletRequest request, HttpServletResponse response) {
- log.info("微信退款回调接口====================================>>>>微信退款回调接口");
- Map<String, String> map = new HashMap<>(12);
- try {
- String timestamp = request.getHeader("Wechatpay-Timestamp");
- String nonce = request.getHeader("Wechatpay-Nonce");
- String serialNo = request.getHeader("Wechatpay-Serial");
- String signature = request.getHeader("Wechatpay-Signature");
- log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
- String result = HttpKit.readData(request);
- log.info("退款通知密文 {}", result);
- // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
- String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
- wxPayProperties.getMchKey(), wxPayProperties.getPlatFormPath());
- log.info("退款通知明文 {}", plainText);
- if (StrUtil.isNotEmpty(plainText)) {
- response.setStatus(200);
- map.put("code", "SUCCESS");
- map.put("message", "SUCCESS");
- // 处理业务逻辑
- JSONObject jsonObject = new JSONObject(plainText);
- //退款单号
- String refundNo = jsonObject.get("out_refund_no").toString();
- refundVoucherService.refundWechatCallback(refundNo);
- } else {
- response.setStatus(500);
- map.put("code", "ERROR");
- map.put("message", "签名错误");
- }
- response.setHeader("Content-type", ContentType.JSON.toString());
- response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
- response.flushBuffer();
- } catch (Exception e) {
- log.error("系统异常", e);
- }
- }
- /**
- * 根据商户订单号查询微信支付订单
- *
- * @param outTradeNo 商户订单号
- * @return R 订单详情
- */
- @GetMapping("/query/order/{outTradeNo}")
- @ApiOperation("根据商户订单号查询微信支付订单")
- public R queryOrderByOutTradeNo(@PathVariable("outTradeNo") String outTradeNo) {
- try {
- log.info("查询微信支付订单,商户订单号:{}", outTradeNo);
- // V3 API:根据商户订单号查询订单接口路径
- // GET /v3/pay/transactions/out-trade-no/{out_trade_no}
- String queryUrl = String.format("/v3/pay/transactions/out-trade-no/%s", outTradeNo);
- log.info("查询订单URL:{}", queryUrl);
- Map<String, String> queryParams = new HashMap<>();
- queryParams.put("mchid", wxPayProperties.getMchId());
- // 调用微信支付V3接口查询订单
- IJPayHttpResponse response = WxPayApi.v3(
- RequestMethodEnum.GET,
- WxDomainEnum.CHINA.toString(),
- queryUrl,
- wxPayProperties.getMchId(),
- getSerialNumber(),
- null,
- wxPayProperties.getCertKeyPath(),
- queryParams
- );
- log.info("查询订单响应状态:{},响应体:{}", response.getStatus(), response.getBody());
- // 处理响应
- if (response.getStatus() == SUCCESS) {
- // 验证响应签名
- boolean verifySignature = WxPayKit.verifySignature(response, wxPayProperties.getPlatFormPath());
- log.info("响应签名验证结果:{}", verifySignature);
- if (verifySignature) {
- log.info("商户订单号 {} 查询成功,订单信息:{}", outTradeNo, response.getBody());
- JSONObject result = JSONUtil.parseObj(response.getBody());
- return R.ok(result);
- } else {
- log.error("商户订单号 {} 查询响应签名验证失败", outTradeNo);
- return R.fail("查询订单响应签名验证失败");
- }
- } else {
- log.error("商户订单号 {} 查询失败,状态码:{},响应:{}", outTradeNo, response.getStatus(), response.getBody());
- return R.fail("查询订单失败:" + response.getBody());
- }
- } catch (Exception e) {
- log.error("查询微信支付订单异常,商户订单号:{},错误:{}", outTradeNo, e.getMessage(), e);
- return R.fail("查询订单异常:" + e.getMessage());
- }
- }
- }
|