From da69a15cb98b980c3fa61020e1571d5d898188b7 Mon Sep 17 00:00:00 2001 From: yang <2119157836@qq.com> Date: Mon, 10 Feb 2025 18:44:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(payment):=20=E6=94=AF=E4=BB=98=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E8=B0=83=E6=95=B4=E5=92=8C=E8=AE=A2=E5=8D=95=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pay/AliPay/AliPayController.java | 17 ++- .../pay/AliPay/OrderTradeController.java | 23 +++- .../src/main/resources/application-druid.yml | 2 +- .../com/mcwl/pay/domain/OrderTradeDto.java | 2 +- .../com/mcwl/pay/service/AliPayService.java | 3 + .../pay/service/impl/AliPayServiceImpl.java | 114 +++++++++++++++--- .../service/impl/OrderTradeServiceImpl.java | 4 + 7 files changed, 135 insertions(+), 30 deletions(-) diff --git a/mcwl-admin/src/main/java/com/mcwl/web/controller/pay/AliPay/AliPayController.java b/mcwl-admin/src/main/java/com/mcwl/web/controller/pay/AliPay/AliPayController.java index cd300fd..aaffb6a 100644 --- a/mcwl-admin/src/main/java/com/mcwl/web/controller/pay/AliPay/AliPayController.java +++ b/mcwl-admin/src/main/java/com/mcwl/web/controller/pay/AliPay/AliPayController.java @@ -74,13 +74,13 @@ public class AliPayController extends BaseController { String appId = "2021005114616085"; String state = ShareCodeUtils.idToCode(SecurityUtils.getUserId()); // 防止CSRF攻击 - String encodedRedirectUri = URLEncoder.encode("https://3195d9a3.r27.cpolar.top/ali/pay/callback", "UTF-8"); + String encodedRedirectUri = URLEncoder.encode("https://4b0ca615.r27.cpolar.top/ali/pay/callback", "UTF-8"); String authUrl = String.format( "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=%s&scope=%s&redirect_uri=%s&state=%s", appId, scope, encodedRedirectUri, state ); - QrCodeUtil.generate(authUrl, 300, 300, "png", response.getOutputStream()); +// QrCodeUtil.generate(authUrl, 300, 300, "png", response.getOutputStream()); return AjaxResult.success("成功", authUrl); } @@ -136,8 +136,10 @@ public class AliPayController extends BaseController { return AjaxResult.error("商品id不能为空"); } payVo = aliPayService.memberPay(orderTradeDto); +// QrCodeUtil.generate(payVo.getUrl(), 300, 300, "png", response.getOutputStream()); } else if ("points".equalsIgnoreCase(type)) { - payVo = aliPayService.pointsPay(orderTradeDto.getPaymentAmount()); + payVo = aliPayService.pointsPay(orderTradeDto.getAmount()); +// QrCodeUtil.generate(payVo.getUrl(), 300, 300, "png", response.getOutputStream()); } return AjaxResult.success(payVo); @@ -198,10 +200,15 @@ public class AliPayController extends BaseController { // 验签通过 String code = params.get("out_trade_no"); // 商户订单号 + OrderTrade orderTrade = orderTradeService.lambdaQuery() + .eq(OrderTrade::getCode, code) + .one(); + if (orderTrade == null) { + redisCache.deleteObject(code); + return "failure"; + } // 获取订单后缀 String suffix = code.substring(code.lastIndexOf("_") + 1); - String orderTradeJson = redisCache.getCacheObject(code); - OrderTrade orderTrade = JSONUtil.toBean(orderTradeJson, OrderTrade.class); orderTradeService.orderHandler(orderTrade, suffix, params); diff --git a/mcwl-admin/src/main/java/com/mcwl/web/controller/pay/AliPay/OrderTradeController.java b/mcwl-admin/src/main/java/com/mcwl/web/controller/pay/AliPay/OrderTradeController.java index 03392a9..dd53af9 100644 --- a/mcwl-admin/src/main/java/com/mcwl/web/controller/pay/AliPay/OrderTradeController.java +++ b/mcwl-admin/src/main/java/com/mcwl/web/controller/pay/AliPay/OrderTradeController.java @@ -5,6 +5,7 @@ import cn.hutool.core.util.RandomUtil; import cn.hutool.extra.qrcode.QrCodeUtil; import cn.hutool.json.JSON; import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSONObject; import com.alipay.api.AlipayApiException; import com.alipay.api.request.AlipaySystemOauthTokenRequest; import com.alipay.easysdk.base.oauth.models.AlipaySystemOauthTokenResponse; @@ -36,10 +37,8 @@ import javax.validation.constraints.NotBlank; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; +import java.util.*; +import java.util.concurrent.TimeUnit; /** * @Author:ChenYan @@ -62,6 +61,12 @@ public class OrderTradeController extends BaseController { @Autowired private OrderTradeService orderTradeService; + @Autowired + private AliPayService aliPayService; + + @Autowired + private RedisCache redisCache; + /** * 查询列表 */ @@ -121,7 +126,14 @@ public class OrderTradeController extends BaseController { */ @GetMapping("/queryTradeStatus") @ApiOperation(value = "查询交易状态") - public Object queryTradeStatus(@Valid @NotBlank(message = "订单号不能为空") String outTradeNo) throws Exception { + public AjaxResult queryTradeStatus(@Valid @NotBlank(message = "订单号不能为空") String outTradeNo) throws Exception { + return aliPayService.queryTradeStatus(outTradeNo); + + + } + + @GetMapping("/getTradStatus") + public Object getTradStatus(String outTradeNo) throws Exception { Factory.setOptions(config); AlipayTradeQueryResponse query = Factory.Payment.Common().query(outTradeNo); Map map = JSONUtils.jsonToMap(query.getHttpBody()); @@ -132,7 +144,6 @@ public class OrderTradeController extends BaseController { // TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款) // TRADE_SUCCESS(交易支付成功) // TRADE_FINISHED(交易结束,不可退款) - // 当 trade_status 等于 TRADE_SUCCESS 或 TRADE_FINISHED 时, 表示支付成功 return map.get("alipay_trade_query_response"); } diff --git a/mcwl-admin/src/main/resources/application-druid.yml b/mcwl-admin/src/main/resources/application-druid.yml index 0161927..b159a11 100644 --- a/mcwl-admin/src/main/resources/application-druid.yml +++ b/mcwl-admin/src/main/resources/application-druid.yml @@ -128,7 +128,7 @@ mall: privateKey: MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCyhwMKnP7oBQ9qKhxke9AXN7ZNcniMnJFcWWFPfs3W6QqKBGFGFYQFyqfmnf2gM4nYtEbvit0CcmSrdniR3aZVb6s0A/KWqD5ejf+zjdnn5XH4GYsm/DXZoBqdAMWQUaQcVB1lC9az1rn13n9UHBJ+s+UAWtFjXMDd78dsE7enU3RjBffyvl/A+r6c0jnSHDNttSXyamxGX8ng8hK5XdCAusOfYpWnLY90sjlyz98snIfVPqAfD3KTP88gRF1KfWZniwMCR/OjCp6jm5d5dvOj8xyk7R1LfZ0CH/fXOcvpCe9JISS9DyzEzW5XpsnO5p8Mo8wFL6TKVBng+Ss0Tr0NAgMBAAECggEAPnaCl+oKPf/XPtvxBy9SxWzWug49I2V8boVPSNMjDfiHmXai2jSY9rXLPSM2JCEkL9ATqag6X6rKkjtnQBH/KpFho1dmP1QqTkb8mkQdY8ehsp6tUzyXUsdzyE8G0NHfy5Gan0ofGtkcQ2xPvIv95JromtErxcokKQLGEYhsj3xL+KVsUh5YedX/6sAiJ1KS5Zs82eh6bbCvUzuSPc4hjyfLSux5AnGBPnwEzWWJiA5z9zK17i8Cb7FnEeRmNvJw7VsXaCMFquZ6+bzlKdSuUqzqNhHMX8AivePTjj7pPHFW3FTh64E20yzl7CIwkScToXT3H7oaHRA1xGySmGNBuQKBgQDXFcg0b8L0A/Zril+4IwfckH1gpUbqlplzfozM/a/0rojQEdS+X7g4ypdPc47cwoLJx08/84L8ZPj49CImoGNdj5K9B2fY/E10SIJJqN5lb9NI4KINvGGM58NDSAl+GjT5mljkPI8jzQ1ACcy9DGV/KVScGHUWUpeLOK22AEcfWwKBgQDUfPFBHc2Bkuv3bRoNT+fnChMw5CI65wD74vR5GKyurh50DB3RZXwhRsAFd9NowINm9B2KAR0IrB+vh3EwfJaWDauh3ykZZtrAphSa11dJXvtathHTuQmXalZv5brh5mHie87yoxMn7vqDi0bwkNlb/b1wwokwc0oc3NSoNsFptwKBgCJhjPs00kkHSSCvodVhZCRRxHjqcoeVQzo/BGrQMow+SghYkVVNH7mUSNOeeTu4rAdtILHsTwfhbBr/i2X5RWjzRlWJFYSN8COiY5E6uTIlyfRgVAc2EbHgakoa/cl2jvlHPuZc99SVNoUCDAQ8Mw08ohXBoxXKZgcPLFVnffb5AoGBAJW5cDmk6WCrCRAwVVIaAtsek0U5CxGcImpakk8d3te7IwQ8YWph6yR7eOSheVrvg+ewjXvnqvCocC877brvnyAeg9isqYYXnn5uyEgPApdVkSFq94bLdIG30S5JnvR8ZG61NK5t0BqsXuL/6b2rIy2Zzrm8qfR02D0cMdBRvNljAoGBAMJG7/TqodrYPYyOZ4/IbbhAitBHWpHYJHSENnuxbQGAQuCt52Jp3eULR2r7uk3dUDuE8mB2QAEStuV/orfdxxRxJIYbEynJFGCa9O5LTevbM4MoNRfLCPPmFK5PJivlJcdrCyQKEmGSLLrVdb3H/Wzu0/4K/ZhwyrtBz4MRkuwD # 沙箱支付宝公钥 publicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgfv3CE8I87I5HHcMtK1RrPQZzLqYlXxRa96lIYkEyFsxdMcMSwnNJJf/mceZhMQnXQDkuEYTZPO/DJbmiZkBWbRZNfIJur5Af/wKZEmHahtEWlrdJdJ6fh7j11eeqbXBfo55H1dxPEQA/obfe85WrqoE0fa06XElgq5/RwsSKjZpnorbO8mMnDaRwxIOoRVHQlHgfkMtTHfP+Zw8Kv0tyJdWLxVDgtg0OoO8VnrDNGBwsxJDOvLjZBbp9NX3wiWQpfh4OBkN1KKfIA9yfBhS6N0bnWfr428Boz/qNhycdrG/MYGQDiG8hVt177fKvTVXRf/kdSIz3izN1GnzOUkadQIDAQAB - notifyUrl: https://253d7236.r27.cpolar.top/ali/pay/notify + notifyUrl: https://4b0ca615.r27.cpolar.top/ali/pay/notify # 沙箱支付宝网关 gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do diff --git a/mcwl-pay/src/main/java/com/mcwl/pay/domain/OrderTradeDto.java b/mcwl-pay/src/main/java/com/mcwl/pay/domain/OrderTradeDto.java index b0695ae..07b979b 100644 --- a/mcwl-pay/src/main/java/com/mcwl/pay/domain/OrderTradeDto.java +++ b/mcwl-pay/src/main/java/com/mcwl/pay/domain/OrderTradeDto.java @@ -22,7 +22,7 @@ public class OrderTradeDto { */ @ApiModelProperty(value = "支付金额", required = true) @NotNull(message = "支付金额不能为空") - private Double paymentAmount; + private Double amount; /** * 订单类型 会员member 积分points diff --git a/mcwl-pay/src/main/java/com/mcwl/pay/service/AliPayService.java b/mcwl-pay/src/main/java/com/mcwl/pay/service/AliPayService.java index 315ac9c..14f38b1 100644 --- a/mcwl-pay/src/main/java/com/mcwl/pay/service/AliPayService.java +++ b/mcwl-pay/src/main/java/com/mcwl/pay/service/AliPayService.java @@ -14,5 +14,8 @@ public interface AliPayService { AjaxResult fetch(String outBizNo, String amount) throws AlipayApiException; + // 查询订单状态 + AjaxResult queryTradeStatus(String outTradeNo) throws Exception; + String balance() throws AlipayApiException; } diff --git a/mcwl-pay/src/main/java/com/mcwl/pay/service/impl/AliPayServiceImpl.java b/mcwl-pay/src/main/java/com/mcwl/pay/service/impl/AliPayServiceImpl.java index 472394a..5b66e41 100644 --- a/mcwl-pay/src/main/java/com/mcwl/pay/service/impl/AliPayServiceImpl.java +++ b/mcwl-pay/src/main/java/com/mcwl/pay/service/impl/AliPayServiceImpl.java @@ -2,6 +2,8 @@ package com.mcwl.pay.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateField; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.UUID; import cn.hutool.extra.mail.MailUtil; import cn.hutool.json.JSONUtil; @@ -21,13 +23,17 @@ import com.alipay.api.response.AlipayFundAccountQueryResponse; import com.alipay.api.response.AlipayFundTransUniTransferResponse; import com.alipay.easysdk.factory.Factory; import com.alipay.easysdk.kernel.Config; +import com.alipay.easysdk.payment.common.models.AlipayTradeCloseResponse; +import com.alipay.easysdk.payment.common.models.AlipayTradeQueryResponse; import com.alipay.easysdk.payment.facetoface.models.AlipayTradePrecreateResponse; +import com.mcwl.common.JSONUtils; import com.mcwl.common.core.domain.AjaxResult; import com.mcwl.common.core.domain.entity.SysUser; import com.mcwl.common.core.redis.RedisCache; import com.mcwl.common.exception.ServiceException; import com.mcwl.common.utils.SecurityUtils; import com.mcwl.common.utils.ShareCodeUtils; +import com.mcwl.common.utils.StringUtils; import com.mcwl.memberCenter.domain.MemberLevel; import com.mcwl.memberCenter.service.MemberLevelService; import com.mcwl.pay.config.AliConfig; @@ -47,10 +53,7 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Date; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.concurrent.TimeUnit; /** @@ -60,6 +63,9 @@ import java.util.concurrent.TimeUnit; @Slf4j public class AliPayServiceImpl implements AliPayService { + @Autowired + private Config config; + @Autowired private RedisCache redisCache; @@ -156,25 +162,24 @@ public class AliPayServiceImpl implements AliPayService { // 设置orderTrade信息 OrderTrade tradeEntity = new OrderTrade(); BeanUtil.copyProperties(orderTradeDto, tradeEntity); - tradeEntity.setCode(UUID.randomUUID().toString(true).substring(0, 30)); + tradeEntity.setCode(UUID.randomUUID().toString(true).substring(0, 30) + "_member"); tradeEntity.setUserId(SecurityUtils.getUserId()); tradeEntity.setUserName(SecurityUtils.getUsername()); tradeEntity.setProductId(productId); tradeEntity.setProductName(memberLevel.getMemberName()); - tradeEntity.setTotalAmount(orderTradeDto.getPaymentAmount()); - tradeEntity.setOrderStatus(3); - tradeEntity.setPayStatus(2); + tradeEntity.setTotalAmount(orderTradeDto.getAmount()); + tradeEntity.setOrderStatus(1); + tradeEntity.setPayStatus(1); orderTradeService.save(tradeEntity); //调用支付宝的接口 AlipayTradePrecreateResponse payResponse = Factory.Payment.FaceToFace() - // 设置过期时 + // 设置过期时间 .preCreate(memberLevel.getMemberName(), - tradeEntity.getCode() + "_member", - orderTradeDto.getPaymentAmount().toString()); + tradeEntity.getCode(), + orderTradeDto.getAmount().toString()); // 缓存到redis - redisCache.setCacheObject(tradeEntity.getCode() + "_member", JSONUtil.toJsonStr(tradeEntity), 3, TimeUnit.MINUTES); - redisCache.setCacheObject(tradeEntity.getCode() + "_member" + "_promotionId", orderTradeDto.getPromotionId(), 3, TimeUnit.MINUTES); + redisCache.setCacheObject(tradeEntity.getCode() + "_promotionId", orderTradeDto.getPromotionId()); // AlipayTradePrecreateResponse payResponse = Factory.Payment.FaceToFace().preCreate("订单主题:Mac笔记本", "LS123qwe123", "19999"); //参照官方文档响应示例,解析返回结果 String httpBodyStr = payResponse.getHttpBody(); @@ -198,7 +203,7 @@ public class AliPayServiceImpl implements AliPayService { public PayVo pointsPay(Double paymentAmount) throws Exception { // 设置orderTrade信息 OrderTrade tradeEntity = new OrderTrade(); - tradeEntity.setCode(UUID.randomUUID().toString(true).substring(0, 30)); + tradeEntity.setCode(UUID.randomUUID().toString(true).substring(0, 30) + "_points"); tradeEntity.setUserId(SecurityUtils.getUserId()); tradeEntity.setProductId(-1); tradeEntity.setProductName("积分充值"); @@ -206,14 +211,14 @@ public class AliPayServiceImpl implements AliPayService { tradeEntity.setTotalAmount(paymentAmount); tradeEntity.setOrderStatus(1); tradeEntity.setPayStatus(1); - + orderTradeService.save(tradeEntity); //调用支付宝的接口 AlipayTradePrecreateResponse payResponse = Factory.Payment.FaceToFace() .preCreate(tradeEntity.getProductName(), - tradeEntity.getCode() + "_points", + tradeEntity.getCode(), tradeEntity.getPaymentAmount().toString()); // 缓存到redis - redisCache.setCacheObject(tradeEntity.getCode() + "_points", JSONUtil.toJsonStr(tradeEntity), 3, TimeUnit.MINUTES); + redisCache.setCacheObject(tradeEntity.getCode(), JSONUtil.toJsonStr(tradeEntity), 30, TimeUnit.SECONDS); //参照官方文档响应示例,解析返回结果 String httpBodyStr = payResponse.getHttpBody(); JSONObject jsonObject = JSONObject.parseObject(httpBodyStr); @@ -310,6 +315,67 @@ public class AliPayServiceImpl implements AliPayService { throw new ServiceException("转账失败:" + response.getSubMsg()); } + @Override + public AjaxResult queryTradeStatus(String outTradeNo) throws Exception { + + // 查询redis中订单信息 + String orderTradeJson = redisCache.getCacheObject(outTradeNo); + OrderTrade orderTrade = JSONUtil.toBean(orderTradeJson, OrderTrade.class); + + // 如果redis中存在该订单, 则直接返回该订单的支付状态 + if (StringUtils.isNotBlank(orderTradeJson) && Objects.nonNull(orderTrade)) { + return AjaxResult.success("查询成功", orderTrade.getPayStatus()); + } + + // 查询支付宝订单 + orderTrade = orderTradeService.lambdaQuery() + .eq(OrderTrade::getCode, outTradeNo) + .one(); + + // 如果数据库中不存在该订单, 则返回订单不存在 + if (Objects.isNull(orderTrade)) { + return AjaxResult.error("订单不存在"); + } + + + if (orderTrade.getPayStatus() == 1) { + // 查询支付宝订单 + Object tradStatus = this.getTradStatus(outTradeNo); + String jsonString = JSONObject.toJSONString(tradStatus); + JSONObject jsonObject = JSONObject.parseObject(jsonString); + Object tradeStatus = jsonObject.get("trade_status"); + if (tradeStatus != null && tradeStatus.equals("TRADE_SUCCESS")) { + Map params = new HashMap<>(); + params.put("buyer_pay_amount", jsonObject.get("buyer_pay_amount").toString()); + params.put("out_trade_no", jsonObject.get("out_trade_no").toString()); + params.put("gmt_payment", jsonObject.get("send_pay_date").toString()); + params.put("total_amount", jsonObject.get("total_amount").toString()); + params.put("trade_no", jsonObject.get("trade_no").toString()); + String code = orderTrade.getCode(); + // 获取订单后缀 + String suffix = code.substring(code.lastIndexOf("_") + 1); + orderTradeService.orderHandler(orderTrade, suffix, params); + return AjaxResult.success("查询成功", orderTrade.getPayStatus()); + } + long expire = redisCache.getExpire(outTradeNo); + // expire 超过15分钟,则删除redis中订单信息 + if (expire > 900) { + // 关闭交易 + AlipayTradeCloseResponse close = Factory.Payment.Common().close(outTradeNo); + if (close.getCode().equals("10000")) { + orderTrade.setOrderStatus(4); + orderTradeService.updateById(orderTrade); + redisCache.deleteObject(outTradeNo + "promotionId"); + } + } + return AjaxResult.success("查询成功", orderTrade.getPayStatus()); + } + + redisCache.setCacheObject(outTradeNo, JSONUtil.toJsonStr(orderTrade), 5, TimeUnit.SECONDS); + return AjaxResult.success("查询成功", orderTrade.getPayStatus()); + } + + @Override public String balance() throws AlipayApiException { // 初始化SDK @@ -380,6 +446,20 @@ public class AliPayServiceImpl implements AliPayService { } + private Object getTradStatus(String outTradeNo) throws Exception { + Factory.setOptions(config); + AlipayTradeQueryResponse query = Factory.Payment.Common().query(outTradeNo); + Map map = JSONUtils.jsonToMap(query.getHttpBody()); + + // 返回交易结果, 是否交易成功需要根据该对象中的 trade_status 来确定 + // trade_status 的枚举值如下, 请见 https://opendocs.alipay.com/apis/api_1/alipay.trade.query + // WAIT_BUYER_PAY(交易创建,等待买家付款) + // TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款) + // TRADE_SUCCESS(交易支付成功) + // TRADE_FINISHED(交易结束,不可退款) + // 当 trade_status 等于 TRADE_SUCCESS 或 TRADE_FINISHED 时, 表示支付成功 + return map.get("alipay_trade_query_response"); + } private void addLogAndUpdateUserWallet(SysUser sysUser, String amount) { BigDecimal wallet = new BigDecimal(sysUser.getWallet().toString()); diff --git a/mcwl-pay/src/main/java/com/mcwl/pay/service/impl/OrderTradeServiceImpl.java b/mcwl-pay/src/main/java/com/mcwl/pay/service/impl/OrderTradeServiceImpl.java index 9c107f5..b518dd9 100644 --- a/mcwl-pay/src/main/java/com/mcwl/pay/service/impl/OrderTradeServiceImpl.java +++ b/mcwl-pay/src/main/java/com/mcwl/pay/service/impl/OrderTradeServiceImpl.java @@ -133,6 +133,10 @@ public class OrderTradeServiceImpl extends ServiceImpl params) {