diff --git a/nla-common/src/main/java/cn/nla/common/config/WebConfig.java b/nla-common/src/main/java/cn/nla/common/config/WebConfig.java index b7e3d2f..a23688c 100644 --- a/nla-common/src/main/java/cn/nla/common/config/WebConfig.java +++ b/nla-common/src/main/java/cn/nla/common/config/WebConfig.java @@ -31,6 +31,8 @@ public class WebConfig implements WebMvcConfigurer { "/user/**/login", "/user/**/uploadFile", "/cop/coupon/v1/new_user_coupon", + "/odr/product/v1/test_pay", + "/odr/callback/v1/**", "/odr/product/v1/query_state"); } diff --git a/nla-common/src/main/java/cn/nla/common/constant/TimeConstant.java b/nla-common/src/main/java/cn/nla/common/constant/TimeConstant.java new file mode 100644 index 0000000..d6e5c50 --- /dev/null +++ b/nla-common/src/main/java/cn/nla/common/constant/TimeConstant.java @@ -0,0 +1,14 @@ +package cn.nla.common.constant; + +public class TimeConstant { + + /** + * + * 支付订单的有效时长,超过未支付则关闭订单 + * + * 订单超时,毫秒,默认30分钟 + */ + //public static final long ORDER_PAY_TIMEOUT_MILLS = 30*60*1000; + public static final long ORDER_PAY_TIMEOUT_MILLS = 5*60*1000; + +} diff --git a/nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponController.java b/nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponController.java index adce067..ad308cf 100644 --- a/nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponController.java +++ b/nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponController.java @@ -40,7 +40,7 @@ public class CouponController { @ApiOperation("领取优惠券") @GetMapping("/add/promotion/{coupon_id}") public JsonData addPromotionCoupon(@ApiParam(value = "优惠券id", required = true) @PathVariable("coupon_id") long couponId) { - return couponService.addCoupon(couponId, CouponCategoryEnum.PROMOTION); + return couponService.addCoupon(couponId, CouponCategoryEnum.NEW_USER); } /** * 新用户注册发放优惠券接口 diff --git a/nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponRecordController.java b/nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponRecordController.java index 1de7842..19d850f 100644 --- a/nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponRecordController.java +++ b/nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponRecordController.java @@ -23,7 +23,7 @@ import java.util.Map; */ @Api(tags = "优惠券领劵记录控制器") @RestController -@RequestMapping("/cop/couponRecord/v1") +@RequestMapping("/cop/coupon_record/v1") public class CouponRecordController { @Resource diff --git a/nla-coupon-service/src/main/java/cn/nla/coupon/service/impl/CouponServiceImpl.java b/nla-coupon-service/src/main/java/cn/nla/coupon/service/impl/CouponServiceImpl.java index 23c7c4f..a41e497 100644 --- a/nla-coupon-service/src/main/java/cn/nla/coupon/service/impl/CouponServiceImpl.java +++ b/nla-coupon-service/src/main/java/cn/nla/coupon/service/impl/CouponServiceImpl.java @@ -87,6 +87,7 @@ public class CouponServiceImpl extends ServiceImpl i CouponEntity coupon = baseMapper.selectOne( Wrappers.lambdaQuery() .eq(CouponEntity::getId, couponId).eq(CouponEntity::getCategory, category.name())); + log.info("参数:{},{}",couponId,category.name()); //优惠券是否可以领取 this.checkCoupon(coupon, loginUser.getId()); //构建领劵记录 diff --git a/nla-order-service/pom.xml b/nla-order-service/pom.xml index 39c7701..af7b3d0 100644 --- a/nla-order-service/pom.xml +++ b/nla-order-service/pom.xml @@ -25,5 +25,10 @@ org.projectlombok lombok + + com.alipay.sdk + alipay-sdk-java + 4.10.218.ALL + diff --git a/nla-order-service/src/main/java/cn/nla/order/component/AlipayStrategy.java b/nla-order-service/src/main/java/cn/nla/order/component/AlipayStrategy.java new file mode 100644 index 0000000..8b45e94 --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/component/AlipayStrategy.java @@ -0,0 +1,128 @@ +package cn.nla.order.component; + +import cn.nla.common.enums.BizCodeEnum; +import cn.nla.common.enums.ClientType; +import cn.nla.common.exception.BizException; +import cn.nla.order.config.AlipayConfig; +import cn.nla.order.config.PayUrlConfig; +import cn.nla.order.model.VO.PayInfoVO; +import com.alibaba.fastjson.JSON; +import com.alipay.api.AlipayApiException; +import com.alipay.api.request.AlipayTradePagePayRequest; +import com.alipay.api.request.AlipayTradeQueryRequest; +import com.alipay.api.request.AlipayTradeWapPayRequest; +import com.alipay.api.response.AlipayTradePagePayResponse; +import com.alipay.api.response.AlipayTradeQueryResponse; +import com.alipay.api.response.AlipayTradeWapPayResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; + + +@Slf4j +@Service +public class AlipayStrategy implements PayStrategy { + @Autowired + private PayUrlConfig payUrlConfig; + + @Override + public String unifiedorder(PayInfoVO payInfoVO) { + HashMap content = new HashMap<>(); + //商户订单号,64个字符以内、可包含字母、数字、下划线;需保证在商户端不重复 + content.put("out_trade_no", payInfoVO.getOutTradeNo()); + content.put("product_code", "FAST_INSTANT_TRADE_PAY"); + //订单总金额,单位为元,精确到小数点后两位 + content.put("total_amount", payInfoVO.getPayFee().toString()); + //商品标题/交易标题/订单标题/订单关键字等。 注意:不可使用特殊字符,如 /,=,& 等。 + content.put("subject", payInfoVO.getTitle()); + //商品描述,可空 + content.put("body", payInfoVO.getDescription()); + double timeout = Math.floor(payInfoVO.getOrderPayTimeoutMills() / (1000 * 60)); + //前端也需要判断订单是否要关闭了, 如果要快要到期则不给二次支付 + if (timeout < 1) { + throw new BizException(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT); + } + // 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 + // 该参数数值不接受小数点, 如 1.5h,可转换为 90m。 + content.put("timeout_express", Double.valueOf(timeout).intValue() + "m"); + String clientType = payInfoVO.getClientType(); + String form = ""; + try { + if (clientType.equalsIgnoreCase(ClientType.H5.name())) { + //H5手机网页支付 + AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest(); + request.setBizContent(JSON.toJSONString(content)); + request.setNotifyUrl(payUrlConfig.getAlipayCallbackUrl()); + request.setReturnUrl(payUrlConfig.getAlipaySuccessReturnUrl()); + AlipayTradeWapPayResponse alipayResponse = AlipayConfig.getInstance().pageExecute(request); + log.info("响应日志:alipayResponse={}", alipayResponse); + if (alipayResponse.isSuccess()) { + form = alipayResponse.getBody(); + } else { + log.error("支付宝构建H5表单失败:alipayResponse={},payInfo={}", alipayResponse, payInfoVO); + } + } else if (clientType.equalsIgnoreCase(ClientType.PC.name())) { + //PC支付 + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); + request.setBizContent(JSON.toJSONString(content)); + request.setNotifyUrl(payUrlConfig.getAlipayCallbackUrl()); + request.setReturnUrl(payUrlConfig.getAlipaySuccessReturnUrl()); + AlipayTradePagePayResponse alipayResponse = AlipayConfig.getInstance().pageExecute(request); + log.info("响应日志:alipayResponse={}", alipayResponse); + if (alipayResponse.isSuccess()) { + form = alipayResponse.getBody(); + } else { + log.error("支付宝构建PC表单失败:alipayResponse={},payInfo={}", alipayResponse, payInfoVO); + } + } + } catch (AlipayApiException e) { + log.error("支付宝构建表单异常:payInfo={},异常={}", payInfoVO, e); + } + return form; + } + + @Override + public String refund(PayInfoVO payInfoVO) { + return null; + } + + /** + * 查询订单状态 + * 支付成功 返回非空 + * 其他返回空 + *

+ * 未支付 + * {"alipay_trade_query_response":{"code":"40004","msg":"Business Failed","sub_code":"ACQ.TRADE_NOT_EXIST","sub_msg":"交易不存在","buyer_pay_amount":"0.00","invoice_amount":"0.00","out_trade_no":"adbe8e8f-3b18-4c9e-b736-02c4c2e15eca","point_amount":"0.00","receipt_amount":"0.00"},"sign":"xxxxx"} + *

+ * 已经支付 + * {"alipay_trade_query_response":{"code":"10000","msg":"Success","buyer_logon_id":"mqv***@sandbox.com","buyer_pay_amount":"0.00","buyer_user_id":"2088102176996700","buyer_user_type":"PRIVATE","invoice_amount":"0.00","out_trade_no":"adbe8e8f-3b18-4c9e-b736-02c4c2e15eca","point_amount":"0.00","receipt_amount":"0.00","send_pay_date":"2020-12-04 17:06:47","total_amount":"111.99","trade_no":"2020120422001496700501648498","trade_status":"TRADE_SUCCESS"},"sign":"xxxx"} + * + * @param payInfoVO + * @return + */ + @Override + public String queryPaySuccess(PayInfoVO payInfoVO) { + AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); + HashMap content = new HashMap<>(); + //订单商户号,64位 + content.put("out_trade_no", payInfoVO.getOutTradeNo()); + request.setBizContent(JSON.toJSONString(content)); + AlipayTradeQueryResponse response = null; + try { + response = AlipayConfig.getInstance().execute(request); + log.info("支付宝订单查询响应:{}", response.getBody()); + + } catch (AlipayApiException e) { + log.error("支付宝订单查询异常:{}", e); + } + if (response.isSuccess()) { + log.info("支付宝订单状态查询成功:{}", payInfoVO); + return response.getTradeStatus(); + } else { + log.info("支付宝订单状态查询失败:{}", payInfoVO); + return ""; + } + } +} diff --git a/nla-order-service/src/main/java/cn/nla/order/component/PayFactory.java b/nla-order-service/src/main/java/cn/nla/order/component/PayFactory.java new file mode 100644 index 0000000..1f0bf52 --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/component/PayFactory.java @@ -0,0 +1,55 @@ +package cn.nla.order.component; + +import cn.nla.common.enums.ProductOrderPayTypeEnum; +import cn.nla.order.model.VO.PayInfoVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class PayFactory { + @Autowired + private AlipayStrategy alipayStrategy; + @Autowired + private WechatPayStrategy wechatPayStrategy; + /** + * 创建支付,简单工程模式 + * @param payInfoVO + * @return + */ + public String pay(PayInfoVO payInfoVO){ + String payType = payInfoVO.getPayType(); + if(ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType)){ + //支付宝支付 + PayStrategyContext payStrategyContext = new PayStrategyContext(alipayStrategy); + return payStrategyContext.executeUnifiedorder(payInfoVO); + } else if(ProductOrderPayTypeEnum.WECHAT.name().equalsIgnoreCase(payType)){ + //微信支付 暂未实现 + PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy); + return payStrategyContext.executeUnifiedorder(payInfoVO); + } + return ""; + } + /** + * 查询订单支付状态 + * + * 支付成功返回非空,其他返回空 + * + * @param payInfoVO + * @return + */ + public String queryPaySuccess(PayInfoVO payInfoVO){ + String payType = payInfoVO.getPayType(); + if(ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType)){ + //支付宝支付 + PayStrategyContext payStrategyContext = new PayStrategyContext(alipayStrategy); + return payStrategyContext.executeQueryPaySuccess(payInfoVO); + } else if(ProductOrderPayTypeEnum.WECHAT.name().equalsIgnoreCase(payType)){ + //微信支付 暂未实现 + PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy); + return payStrategyContext.executeQueryPaySuccess(payInfoVO); + } + return ""; + } +} diff --git a/nla-order-service/src/main/java/cn/nla/order/component/PayStrategy.java b/nla-order-service/src/main/java/cn/nla/order/component/PayStrategy.java new file mode 100644 index 0000000..e6c7448 --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/component/PayStrategy.java @@ -0,0 +1,18 @@ +package cn.nla.order.component; + +import cn.nla.order.model.VO.PayInfoVO; + +public interface PayStrategy { + /** + * 下单 + */ + String unifiedorder(PayInfoVO payInfoVO); + /** + * 退款 + */ + default String refund(PayInfoVO payInfoVO){return "";} + /** + * 查询支付是否成功 + */ + default String queryPaySuccess(PayInfoVO payInfoVO){return "";} +} diff --git a/nla-order-service/src/main/java/cn/nla/order/component/PayStrategyContext.java b/nla-order-service/src/main/java/cn/nla/order/component/PayStrategyContext.java new file mode 100644 index 0000000..abc074c --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/component/PayStrategyContext.java @@ -0,0 +1,22 @@ +package cn.nla.order.component; +import cn.nla.order.model.VO.PayInfoVO; + +public class PayStrategyContext { + private PayStrategy payStrategy; + public PayStrategyContext(PayStrategy payStrategy){ + this.payStrategy = payStrategy; + } + /** + * 根据支付策略,调用不同的支付 + */ + public String executeUnifiedorder(PayInfoVO payInfoVO){ + return this.payStrategy.unifiedorder(payInfoVO); + } + + /** + * 根据支付的策略,调用不同的查询订单支持状态 + */ + public String executeQueryPaySuccess(PayInfoVO payInfoVO){ + return this.payStrategy.queryPaySuccess(payInfoVO); + } +} diff --git a/nla-order-service/src/main/java/cn/nla/order/component/WechatPayStrategy.java b/nla-order-service/src/main/java/cn/nla/order/component/WechatPayStrategy.java new file mode 100644 index 0000000..d7e2af0 --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/component/WechatPayStrategy.java @@ -0,0 +1,22 @@ +package cn.nla.order.component; + +import cn.nla.order.model.VO.PayInfoVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class WechatPayStrategy implements PayStrategy { + @Override + public String unifiedorder(PayInfoVO payInfoVO) { + return null; + } + @Override + public String refund(PayInfoVO payInfoVO) { + return null; + } + @Override + public String queryPaySuccess(PayInfoVO payInfoVO) { + return null; + } +} diff --git a/nla-order-service/src/main/java/cn/nla/order/config/AlipayConfig.java b/nla-order-service/src/main/java/cn/nla/order/config/AlipayConfig.java new file mode 100644 index 0000000..abe8e9e --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/config/AlipayConfig.java @@ -0,0 +1,74 @@ +package cn.nla.order.config; + +import com.alipay.api.AlipayClient; +import com.alipay.api.DefaultAlipayClient; + +public class AlipayConfig { + + /** + * 支付宝网关地址 + */ + public static final String PAY_GATEWAY = "https://openapi-sandbox.dl.alipaydev.com/gateway.do"; + + + /** + * 支付宝 APPID + */ + public static final String APPID = "9021000140670744"; + + /** + * 应用私钥 + */ + public static final String APP_PRI_KEY = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCZqPyUdTWlvNXo9MVd2mXVNSugokQSsAkdfArNoecoSVzxZvXzrK3hyIFSvw3N8BXgdFy2WPZ/55uuWReuudpDmsbUntdT//31hCm86KMKLinK/9qd43+L+xtOc7W9X7WUn0Zdy29OtUUgzp1SV17vZoxi+GVijQkBLgENHxQUe6N6gupl8YtSMrG8aUdw63VA2JzGIIuehv5icseBtPqAv1Bb3PV5RGvhrPxQhY87FXE3Yr144yVCQjqUJIBP6KGqDLt8qp5+If+mNkuE3JfdFQKxAj5h6fWFXtZ4JRP2ax0bJcgk8GA3mhHd/fofFYE5D4nekQ2dTVat4n0D62MVAgMBAAECggEAFFSObpn/ahlM+BCCmINP2+C4D/3IIezWl+cUitZz/hmOyYXE3uOm8euUaL1Md0Xfrx3WMm4c1xluuKejAYQng7BTxEfr13pUQIqm9w6Um8E12Fz3sNIryvqDR2L51b93B9328mv7Ix/KsKeFmyJiNiyMQH/68goU+en7S6AkCP2KRUf9zIkCn+7s5ARlB82nH8nOlL8W4J57GBM5w8T5TXIdeZ+kVehaLbNYs45kRDZHgSCDLVZcvWOlQEDLQupU8Z97LMgNugwu26lChX3XJ1Nd8DrixTV61DKzg5qkKFhHb3f+oK89SYbw6DCNmMdI/Eo+MKK4x71fXeCJF7jigQKBgQDNKWgml+DQ1Yv92NI9jeotmtTKfPmHvYjY99+7WZe9hofwLh0F/53efc/x2woSaFMwF/fYDM2qi4sPf/ee2r9jrz2zr7igCfs65YPePWEfw61Enb6XXY5tVz3+0ukkLh5qsJe3+fYcJryvyFhLRHoR01VadxextqwogFQyP5pwtQKBgQC/vIiXNWdnw1+sgoH1ERJCFcOgQZqkHNOj985so+owiRlDjGY20Vhe+i7AJpASIuZIG5vHSG7Q13m15vkj1eSLa/Xjvo985paEv/wkZH5QWv3VSvN+3KuZX1uxDIhwiSKw8o7T8vIPIwPehemI5/wh2eVz5xuMx0zqpl6kRm2E4QKBgQCza3zcE4LW34qYK2Bh2JRt4bQqiXUoEsd1CAGvtMCRTO/hSlKzhkKn61tJ5gc+5eGqc6uaVEgg1/x7R6xkTmWGZiEN3aUWwOPnNAVI/GDOCLClC1c8xOLkGKHtEsI1k+eh4mIs0Ps8z1vAVvNwj6g5oppIjVI5ZsnUebYoY5tK6QKBgGW8TeWdqo5+Nke8v+Qe+Mcj8TcZWEp4GTxJgrhWiS7TsBoNZzbzAabzrNa8H3ngFrxwGco1/sbFbok8UznWhnwL/t6nAE/PslIh5FITGIrenQ0NxgGsaUjqNibNdZ/ww/2L1olTwWiStbze5TmFjl0P1xYWrKGYJpHjURhtXjNBAoGARNV7eG+H6pAFjECQ7fgNWAbPYQaFuWZQijMdRec6OOnLRvnzMlBWkrCmlSQS8Wl5PKNSkj9x1BCbFitrfv6SUONFi8Mx13lRQKHrWEvyrvPwv7pA2x1QkIn3po1p6obh8xmy7NsUPoBnZsWUWgyE94bYYmHteEmxclezrazsac4="; + + /** + * 支付宝公钥 + */ + public static final String ALIPAY_PUB_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoKAJ8j4r772JuPQDgVsMckqkeDQtJN4xZRa208dv9TClOsPUyptpyjDTnb01cXrZCBxWWbnYx5YlYjq+4Fee22HOWlh+lozSmFWeWigKefj7zSFji1WMmHTnSlYF0nvRPKWMfXKBqlnvkG1H82jxmeqDsZGw/a53jFL5EKiaS7bvLf3BlS8KCEuMvnwGQXMiTaIe98GkV8YcbEUXAEi7KLrinslEVwkCOA7y8exjOlCWJEi2lcbXyiuqejDs2K594O8wOd5RcoWpOmWYRMV0dhjue7ovETyErfeK0A0nTCmXmbZdcLYVOODzLHO2zupVSyF6Rj845BLb+z2tnZ3QwIDAQAB"; + + + /** + * 签名类型 + */ + public static final String SIGN_TYPE = "RSA2"; + + + /** + * 字符编码 + */ + public static final String CHARSET = "UTF-8"; + + + /** + * 返回参数格式 + */ + public static final String FORMAT = "json"; + + + /** + * 构造函数私有化 + */ + private AlipayConfig() { + + } + + private volatile static AlipayClient instance = null; + + /** + * 单例模式获取, 双重锁校验 + * + * @return + */ + public static AlipayClient getInstance() { + if (instance == null) { + synchronized (AlipayConfig.class) { + if (instance == null) { + instance = new DefaultAlipayClient(PAY_GATEWAY, APPID, APP_PRI_KEY, FORMAT, CHARSET, ALIPAY_PUB_KEY, SIGN_TYPE); + } + } + } + return instance; + } + + +} diff --git a/nla-order-service/src/main/java/cn/nla/order/config/PayUrlConfig.java b/nla-order-service/src/main/java/cn/nla/order/config/PayUrlConfig.java new file mode 100644 index 0000000..709feb1 --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/config/PayUrlConfig.java @@ -0,0 +1,23 @@ +package cn.nla.order.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +@Data +public class PayUrlConfig { + + /** + * 支付成功页面跳转 + */ + @Value("${alipay.success_return_url}") + private String alipaySuccessReturnUrl; + + + /** + * 支付成功,回调通知 + */ + @Value("${alipay.callback_url}") + private String alipayCallbackUrl; +} diff --git a/nla-order-service/src/main/java/cn/nla/order/config/RabbitMQConfig.java b/nla-order-service/src/main/java/cn/nla/order/config/RabbitMQConfig.java index ad10a51..a629af1 100644 --- a/nla-order-service/src/main/java/cn/nla/order/config/RabbitMQConfig.java +++ b/nla-order-service/src/main/java/cn/nla/order/config/RabbitMQConfig.java @@ -47,7 +47,6 @@ public class RabbitMQConfig { */ @Value("${mq.config.ttl}") private Integer ttl; - /** * 消息转换器 * @return @@ -99,5 +98,4 @@ public class RabbitMQConfig { public Binding orderCloseBinding(){ return new Binding(orderCloseQueue,Binding.DestinationType.QUEUE,eventExchange,orderCloseRoutingKey,null); } - } diff --git a/nla-order-service/src/main/java/cn/nla/order/controller/CallbackController.java b/nla-order-service/src/main/java/cn/nla/order/controller/CallbackController.java new file mode 100644 index 0000000..2ef62c3 --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/controller/CallbackController.java @@ -0,0 +1,74 @@ +package cn.nla.order.controller; + +import cn.nla.common.enums.ProductOrderPayTypeEnum; +import cn.nla.common.util.JsonData; +import cn.nla.order.config.AlipayConfig; +import cn.nla.order.service.ProductOrderService; +import com.alipay.api.AlipayApiException; +import com.alipay.api.internal.util.AlipaySignature; +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + + +@Api(tags ="订单回调通知模块") +@Controller +@RequestMapping("/odr/callback/v1") +@Slf4j +public class CallbackController { + @Autowired + private ProductOrderService productOrderService; + + /** + * 支付回调通知 post方式 + */ + @PostMapping("alipay") + public String alipayCallback(HttpServletRequest request, HttpServletResponse response) { + //将异步通知中收到的所有参数存储到map中 + log.info("into alipay "); + Map paramsMap = convertRequestParamsToMap(request); + log.info("支付宝回调通知结果:{}", paramsMap); + //调用SDK验证签名 + try { + boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.ALIPAY_PUB_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE); + if (signVerified) { + JsonData jsonData = productOrderService.handlerOrderCallbackMsg(ProductOrderPayTypeEnum.ALIPAY, paramsMap); + if (jsonData.getCode() == 0) { + //通知结果确认成功,不然会一直通知,八次都没返回success就认为交易失败 + return "success"; + } + } + } catch (AlipayApiException e) { + log.info("支付宝回调验证签名失败:异常:{},参数:{}", e, paramsMap); + } + return "failure"; + } + + /** + * 将request中的参数转换成Map + */ + private static Map convertRequestParamsToMap(HttpServletRequest request) { + Map paramsMap = new HashMap<>(16); + Set> entrySet = request.getParameterMap().entrySet(); + for (Map.Entry entry : entrySet) { + String name = entry.getKey(); + String[] values = entry.getValue(); + int size = values.length; + if (size == 1) { + paramsMap.put(name, values[0]); + } else { + paramsMap.put(name, ""); + } + } + return paramsMap; + } +} diff --git a/nla-order-service/src/main/java/cn/nla/order/controller/ProductOrderController.java b/nla-order-service/src/main/java/cn/nla/order/controller/ProductOrderController.java index 17bd311..a2cb3fe 100644 --- a/nla-order-service/src/main/java/cn/nla/order/controller/ProductOrderController.java +++ b/nla-order-service/src/main/java/cn/nla/order/controller/ProductOrderController.java @@ -1,22 +1,41 @@ package cn.nla.order.controller; +import cn.nla.common.constant.CacheKey; import cn.nla.common.enums.BizCodeEnum; import cn.nla.common.enums.ClientType; import cn.nla.common.enums.ProductOrderPayTypeEnum; +import cn.nla.common.interceptor.LoginInterceptor; +import cn.nla.common.model.LoginUser; +import cn.nla.common.util.CommonUtil; import cn.nla.common.util.JsonData; +import cn.nla.order.config.AlipayConfig; +import cn.nla.order.config.PayUrlConfig; import cn.nla.order.model.request.ConfirmOrderRequest; +import cn.nla.order.model.request.RepayOrderRequest; import cn.nla.order.service.ProductOrderService; +import com.alibaba.fastjson.JSON; +import com.alipay.api.AlipayApiException; +import com.alipay.api.request.AlipayTradeWapPayRequest; +import com.alipay.api.response.AlipayTradeWapPayResponse; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; /** *

@@ -34,6 +53,33 @@ public class ProductOrderController { @Resource private ProductOrderService orderService; + @Resource + private PayUrlConfig payUrlConfig; + + @Autowired + private StringRedisTemplate redisTemplate; + + @ApiOperation("获取提交订单令牌") + @GetMapping("get_token") + public JsonData getOrderToken() { + LoginUser loginUser = LoginInterceptor.threadLocal.get(); + String key = String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY, loginUser.getId()); + String token = CommonUtil.getStringNumRandom(32); + redisTemplate.opsForValue().set(key, token, 30, TimeUnit.MINUTES); + return JsonData.buildSuccess(token); + } + + + @ApiOperation("分页查询我的订单列表") + @GetMapping("page") + public JsonData pagePOrderList( + @ApiParam(value = "当前页") @RequestParam(value = "page", defaultValue = "1") int page, + @ApiParam(value = "每页显示多少条") @RequestParam(value = "size", defaultValue = "10") int size, + @ApiParam(value = "订单状态") @RequestParam(value = "state", required = false) String state) { + return JsonData.buildSuccess(orderService.page(page, size, state)); + } + + /** * 查询订单状态 *

@@ -52,21 +98,46 @@ public class ProductOrderController { public void confirmOrder(@ApiParam("订单对象") @RequestBody ConfirmOrderRequest orderRequest, HttpServletResponse response) { JsonData jsonData = orderService.confirmOrder(orderRequest); if (jsonData.getCode() == 0) { -// String client = orderRequest.getClientType(); -// String payType = orderRequest.getPayType(); -// //如果是支付宝网页支付,都是跳转网页,APP除外 -// if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())) { -// log.info("创建支付宝订单成功:{}", orderRequest.toString()); -// if (client.equalsIgnoreCase(ClientType.H5.name())) { -// writeData(response, jsonData); -// } else if (client.equalsIgnoreCase(ClientType.APP.name())) { -// //APP SDK支付 TODO -// } -// } else if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) { -// //微信支付 TODO -// } + String client = orderRequest.getClientType(); + String payType = orderRequest.getPayType(); + //如果是支付宝网页支付,都是跳转网页,APP除外 + if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())) { + log.info("创建支付宝订单成功:{}", orderRequest.toString()); + if (client.equalsIgnoreCase(ClientType.H5.name())) { + writeData(response, jsonData); + } else if (client.equalsIgnoreCase(ClientType.APP.name())) { + //APP SDK支付 TODO + } + } else if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) { + //微信支付 TODO + } } else { log.error("创建订单失败{}", jsonData); + CommonUtil.sendJsonMessage(response, jsonData); + } + } + + @ApiOperation("重新支付订单") + @PostMapping("repay") + public void repay(@ApiParam("订单对象") @RequestBody RepayOrderRequest repayOrderRequest, HttpServletResponse response) { + JsonData jsonData = orderService.repay(repayOrderRequest); + if (jsonData.getCode() == 0) { + String client = repayOrderRequest.getClientType(); + String payType = repayOrderRequest.getPayType(); + //如果是支付宝网页支付,都是跳转网页,APP除外 + if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())) { + log.info("重新支付订单成功:{}", repayOrderRequest.toString()); + if (client.equalsIgnoreCase(ClientType.H5.name())) { + writeData(response, jsonData); + } else if (client.equalsIgnoreCase(ClientType.APP.name())) { + //APP SDK支付 TODO + } + } else if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) { + //微信支付 TODO + } + } else { + log.error("重新支付订单失败{}", jsonData.toString()); + CommonUtil.sendJsonMessage(response, jsonData); } } @@ -81,5 +152,64 @@ public class ProductOrderController { log.error("写出Html异常", e); } } + + + /** + * 将request中的参数转换成Map + */ + private static Map convertRequestParamsToMap(HttpServletRequest request) { + Map paramsMap = new HashMap<>(16); + Set> entrySet = request.getParameterMap().entrySet(); + + for (Map.Entry entry : entrySet) { + String name = entry.getKey(); + String[] values = entry.getValue(); + int size = values.length; + if (size == 1) { + paramsMap.put(name, values[0]); + } else { + paramsMap.put(name, ""); + } + } + return paramsMap; + } + + /** + * 测试支付方法 + */ + @GetMapping("test_pay") + public void testAlipay(HttpServletResponse response) throws AlipayApiException, IOException { + HashMap content = new HashMap<>(); + //商户订单号,64个字符以内、可包含字母、数字、下划线;需保证在商户端不重复 + String no = UUID.randomUUID().toString(); + log.info("订单号:{}", no); + content.put("out_trade_no", no); + content.put("product_code", "FAST_INSTANT_TRADE_PAY"); + //订单总金额,单位为元,精确到小数点后两位 + content.put("total_amount", String.valueOf("111.99")); + //商品标题/交易标题/订单标题/订单关键字等。 注意:不可使用特殊字符,如 /,=,& 等。 + content.put("subject", "杯子"); + //商品描述,可空 + content.put("body", "好的杯子"); + // 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 + // 该参数数值不接受小数点, 如 1.5h,可转换为 90m。 + content.put("timeout_express", "5m"); + AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest(); + request.setBizContent(JSON.toJSONString(content)); + request.setNotifyUrl(payUrlConfig.getAlipayCallbackUrl()); + request.setReturnUrl(payUrlConfig.getAlipaySuccessReturnUrl()); + AlipayTradeWapPayResponse alipayResponse = AlipayConfig.getInstance().pageExecute(request); + if (alipayResponse.isSuccess()) { + System.out.println("调用成功"); + String form = alipayResponse.getBody(); + response.setContentType("text/html;charset=UTF-8"); + response.getWriter().write(form); + response.getWriter().flush(); + response.getWriter().close(); + } else { + System.out.println("调用失败"); + } + } + } diff --git a/nla-order-service/src/main/java/cn/nla/order/feign/CouponFeignService.java b/nla-order-service/src/main/java/cn/nla/order/feign/CouponFeignService.java index 7530f58..747fa09 100644 --- a/nla-order-service/src/main/java/cn/nla/order/feign/CouponFeignService.java +++ b/nla-order-service/src/main/java/cn/nla/order/feign/CouponFeignService.java @@ -13,9 +13,10 @@ public interface CouponFeignService { /** * 领取优惠券 */ - @GetMapping("/cop/coupon/v1/promotion/{coupon_id}") + @GetMapping("/cop/coupon_record/v1/detail/{record_id}") JsonData findUserCouponRecordById(@PathVariable("record_id") long recordId); - @PostMapping("/cop/coupon/v1/lock_records") + + @PostMapping("/cop/coupon_record/v1/lock_records") JsonData lockCouponRecords(@RequestBody LockCouponRecordRequest recordRequest); } diff --git a/nla-order-service/src/main/java/cn/nla/order/feign/ProductFeignService.java b/nla-order-service/src/main/java/cn/nla/order/feign/ProductFeignService.java index 98db4b3..38f8414 100644 --- a/nla-order-service/src/main/java/cn/nla/order/feign/ProductFeignService.java +++ b/nla-order-service/src/main/java/cn/nla/order/feign/ProductFeignService.java @@ -18,6 +18,6 @@ public interface ProductFeignService { /** * 商品库存锁定 */ - @PostMapping("/cop/couponRecord/v1/lock_products") + @PostMapping("/pdt/product/v1/lock_products") JsonData lockProducts(@RequestBody LockProductRequest lockProductRequest); } diff --git a/nla-order-service/src/main/java/cn/nla/order/mapper/ProductOrderMapper.java b/nla-order-service/src/main/java/cn/nla/order/mapper/ProductOrderMapper.java index 2b29f37..0e25230 100644 --- a/nla-order-service/src/main/java/cn/nla/order/mapper/ProductOrderMapper.java +++ b/nla-order-service/src/main/java/cn/nla/order/mapper/ProductOrderMapper.java @@ -2,6 +2,7 @@ package cn.nla.order.mapper; import cn.nla.order.model.entity.ProductOrderEntity; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; /** *

@@ -13,4 +14,11 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; */ public interface ProductOrderMapper extends BaseMapper { + /** + * 更新订单状态 + * @param outTradeNo + * @param newState + * @param oldState + */ + void updateOrderPayState(@Param("outTradeNo") String outTradeNo, @Param("newState") String newState, @Param("oldState") String oldState); } diff --git a/nla-order-service/src/main/java/cn/nla/order/model/VO/PayInfoVO.java b/nla-order-service/src/main/java/cn/nla/order/model/VO/PayInfoVO.java new file mode 100644 index 0000000..ba1a63d --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/model/VO/PayInfoVO.java @@ -0,0 +1,42 @@ +package cn.nla.order.model.VO; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PayInfoVO { + /** + * 订单号 + */ + private String outTradeNo; + /** + * 订单总金额 + */ + private BigDecimal payFee; + /** + * 支付类型 微信-支付宝-银行-其他 + */ + private String payType; + /** + * 端类型 APP/H5/PC + */ + private String clientType; + /** + * 标题 + */ + private String title; + /** + * 描述 + */ + private String description; + /** + * 订单支付超时时间,毫秒 + */ + private long orderPayTimeoutMills; +} diff --git a/nla-order-service/src/main/java/cn/nla/order/model/VO/ProductOrderVO.java b/nla-order-service/src/main/java/cn/nla/order/model/VO/ProductOrderVO.java new file mode 100644 index 0000000..5733f19 --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/model/VO/ProductOrderVO.java @@ -0,0 +1,82 @@ +package cn.nla.order.model.VO; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +public class ProductOrderVO { + private Long id; + /** + * 订单唯一标识 + */ + private String outTradeNo; + + /** + * NEW 未支付订单,PAY已经支付订单,CANCEL超时取消订单 + */ + private String state; + + /** + * 订单生成时间 + */ + private Date createTime; + + /** + * 订单总金额 + */ + private BigDecimal totalAmount; + + /** + * 订单实际支付价格 + */ + private BigDecimal payAmount; + + /** + * 支付类型,微信-银行-支付宝 + */ + private String payType; + + /** + * 昵称 + */ + private String nickname; + + /** + * 头像 + */ + private String headImg; + + /** + * 用户id + */ + private Long userId; + + /** + * 0表示未删除,1表示已经删除 + */ + private Integer del; + + /** + * 更新时间 + */ + private Date updateTime; + + /** + * 订单类型 DAILY普通单,PROMOTION促销订单 + */ + private String orderType; + + /** + * 收货地址 json存储 + */ + private String receiverAddress; + + /** + * 订单项 + */ + private List orderItemList; + +} diff --git a/nla-order-service/src/main/java/cn/nla/order/model/request/RepayOrderRequest.java b/nla-order-service/src/main/java/cn/nla/order/model/request/RepayOrderRequest.java new file mode 100644 index 0000000..52bb7dd --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/model/request/RepayOrderRequest.java @@ -0,0 +1,23 @@ +package cn.nla.order.model.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class RepayOrderRequest { + /** + * 订单号 + */ + @JsonProperty("out_trade_no") + private String outTradeNo; + /** + * 支付类型- 微信-银行卡-支付宝 + */ + @JsonProperty("pay_type") + private String payType; + /** + * 订单号 + */ + @JsonProperty("client_type") + private String clientType; +} diff --git a/nla-order-service/src/main/java/cn/nla/order/mq/ProductOrderMQListener.java b/nla-order-service/src/main/java/cn/nla/order/mq/ProductOrderMQListener.java new file mode 100644 index 0000000..5012d92 --- /dev/null +++ b/nla-order-service/src/main/java/cn/nla/order/mq/ProductOrderMQListener.java @@ -0,0 +1,43 @@ +package cn.nla.order.mq; + +import cn.nla.common.model.OrderMessage; +import cn.nla.order.service.ProductOrderService; +import com.rabbitmq.client.Channel; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.RabbitHandler; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import java.io.IOException; + + +@Slf4j +@Component +@RabbitListener(queues = "${mq.config.order_close_queue}") +public class ProductOrderMQListener { + @Autowired + private ProductOrderService productOrderService; + + /** + * + * 消费重复消息,幂等性保证 + * 并发情况下如何保证安全 + */ + @RabbitHandler + public void closeProductOrder(OrderMessage orderMessage, Message message, Channel channel) throws IOException { + log.info("监听到消息:closeProductOrder:{}",orderMessage); + long msgTag = message.getMessageProperties().getDeliveryTag(); + try{ + boolean flag = productOrderService.closeProductOrder(orderMessage); + if(flag){ + channel.basicAck(msgTag,false); + }else { + channel.basicReject(msgTag,true); + } + }catch (IOException e){ + log.error("定时关单失败:",orderMessage); + channel.basicReject(msgTag,true); + } + } +} diff --git a/nla-order-service/src/main/java/cn/nla/order/service/ProductOrderService.java b/nla-order-service/src/main/java/cn/nla/order/service/ProductOrderService.java index 60e58d9..e3a6438 100644 --- a/nla-order-service/src/main/java/cn/nla/order/service/ProductOrderService.java +++ b/nla-order-service/src/main/java/cn/nla/order/service/ProductOrderService.java @@ -1,10 +1,17 @@ package cn.nla.order.service; +import cn.nla.common.enums.ProductOrderPayTypeEnum; +import cn.nla.common.model.OrderMessage; +import cn.nla.common.model.PageResult; import cn.nla.common.util.JsonData; +import cn.nla.order.model.VO.ProductOrderVO; import cn.nla.order.model.entity.ProductOrderEntity; import cn.nla.order.model.request.ConfirmOrderRequest; +import cn.nla.order.model.request.RepayOrderRequest; import com.baomidou.mybatisplus.extension.service.IService; +import java.util.Map; + /** *

* 订单表 服务类 @@ -31,5 +38,29 @@ public interface ProductOrderService extends IService { */ JsonData confirmOrder(ConfirmOrderRequest request); + /** + * 查询订单状态 + */ String queryProductOrderState(String outTradeNo); + + /** + * 队列监听,定时关单 + */ + boolean closeProductOrder(OrderMessage orderMessage); + + /** + * 支付结果回调通知 + */ + JsonData handlerOrderCallbackMsg(ProductOrderPayTypeEnum alipay, Map paramsMap); + + /** + * 分页查询我的订单列表 + */ + PageResult page(int page, int size, String state); + + /** + * 订单二次支付 + */ + JsonData repay(RepayOrderRequest repayOrderRequest); + } diff --git a/nla-order-service/src/main/java/cn/nla/order/service/impl/ProductOrderServiceImpl.java b/nla-order-service/src/main/java/cn/nla/order/service/impl/ProductOrderServiceImpl.java index a13e157..38b0a3f 100644 --- a/nla-order-service/src/main/java/cn/nla/order/service/impl/ProductOrderServiceImpl.java +++ b/nla-order-service/src/main/java/cn/nla/order/service/impl/ProductOrderServiceImpl.java @@ -1,36 +1,37 @@ package cn.nla.order.service.impl; import cn.nla.common.constant.CacheKey; +import cn.nla.common.constant.TimeConstant; import cn.nla.common.enums.*; import cn.nla.common.exception.BizException; import cn.nla.common.interceptor.LoginInterceptor; import cn.nla.common.model.LoginUser; import cn.nla.common.model.OrderMessage; +import cn.nla.common.model.PageResult; import cn.nla.common.util.CommonUtil; import cn.nla.common.util.JsonData; +import cn.nla.order.component.PayFactory; import cn.nla.order.config.RabbitMQConfig; import cn.nla.order.feign.CouponFeignService; import cn.nla.order.feign.ProductFeignService; import cn.nla.order.feign.UserFeignService; import cn.nla.order.mapper.ProductOrderItemMapper; -import cn.nla.order.model.VO.CouponRecordVO; -import cn.nla.order.model.VO.OrderItemVO; -import cn.nla.order.model.VO.ProductOrderAddressVO; +import cn.nla.order.model.VO.*; import cn.nla.order.model.entity.ProductOrderEntity; import cn.nla.order.mapper.ProductOrderMapper; import cn.nla.order.model.entity.ProductOrderItemEntity; -import cn.nla.order.model.request.ConfirmOrderRequest; -import cn.nla.order.model.request.LockCouponRecordRequest; -import cn.nla.order.model.request.LockProductRequest; -import cn.nla.order.model.request.OrderItemRequest; +import cn.nla.order.model.request.*; import cn.nla.order.service.ProductOrderService; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; @@ -38,9 +39,7 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; /** @@ -76,6 +75,9 @@ public class ProductOrderServiceImpl extends ServiceImpl(script, Long.class), -// List.of(String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY, loginUser.getId())), orderToken); -// if (result == 0L) { -// throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_EQUAL_FAIL); -// } + String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end"; + Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), + List.of(String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY, loginUser.getId())), orderToken); + if (result == 0L) { + throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_EQUAL_FAIL); + } + String orderOutTradeNo = CommonUtil.getStringNumRandom(32); //获取收货地址详情 ProductOrderAddressVO addressVO = this.getUserAddress(request.getAddressId()); @@ -113,17 +116,26 @@ public class ProductOrderServiceImpl extends ServiceImpl orderItemList) { List list = orderItemList.stream().map( - obj->{ + obj -> { ProductOrderItemEntity itemDO = new ProductOrderItemEntity(); itemDO.setBuyNum(obj.getBuyNum()); itemDO.setProductId(obj.getProductId()); @@ -158,6 +170,7 @@ public class ProductOrderServiceImpl extends ServiceImpl() + .eq("out_trade_no", orderMessage.getOutTradeNo())); + if (productOrderDO == null) { + //订单不存在 + log.warn("直接确认消息,订单不存在:{}", orderMessage); + return true; + } + if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) { + //已经支付 + log.info("直接确认消息,订单已经支付:{}", orderMessage); + return true; + } + //向第三方支付查询订单是否真的未支付 todo + PayInfoVO payInfoVO = new PayInfoVO(); + payInfoVO.setPayType(productOrderDO.getPayType()); + payInfoVO.setOutTradeNo(orderMessage.getOutTradeNo()); + String payResult = payFactory.queryPaySuccess(payInfoVO); + //结果为空,则未支付成功,本地取消订单 + if (StringUtils.isBlank(payResult)) { + baseMapper.updateOrderPayState(productOrderDO.getOutTradeNo(), ProductOrderStateEnum.CANCEL.name(), ProductOrderStateEnum.NEW.name()); + log.info("结果为空,则未支付成功,本地取消订单:{}", orderMessage); + return true; + } else { + //支付成功,主动的把订单状态改成UI就支付,造成该原因的情况可能是支付通道回调有问题 + log.warn("支付成功,主动的把订单状态改成UI就支付,造成该原因的情况可能是支付通道回调有问题:{}", orderMessage); + baseMapper.updateOrderPayState(productOrderDO.getOutTradeNo(), ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name()); + return true; + } + + } + + + /*** + * 支付通知结果更新订单状态 + */ + @Override + public JsonData handlerOrderCallbackMsg(ProductOrderPayTypeEnum payType, Map paramsMap) { + //MQ投递 --》在慢慢消费 + + if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())) { + //支付宝支付 + //获取商户订单号 + String outTradeNo = paramsMap.get("out_trade_no"); + //交易的状态 + String tradeStatus = paramsMap.get("trade_status"); + if ("TRADE_SUCCESS".equalsIgnoreCase(tradeStatus) || "TRADE_FINISHED".equalsIgnoreCase(tradeStatus)) { + //更新订单状态 + baseMapper.updateOrderPayState(outTradeNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name()); + return JsonData.buildSuccess(); + } + + } else if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) { + //微信支付 TODO + } + return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS); + } + + @Override + public PageResult page(int page, int size, String state) { + LoginUser loginUser = LoginInterceptor.threadLocal.get(); + Page pageInfo = new Page<>(page, size); + IPage orderDOPage = null; + if (StringUtils.isBlank(state)) { + orderDOPage = baseMapper.selectPage(pageInfo, new QueryWrapper().eq("user_id", loginUser.getId())); + } else { + orderDOPage = baseMapper.selectPage(pageInfo, new QueryWrapper().eq("user_id", loginUser.getId()) + .eq("state", state)); + } + //获取订单列表 + List productOrderDOList = orderDOPage.getRecords(); + List productOrderVOList = productOrderDOList.stream().map(orderDO -> { + List itemDOList = productOrderItemMapper.selectList( + new QueryWrapper().eq("product_order_id", orderDO.getId())); + List itemVOList = itemDOList.stream().map(item -> { + OrderItemVO itemVO = new OrderItemVO(); + BeanUtils.copyProperties(item, itemVO); + return itemVO; + }).collect(Collectors.toList()); + ProductOrderVO productOrderVO = new ProductOrderVO(); + BeanUtils.copyProperties(orderDO, productOrderVO); + productOrderVO.setOrderItemList(itemVOList); + return productOrderVO; + }).collect(Collectors.toList()); + return new PageResult<>(orderDOPage.getTotal(), orderDOPage.getPages(), productOrderVOList); + } + + @Override + public JsonData repay(RepayOrderRequest repayOrderRequest) { + LoginUser loginUser = LoginInterceptor.threadLocal.get(); + ProductOrderEntity productOrderDO = baseMapper.selectOne(new QueryWrapper().eq("out_trade_no", + repayOrderRequest.getOutTradeNo()).eq("user_id", loginUser.getId())); + log.info("订单状态:{}", productOrderDO); + if (productOrderDO == null) { + return JsonData.buildResult(BizCodeEnum.PAY_ORDER_NOT_EXIST); + } + //订单状态不对,不是NEW状态 + if (!productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())) { + return JsonData.buildResult(BizCodeEnum.PAY_ORDER_STATE_ERROR); + } else { + //订单创建到现在的存活时间 + long orderLiveTime = CommonUtil.getCurrentTimestamp() - productOrderDO.getCreateTime().getTime(); + //创建订单是临界点,所以再增加1分钟多几秒,假如29分,则也不能支付了 + orderLiveTime = orderLiveTime + 70 * 1000; + //大于订单超时时间,则失效 + if (orderLiveTime > TimeConstant.ORDER_PAY_TIMEOUT_MILLS) { + return JsonData.buildResult(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT); + } else { + //记得更新DB订单支付参数 payType,还可以增加订单支付信息日志 TODO + //总时间-存活的时间 = 剩下的有效时间 + long timeout = TimeConstant.ORDER_PAY_TIMEOUT_MILLS - orderLiveTime; + //创建支付 + PayInfoVO payInfoVO = new PayInfoVO(productOrderDO.getOutTradeNo(), + productOrderDO.getPayAmount(), repayOrderRequest.getPayType(), + repayOrderRequest.getClientType(), productOrderDO.getOutTradeNo(), "", timeout); + log.info("payInfoVO={}", payInfoVO); + String payResult = payFactory.pay(payInfoVO); + if (StringUtils.isNotBlank(payResult)) { + log.info("创建二次支付订单成功:payInfoVO={},payResult={}", payInfoVO, payResult); + return JsonData.buildSuccess(payResult); + } else { + log.error("创建二次支付订单失败:payInfoVO={},payResult={}", payInfoVO, payResult); + return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL); + } + } + + } + } + /** * 验证价格 @@ -267,7 +410,7 @@ public class ProductOrderServiceImpl extends ServiceImpl + + + + update product_order set `state`=#{newState} where out_trade_no = #{outTradeNo} and `state` = #{oldState} + + diff --git a/nla-product-service/src/main/java/cn/nla/product/controller/ProductController.java b/nla-product-service/src/main/java/cn/nla/product/controller/ProductController.java index e3ca13d..bf80b7b 100644 --- a/nla-product-service/src/main/java/cn/nla/product/controller/ProductController.java +++ b/nla-product-service/src/main/java/cn/nla/product/controller/ProductController.java @@ -24,7 +24,7 @@ import java.util.Map; */ @Api(tags = "商品控制器") @RestController -@RequestMapping("//pdt/product/v1") +@RequestMapping("/pdt/product/v1") public class ProductController { @Resource diff --git a/nla-product-service/src/main/resources/application.yml b/nla-product-service/src/main/resources/application.yml index ebece0b..91a17e1 100644 --- a/nla-product-service/src/main/resources/application.yml +++ b/nla-product-service/src/main/resources/application.yml @@ -60,7 +60,7 @@ mq: #消息过期,进入释放队列的key stock_release_routing_key: stock.release.routing.key #消息过期时间,毫秒,临时改为6分钟 - ttl: 60000 + ttl: 360000 feign: client: