浏览代码

添加支付功能

macro 1 年之前
父节点
当前提交
b3bc051e79

+ 7 - 0
mall-portal/pom.xml

@@ -41,6 +41,13 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-amqp</artifactId>
         </dependency>
+        <!--支付宝支付Java SDK-->
+        <dependency>
+            <groupId>com.alipay.sdk</groupId>
+            <artifactId>alipay-sdk-java</artifactId>
+            <version>${alipay-sdk.version}</version>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 21 - 0
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());
+    }
+}

+ 57 - 0
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";
+}

+ 74 - 0
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<String, String> params = new HashMap<>();
+        Map<String, String[]> 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<String> query(String outTradeNo, String tradeNo){
+        return CommonResult.success(alipayService.query(outTradeNo,tradeNo));
+    }
+}

+ 27 - 0
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;
+}

+ 36 - 0
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<String, String> params);
+
+    /**
+     * @param outTradeNo 商户订单编号
+     * @param tradeNo 支付宝交易编号
+     * @return 支付宝交易状态
+     */
+    String query(String outTradeNo, String tradeNo);
+
+    /**
+     * 根据提交参数生成手机支付页面
+     */
+    String webPay(AliPayParam aliPayParam);
+}

+ 6 - 0
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);
 }

+ 160 - 0
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<String, String> 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;
+    }
+}

+ 14 - 0
mall-portal/src/main/java/com/macro/mall/portal/service/impl/OmsPortalOrderServiceImpl.java

@@ -419,6 +419,20 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
         }
     }
 
+    @Override
+    public void paySuccessByOrderSn(String orderSn, Integer payType) {
+        OmsOrderExample example =  new OmsOrderExample();
+        example.createCriteria()
+                .andOrderSnEqualTo(orderSn)
+                .andStatusEqualTo(0)
+                .andDeleteStatusEqualTo(0);
+        List<OmsOrder> orderList = orderMapper.selectByExample(example);
+        if(CollUtil.isNotEmpty(orderList)){
+            OmsOrder order = orderList.get(0);
+            paySuccess(order.getId(),payType);
+        }
+    }
+
     /**
      * 生成18位订单编号:8位日期+2位平台号码+2位支付方式+6位以上自增id
      */

+ 8 - 0
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:
+

+ 8 - 0
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:
+

+ 1 - 0
mall-portal/src/main/resources/application.yml

@@ -36,6 +36,7 @@ secure:
       - /home/**
       - /product/**
       - /brand/**
+      - /alipay/**
 
 # 自定义redis key
 redis:

+ 15 - 0
pom.xml

@@ -46,6 +46,7 @@
         <spring-data-commons.version>2.7.5</spring-data-commons.version>
         <jjwt.version>0.9.1</jjwt.version>
         <aliyun-oss.version>2.5.0</aliyun-oss.version>
+        <alipay-sdk.version>4.38.61.ALL</alipay-sdk.version>
         <logstash-logback.version>7.2</logstash-logback.version>
         <minio.version>8.4.5</minio.version>
         <jaxb-api.version>2.3.1</jaxb-api.version>
@@ -257,4 +258,18 @@
         </pluginManagement>
     </build>
 
+    <!--使用aliyun的Maven镜像源提升下载速度-->
+    <repositories>
+        <repository>
+            <id>aliyunmaven</id>
+            <name>aliyun</name>
+            <url>https://maven.aliyun.com/repository/public</url>
+        </repository>
+        <repository>
+            <id>central2</id>
+            <name>central2</name>
+            <url>https://repo1.maven.org/maven2/</url>
+        </repository>
+    </repositories>
+
 </project>