From b3bc051e790fcf006de9d8b61057fa5241331ef7 Mon Sep 17 00:00:00 2001 From: macro Date: Tue, 19 Sep 2023 09:37:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=94=AF=E4=BB=98=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mall-portal/pom.xml | 7 + .../portal/config/AlipayClientConfig.java | 21 +++ .../mall/portal/config/AlipayConfig.java | 57 +++++++ .../portal/controller/AlipayController.java | 74 ++++++++ .../macro/mall/portal/domain/AliPayParam.java | 27 +++ .../mall/portal/service/AlipayService.java | 36 ++++ .../portal/service/OmsPortalOrderService.java | 6 + .../service/impl/AlipayServiceImpl.java | 160 ++++++++++++++++++ .../impl/OmsPortalOrderServiceImpl.java | 14 ++ .../src/main/resources/application-dev.yml | 8 + .../src/main/resources/application-prod.yml | 8 + .../src/main/resources/application.yml | 1 + pom.xml | 15 ++ 13 files changed, 434 insertions(+) create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/config/AlipayClientConfig.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/config/AlipayConfig.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/controller/AlipayController.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/domain/AliPayParam.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/service/AlipayService.java create mode 100644 mall-portal/src/main/java/com/macro/mall/portal/service/impl/AlipayServiceImpl.java diff --git a/mall-portal/pom.xml b/mall-portal/pom.xml index 0e08369ea3..cf8ef45b30 100644 --- a/mall-portal/pom.xml +++ b/mall-portal/pom.xml @@ -41,6 +41,13 @@ org.springframework.boot spring-boot-starter-amqp + + + com.alipay.sdk + alipay-sdk-java + ${alipay-sdk.version} + + diff --git a/mall-portal/src/main/java/com/macro/mall/portal/config/AlipayClientConfig.java b/mall-portal/src/main/java/com/macro/mall/portal/config/AlipayClientConfig.java new file mode 100644 index 0000000000..6f50cc8ffc --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/config/AlipayClientConfig.java @@ -0,0 +1,21 @@ +package com.macro.mall.portal.config; + +import com.alipay.api.AlipayClient; +import com.alipay.api.DefaultAlipayClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @auther macrozheng + * @description 支付宝请求客户端配置 + * @date 2023/9/8 + * @github https://github.com/macrozheng + */ +@Configuration +public class AlipayClientConfig { + + @Bean + public AlipayClient alipayClient(AlipayConfig config){ + return new DefaultAlipayClient(config.getGatewayUrl(),config.getAppId(),config.getAppPrivateKey(), config.getFormat(),config.getCharset(),config.getAlipayPublicKey(),config.getSignType()); + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/config/AlipayConfig.java b/mall-portal/src/main/java/com/macro/mall/portal/config/AlipayConfig.java new file mode 100644 index 0000000000..7a26e1fc3c --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/config/AlipayConfig.java @@ -0,0 +1,57 @@ +package com.macro.mall.portal.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @auther macrozheng + * @description 支付宝支付相关配置 + * @date 2023/9/8 + * @github https://github.com/macrozheng + */ +@Getter +@Setter +@Component +@ConfigurationProperties(prefix = "alipay") +public class AlipayConfig { + /** + * 支付宝网关 + */ + private String gatewayUrl; + /** + * 应用ID + */ + private String appId; + /** + * 应用私钥 + */ + private String appPrivateKey; + /** + * 支付宝公钥 + */ + private String alipayPublicKey; + /** + * 用户确认支付后,支付宝调用的页面返回路径 + * 开发环境为:http://localhost:8060/#/pages/money/paySuccess + */ + private String returnUrl; + /** + * 支付成功后,支付宝服务器主动通知商户服务器里的异步通知回调(需要公网能访问) + * 开发环境为:http://localhost:8085/alipay/notify + */ + private String notifyUrl; + /** + * 参数返回格式,只支持JSON + */ + private String format = "JSON"; + /** + * 请求使用的编码格式 + */ + private String charset = "UTF-8"; + /** + * 生成签名字符串所使用的签名算法类型 + */ + private String signType = "RSA2"; +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/controller/AlipayController.java b/mall-portal/src/main/java/com/macro/mall/portal/controller/AlipayController.java new file mode 100644 index 0000000000..cde1cbad70 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/controller/AlipayController.java @@ -0,0 +1,74 @@ +package com.macro.mall.portal.controller; + +import com.macro.mall.common.api.CommonResult; +import com.macro.mall.portal.config.AlipayConfig; +import com.macro.mall.portal.domain.AliPayParam; +import com.macro.mall.portal.service.AlipayService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * @auther macrozheng + * @description 支付宝支付Controller + * @date 2023/9/8 + * @github https://github.com/macrozheng + */ +@Controller +@Api(tags = "AlipayController") +@Tag(name = "AlipayController", description = "支付宝支付相关接口") +@RequestMapping("/alipay") +public class AlipayController { + + @Autowired + private AlipayConfig alipayConfig; + @Autowired + private AlipayService alipayService; + + @ApiOperation("支付宝电脑网站支付") + @RequestMapping(value = "/pay", method = RequestMethod.GET) + public void pay(AliPayParam aliPayParam, HttpServletResponse response) throws IOException { + response.setContentType("text/html;charset=" + alipayConfig.getCharset()); + response.getWriter().write(alipayService.pay(aliPayParam)); + response.getWriter().flush(); + response.getWriter().close(); + } + + @ApiOperation("支付宝手机网站支付") + @RequestMapping(value = "/webPay", method = RequestMethod.GET) + public void webPay(AliPayParam aliPayParam, HttpServletResponse response) throws IOException { + response.setContentType("text/html;charset=" + alipayConfig.getCharset()); + response.getWriter().write(alipayService.webPay(aliPayParam)); + response.getWriter().flush(); + response.getWriter().close(); + } + + @ApiOperation(value = "支付宝异步回调",notes = "必须为POST请求,执行成功返回success,执行失败返回failure") + @RequestMapping(value = "/notify", method = RequestMethod.POST) + public String notify(HttpServletRequest request){ + Map params = new HashMap<>(); + Map requestParams = request.getParameterMap(); + for (String name : requestParams.keySet()) { + params.put(name, request.getParameter(name)); + } + return alipayService.notify(params); + } + + @ApiOperation(value = "支付宝统一收单线下交易查询",notes = "订单支付成功返回:TRADE_SUCCESS") + @RequestMapping(value = "/query", method = RequestMethod.GET) + @ResponseBody + public CommonResult query(String outTradeNo, String tradeNo){ + return CommonResult.success(alipayService.query(outTradeNo,tradeNo)); + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/domain/AliPayParam.java b/mall-portal/src/main/java/com/macro/mall/portal/domain/AliPayParam.java new file mode 100644 index 0000000000..f2e704f46c --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/domain/AliPayParam.java @@ -0,0 +1,27 @@ +package com.macro.mall.portal.domain; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @auther macrozheng + * @description 支付宝支付请求参数 + * @date 2023/9/8 + * @github https://github.com/macrozheng + */ +@Data +public class AliPayParam { + /** + * 商户订单号,商家自定义,保持唯一性 + */ + private String outTradeNo; + /** + * 商品的标题/交易标题/订单标题/订单关键字等 + */ + private String subject; + /** + * 订单总金额,单位为元,精确到小数点后两位 + */ + private BigDecimal totalAmount; +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/service/AlipayService.java b/mall-portal/src/main/java/com/macro/mall/portal/service/AlipayService.java new file mode 100644 index 0000000000..785f3477cf --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/service/AlipayService.java @@ -0,0 +1,36 @@ +package com.macro.mall.portal.service; + + +import com.macro.mall.portal.domain.AliPayParam; + +import java.util.Map; + +/** + * @auther macrozheng + * @description 支付宝支付Service + * @date 2023/9/8 + * @github https://github.com/macrozheng + */ +public interface AlipayService { + /** + * 根据提交参数生成电脑支付页面 + */ + String pay(AliPayParam aliPayParam); + + /** + * 支付宝异步回调处理 + */ + String notify(Map params); + + /** + * @param outTradeNo 商户订单编号 + * @param tradeNo 支付宝交易编号 + * @return 支付宝交易状态 + */ + String query(String outTradeNo, String tradeNo); + + /** + * 根据提交参数生成手机支付页面 + */ + String webPay(AliPayParam aliPayParam); +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/service/OmsPortalOrderService.java b/mall-portal/src/main/java/com/macro/mall/portal/service/OmsPortalOrderService.java index 1df24b865f..67847c0c4c 100644 --- a/mall-portal/src/main/java/com/macro/mall/portal/service/OmsPortalOrderService.java +++ b/mall-portal/src/main/java/com/macro/mall/portal/service/OmsPortalOrderService.java @@ -67,4 +67,10 @@ public interface OmsPortalOrderService { * 用户根据订单ID删除订单 */ void deleteOrder(Long orderId); + + /** + * 根据orderSn来实现的支付成功逻辑 + */ + @Transactional + void paySuccessByOrderSn(String orderSn, Integer payType); } diff --git a/mall-portal/src/main/java/com/macro/mall/portal/service/impl/AlipayServiceImpl.java b/mall-portal/src/main/java/com/macro/mall/portal/service/impl/AlipayServiceImpl.java new file mode 100644 index 0000000000..4c35875718 --- /dev/null +++ b/mall-portal/src/main/java/com/macro/mall/portal/service/impl/AlipayServiceImpl.java @@ -0,0 +1,160 @@ +package com.macro.mall.portal.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayClient; +import com.alipay.api.internal.util.AlipaySignature; +import com.alipay.api.request.AlipayTradePagePayRequest; +import com.alipay.api.request.AlipayTradeQueryRequest; +import com.alipay.api.request.AlipayTradeWapPayRequest; +import com.alipay.api.response.AlipayTradeQueryResponse; +import com.macro.mall.mapper.OmsOrderMapper; +import com.macro.mall.portal.config.AlipayConfig; +import com.macro.mall.portal.domain.AliPayParam; +import com.macro.mall.portal.service.AlipayService; +import com.macro.mall.portal.service.OmsPortalOrderService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * @auther macrozheng + * @description 支付宝支付Service实现类 + * @date 2023/9/8 + * @github https://github.com/macrozheng + */ +@Slf4j +@Service +public class AlipayServiceImpl implements AlipayService { + @Autowired + private AlipayConfig alipayConfig; + @Autowired + private AlipayClient alipayClient; + @Autowired + private OmsOrderMapper orderMapper; + @Autowired + private OmsPortalOrderService portalOrderService; + @Override + public String pay(AliPayParam aliPayParam) { + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); + if(StrUtil.isNotEmpty(alipayConfig.getNotifyUrl())){ + //异步接收地址,公网可访问 + request.setNotifyUrl(alipayConfig.getNotifyUrl()); + } + if(StrUtil.isNotEmpty(alipayConfig.getReturnUrl())){ + //同步跳转地址 + request.setReturnUrl(alipayConfig.getReturnUrl()); + } + //******必传参数****** + JSONObject bizContent = new JSONObject(); + //商户订单号,商家自定义,保持唯一性 + bizContent.put("out_trade_no", aliPayParam.getOutTradeNo()); + //支付金额,最小值0.01元 + bizContent.put("total_amount", aliPayParam.getTotalAmount()); + //订单标题,不可使用特殊符号 + bizContent.put("subject", aliPayParam.getSubject()); + //电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAY + bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); + request.setBizContent(bizContent.toString()); + String formHtml = null; + try { + formHtml = alipayClient.pageExecute(request).getBody(); + } catch (AlipayApiException e) { + e.printStackTrace(); + } + return formHtml; + } + + @Override + public String notify(Map params) { + String result = "failure"; + boolean signVerified = false; + try { + //调用SDK验证签名 + signVerified = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(), alipayConfig.getCharset(), alipayConfig.getSignType()); + } catch (AlipayApiException e) { + log.error("支付回调签名校验异常!",e); + e.printStackTrace(); + } + if (signVerified) { + String tradeStatus = params.get("trade_status"); + if("TRADE_SUCCESS".equals(tradeStatus)){ + result = "success"; + log.info("notify方法被调用了,tradeStatus:{}",tradeStatus); + String outTradeNo = params.get("out_trade_no"); + portalOrderService.paySuccessByOrderSn(outTradeNo,1); + }else{ + log.warn("订单未支付成功,trade_status:{}",tradeStatus); + } + } else { + log.warn("支付回调签名校验失败!"); + } + return result; + } + + @Override + public String query(String outTradeNo, String tradeNo) { + AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); + //******必传参数****** + JSONObject bizContent = new JSONObject(); + //设置查询参数,out_trade_no和trade_no至少传一个 + if(StrUtil.isNotEmpty(outTradeNo)){ + bizContent.put("out_trade_no",outTradeNo); + } + if(StrUtil.isNotEmpty(tradeNo)){ + bizContent.put("trade_no",tradeNo); + } + //交易结算信息: trade_settle_info + String[] queryOptions = {"trade_settle_info"}; + bizContent.put("query_options", queryOptions); + request.setBizContent(bizContent.toString()); + AlipayTradeQueryResponse response = null; + try { + response = alipayClient.execute(request); + } catch (AlipayApiException e) { + log.error("查询支付宝账单异常!",e); + } + if(response.isSuccess()){ + log.info("查询支付宝账单成功!"); + portalOrderService.paySuccessByOrderSn(outTradeNo,1); + } else { + log.error("查询支付宝账单失败!"); + } + //交易状态:WAIT_BUYER_PAY(交易创建,等待买家付款)、TRADE_CLOSED(未付款交易超时关闭,或支付完成后全额退款)、TRADE_SUCCESS(交易支付成功)、TRADE_FINISHED(交易结束,不可退款) + return response.getTradeStatus(); + } + + @Override + public String webPay(AliPayParam aliPayParam) { + AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest (); + if(StrUtil.isNotEmpty(alipayConfig.getNotifyUrl())){ + //异步接收地址,公网可访问 + request.setNotifyUrl(alipayConfig.getNotifyUrl()); + } + if(StrUtil.isNotEmpty(alipayConfig.getReturnUrl())){ + //同步跳转地址 + request.setReturnUrl(alipayConfig.getReturnUrl()); + } + //******必传参数****** + JSONObject bizContent = new JSONObject(); + //商户订单号,商家自定义,保持唯一性 + bizContent.put("out_trade_no", aliPayParam.getOutTradeNo()); + //支付金额,最小值0.01元 + bizContent.put("total_amount", aliPayParam.getTotalAmount()); + //订单标题,不可使用特殊符号 + bizContent.put("subject", aliPayParam.getSubject()); + //手机网站支付默认传值FAST_INSTANT_TRADE_PAY + bizContent.put("product_code", "QUICK_WAP_WAY"); + request.setBizContent(bizContent.toString()); + String formHtml = null; + try { + formHtml = alipayClient.pageExecute(request).getBody(); + } catch (AlipayApiException e) { + e.printStackTrace(); + } + return formHtml; + } +} diff --git a/mall-portal/src/main/java/com/macro/mall/portal/service/impl/OmsPortalOrderServiceImpl.java b/mall-portal/src/main/java/com/macro/mall/portal/service/impl/OmsPortalOrderServiceImpl.java index 8d6de6a78c..dbc4b5ee7d 100644 --- a/mall-portal/src/main/java/com/macro/mall/portal/service/impl/OmsPortalOrderServiceImpl.java +++ b/mall-portal/src/main/java/com/macro/mall/portal/service/impl/OmsPortalOrderServiceImpl.java @@ -419,6 +419,20 @@ public void deleteOrder(Long orderId) { } } + @Override + public void paySuccessByOrderSn(String orderSn, Integer payType) { + OmsOrderExample example = new OmsOrderExample(); + example.createCriteria() + .andOrderSnEqualTo(orderSn) + .andStatusEqualTo(0) + .andDeleteStatusEqualTo(0); + List orderList = orderMapper.selectByExample(example); + if(CollUtil.isNotEmpty(orderList)){ + OmsOrder order = orderList.get(0); + paySuccess(order.getId(),payType); + } + } + /** * 生成18位订单编号:8位日期+2位平台号码+2位支付方式+6位以上自增id */ diff --git a/mall-portal/src/main/resources/application-dev.yml b/mall-portal/src/main/resources/application-dev.yml index 8ab796b193..867ecae855 100644 --- a/mall-portal/src/main/resources/application-dev.yml +++ b/mall-portal/src/main/resources/application-dev.yml @@ -42,3 +42,11 @@ logstash: host: localhost enableInnerLog: false +alipay: + gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do + appId: your appId + alipayPublicKey: your alipayPublicKey + appPrivateKey: your appPrivateKey + returnUrl: http://localhost:8060/#/pages/money/paySuccess + notifyUrl: + diff --git a/mall-portal/src/main/resources/application-prod.yml b/mall-portal/src/main/resources/application-prod.yml index 0782b34ea4..d06d8a0902 100644 --- a/mall-portal/src/main/resources/application-prod.yml +++ b/mall-portal/src/main/resources/application-prod.yml @@ -50,3 +50,11 @@ logging: logstash: host: logstash +alipay: + gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do + appId: your appId + alipayPublicKey: your alipayPublicKey + appPrivateKey: your appPrivateKey + returnUrl: http://192.168.3.101:8060/#/pages/money/paySuccess + notifyUrl: + diff --git a/mall-portal/src/main/resources/application.yml b/mall-portal/src/main/resources/application.yml index 2547f1f361..a50c0d906f 100644 --- a/mall-portal/src/main/resources/application.yml +++ b/mall-portal/src/main/resources/application.yml @@ -36,6 +36,7 @@ secure: - /home/** - /product/** - /brand/** + - /alipay/** # 自定义redis key redis: diff --git a/pom.xml b/pom.xml index cffa2e31c2..4cad1b217a 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,7 @@ 2.7.5 0.9.1 2.5.0 + 4.38.61.ALL 7.2 8.4.5 2.3.1 @@ -257,4 +258,18 @@ + + + + aliyunmaven + aliyun + https://maven.aliyun.com/repository/public + + + central2 + central2 + https://repo1.maven.org/maven2/ + + + \ No newline at end of file