• 2022谷粒商城学习笔记(二十五)支付宝沙箱模拟支付


    前言

    本系列博客基于B站谷粒商城,只作为本人学习总结使用。这里我会比较注重业务逻辑的编写和相关配置的流程。有问题可以评论或者联系我互相交流。原视频地址谷粒商城雷丰阳版。本人git仓库地址Draknessssw的谷粒商城


    示例文档

    在这里插入图片描述


    两种加密方式

    在这里插入图片描述
    在这里插入图片描述


    相关概念

    私钥,公钥

    私钥适合加密数据,公钥适合明文传输数据。
    在这里插入图片描述

    加密和数字签名

    在这里插入图片描述

    验证签名

    在这里插入图片描述


    设置沙箱环境

    在支付宝开放平台助手上生成密钥

    在这里插入图片描述

    公钥复制到沙箱环境生成支付宝的公钥
    在这里插入图片描述

    至此,支付宝支付配置了支付宝公钥和自己的私钥

    在这里插入图片描述

    package com.xxxx.gulimall.order.config;
    
    import com.alipay.api.AlipayApiException;
    import com.alipay.api.AlipayClient;
    import com.alipay.api.DefaultAlipayClient;
    import com.alipay.api.request.AlipayTradePagePayRequest;
    import com.xxxx.gulimall.order.vo.PayVo;
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    @ConfigurationProperties(prefix = "alipay")
    @Component
    @Data
    public class AlipayTemplate {
    
        // 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
        public String app_id;
    
        // 商户私钥,您的PKCS8格式RSA2私钥
        public String merchant_private_key;
    
        // 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。
        public String alipay_public_key;
    
        // 服务器[异步通知]页面路径  需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
        // 支付宝会悄悄的给我们发送一个请求,告诉我们支付成功的信息
        public String notify_url;
    
        // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
        //同步通知,支付成功,一般跳转到成功页
        public String return_url;
    
        // 签名方式
        private  String sign_type;
    
        // 字符编码格式
        private  String charset;
    
        //订单超时时间
        private String timeout = "1m";
    
        // 支付宝网关; https://openapi.alipaydev.com/gateway.do
        public String gatewayUrl;
    
        public  String pay(PayVo vo) throws AlipayApiException {
    
            //AlipayClient alipayClient = new DefaultAlipayClient(AlipayTemplate.gatewayUrl, AlipayTemplate.app_id, AlipayTemplate.merchant_private_key, "json", AlipayTemplate.charset, AlipayTemplate.alipay_public_key, AlipayTemplate.sign_type);
            //1、根据支付宝的配置生成一个支付客户端
            AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl,
                    app_id, merchant_private_key, "json",
                    charset, alipay_public_key, sign_type);
    
            //2、创建一个支付请求 //设置请求参数
            AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
            alipayRequest.setReturnUrl(return_url);
            alipayRequest.setNotifyUrl(notify_url);
    
            //商户订单号,商户网站订单系统中唯一订单号,必填
            String out_trade_no = vo.getOut_trade_no();
            //付款金额,必填
            String total_amount = vo.getTotal_amount();
            //订单名称,必填
            String subject = vo.getSubject();
            //商品描述,可空
            String body = vo.getBody();
    
            alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
                    + "\"total_amount\":\""+ total_amount +"\","
                    + "\"subject\":\""+ subject +"\","
                    + "\"body\":\""+ body +"\","
                    + "\"timeout_express\":\""+timeout+"\","
                    + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
    
            String result = alipayClient.pageExecute(alipayRequest).getBody();
    
            //会收到支付宝的响应,响应的是一个页面,只要浏览器显示这个页面,就会自动来到支付宝的收银台页面
            System.out.println("支付宝的响应:"+result);
    
            return result;
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    内网穿透

    设置支付宝异步回调页面

    五种内网穿透方案

    沙箱环境中也要进行配置

    在这里插入图片描述
    最后是properties文件中的配置
    公钥和私钥按照自己的来吧

    alipay.app_id=2021000121642435
    alipay.merchant_private_key=MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCLjwJ0VNa8I5N8jVhY6Y3+rSrJLlyLcIxbQKY/nQMkDm2D7pbarVbQ5+kTP6uI50BBKMVAV6SenjNbdRNSC18Ynbqns/vWp4NjvG1c4R6zC5tkwqA3HkHtaGX9awl15qaK8W5vLO9qczuFA+s2Syk5KkEs4IW8cF93Mkb4cxAoU6j/3XONzHShWOrGyF1vbQyRpvADL0U+EUTvAhvkPfhbl/FdvB9ng6c3PnLm6D8Gsq6zDBiCnsx6esY7hX7iUpfI3BVBG39BfIjQWz/GBRWADvCKeJnbr+gxPzIF+P+8h2mcrVZHQ5wTwru5DzO9mxacd/K6bbvj0L69SxKRLpg/AgMBAAECggEAf0T94gT0h0KSX8WuyfbD7XsSR8Gl1+vds+IzOlP/50PZD7XnkKj/QSgc39byobjyWfnKWLEUiSqQf1k3M1z3bkV5UTLL+aduJOhTe545FuMA5VSwM1O+n7jTOdYBHKopOxCNu4lpFNTddKJsQGkuwNkN8tUWYRDBnrwVCMI1++9g+IsEcBEEph8Uj4LIrJnH7+d1+azmy+Vl+qqd94ZraK972rysz2w0+sa8IXu7vmpLMymQayF58P01rFG+OsBb/YMK7kOwRTNjveTFcY7R7TXPz8gsW1PjtnssUw0EUgOGUpqd5S+LOnKUc6aWuZ8NK9r7QlqRkR1RUbR46fyR0QKBgQDivRlK8MEGmk3r+iltBnnXK/zkPsNeaCcODdDiB4/hL7MBdOenGBQuaZo+HDLNiXNvFkO5KIJ/2ljNKQlkZWqZFv6oVVM6Y2AgL6/C3KQj1YDRj/sDplg/5DK+kfw4qZt6sASVBkXetaj8N9KhUTuMJ6tDTisDsFSrzzFReLnZzQKBgQCdkbETiN0dawswfhEOT38Mv9CN0pziYtQUFM3q6QpKVfzI3rF32xg+W1P5ixWKL8CMSYCKyEvkPN1rRNe0jwW6FZPRcAZDpbexq7XQTrascmJXL8aZuz+FPFb2CPUULcMK9Ev1MePxh9AYjh3VN6MlbLzSzD4/fz7CiQ6HeUD+OwKBgQCeAFIww8Zu+HYWW/QkMmATTmbjEs2H6yJUC9Kkv8pGjLu75yBKc2AU26gNYg8Q5ZiYL7avv4f42koJZXBTEs0Os1RwL01ZIcjphPGA48pJ4kzrO98asv9KPpYR8J8HSUG8ZA49XuqvgH2qjKftnDLXvwj8VOtqnaTTOQXQFUXFfQKBgASnmSOKl32W++2iy74wewBVakPGRPwrDzjIpIyb9cHcaGtGqNdxkXXGHOTyRuCeKIH8ad+vqw5C/gd9MSIUV4b3vDYjqQu2iYamG+jbamoNtvn8X1GLRoUZEziRayv9bhWUwemsX59y86LGD/uMeTVR0QIpJm2ZxLDae6Nk4ZahAoGAVYgsCuXaTlvGqWBH98RcvyQMT76w1FU/y9B8XSplpSGpHp1bGGZEnHzr3cwxhwNPhixKAEgA9jZrwEcEKaQELLNF5eXKnCwSrBevyh3SFy+KRa6KIox7ymOoElKccjPXdYEE7YO/3isgKC2Wdsfdy82luqo1XLGo1e3j1FVpn/I=
    alipay.alipay_public_key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtbbxUtAxCfZCIy47WM3N/oVxa4X5RzwUb94AcPtkHJsNYsvrpZ5moAceTZiMKlxg1+SRU1Hex5DJ7+tEB5d/Tz+5TKjY4YENgmdyIc3jv0KysFd8e2/8+yH4HNu062OHMr92rc7q/HqK2jwwJ5+4KWAOEbBxLf69pZZwbTA/aSXonR7i32+JzYzoJ1jgVL0YnMl3IgPAqo7dZrl3lho7XtFxe5BF+WyhiKJKwgYlGvYcyYmkDMwR7Mzj5bsrC1ZAEEVkjtt7rpRR30BIIAS8EkKCl7dY1VIc+Eh0q8Ax/W9ur1GkXY1ti7mEQK+AphaVTg8LE546yEKSJKKNYVxLhQIDAQAB
    alipay.notify_url=http://dongrongrong.free.svipss.top/payed/notify
    #alipay.notify_url=http://member.gulimall.com/payed/notify
    alipay.return_url=http://member.gulimall.com/memberOrder.html
    alipay.sign_type=RSA2
    alipay.charset=utf-8
    alipay.gatewayUrl=https://openapi.alipaydev.com/gateway.do
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    订单支付

    引入依赖

    整合支付宝的sdk

    		<!-- 支付宝sdk -->
            <dependency>
                <groupId>com.alipay.sdk</groupId>
                <artifactId>alipay-sdk-java</artifactId>
                <version>4.10.111.ALL</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    准备支付的Vo

    package com.xxxx.gulimall.order.vo;
    
    import lombok.Data;
    
    /**
     * @author LinLinD
     * @Create 2022-08-03-18:07
     */
    @Data
    public class PayVo {
        private String out_trade_no; // 商户订单号 必填
        private String subject; // 订单名称 必填
        private String total_amount;  // 付款金额 必填
        private String body; // 商品描述 可空
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    获取订单的支付信息
    在这里插入图片描述
    需要注意的是,这里返回的是alipatTemplate处理过的html页面,而不是json数据。

    
        /**
         * 用户下单:支付宝支付
         * 1、让支付页让浏览器展示
         * 2、支付成功以后,跳转到用户的订单列表页
         * @param orderSn
         * @return
         * @throws AlipayApiException
         */
        @ResponseBody
        @GetMapping(value = "/aliPayOrder",produces = "text/html")
        public String aliPayOrder(@RequestParam("orderSn") String orderSn) throws AlipayApiException {
    
            PayVo payVo = orderService.getOrderPay(orderSn);
            String pay = alipayTemplate.pay(payVo);
            System.out.println(pay);
            return pay;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    实现类

    
        /**
         * 获取当前订单的支付信息
         * @param orderSn
         * @return
         */
        @Override
        public PayVo getOrderPay(String orderSn) {
    
            PayVo payVo = new PayVo();
            OrderEntity orderInfo = this.getOrderByOrderSn(orderSn);
    
            //保留两位小数点,向上取值
            BigDecimal payAmount = orderInfo.getPayAmount().setScale(2, BigDecimal.ROUND_UP);
            payVo.setTotal_amount(payAmount.toString());
            payVo.setOut_trade_no(orderInfo.getOrderSn());
    
            //查询订单项的数据
            List<OrderItemEntity> orderItemInfo = orderItemService.list(
                    new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
            OrderItemEntity orderItemEntity = orderItemInfo.get(0);
            payVo.setBody(orderItemEntity.getSkuAttrsVals());
    
            payVo.setSubject(orderItemEntity.getSkuName());
    
            return payVo;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    支付成功返回订单列表页面

    配置订单列表页面为同步回调地址

    在这里插入图片描述

    alipay.return_url=http://member.gulimall.com/memberOrder.html
    
    • 1

    回到会员服务

    配置拦截器,确保用户在登录状态,而且是member下的页面

    在这里插入图片描述

    package com.xxxx.gulimall.member.interceptor;
    
    import com.xxxx.common.vo.MemberResponseVo;
    import org.springframework.stereotype.Component;
    import org.springframework.util.AntPathMatcher;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.PrintWriter;
    
    import static com.xxxx.common.constant.AuthServerConstant.LOGIN_USER;
    
    
    
    @Component
    public class LoginUserInterceptor implements HandlerInterceptor {
    
        public static ThreadLocal<MemberResponseVo> loginUser = new ThreadLocal<>();
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            String uri = request.getRequestURI();
            boolean match = new AntPathMatcher().match("/member/**", uri);
            if (match) {
                return true;
            }
    
            HttpSession session = request.getSession();
    
            //获取登录的用户信息
            MemberResponseVo attribute = (MemberResponseVo) session.getAttribute(LOGIN_USER);
    
            if (attribute != null) {
                //把登录后用户的信息放在ThreadLocal里面进行保存
                loginUser.set(attribute);
    
                return true;
            } else {
                //未登录,返回登录页面
                response.setContentType("text/html;charset=UTF-8");
                PrintWriter out = response.getWriter();
                out.println("");
                // session.setAttribute("msg", "请先进行登录");
                // response.sendRedirect("http://auth.gulimall.com/login.html");
                return false;
            }
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    同样配置远程请求的cookie同步配置,session配置和添加拦截器配置

    package com.xxxx.gulimall.member.config;
    
    import feign.RequestInterceptor;
    import feign.RequestTemplate;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    
    
    
    @Configuration
    public class GuliFeignConfig {
    
        @Bean("requestInterceptor")
        public RequestInterceptor requestInterceptor() {
    
            RequestInterceptor requestInterceptor = new RequestInterceptor() {
                @Override
                public void apply(RequestTemplate template) {
                    //1、使用RequestContextHolder拿到刚进来的请求数据
                    ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    
                    if (requestAttributes != null) {
                        //老请求
                        HttpServletRequest request = requestAttributes.getRequest();
    
                        if (request != null) {
                            //2、同步请求头的数据(主要是cookie)
                            //把老请求的cookie值放到新请求上来,进行一个同步
                            String cookie = request.getHeader("Cookie");
                            template.header("Cookie", cookie);
                        }
                    }
                }
            };
            return requestInterceptor;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    package com.xxxx.gulimall.member.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.session.web.http.CookieSerializer;
    import org.springframework.session.web.http.DefaultCookieSerializer;
    
    
    
    @Configuration
    public class GulimallSessionConfig {
    
        @Bean
        public CookieSerializer cookieSerializer() {
    
            DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
    
            //放大作用域
            cookieSerializer.setDomainName("gulimall.com");
            cookieSerializer.setCookieName("GULISESSION");
    
            return cookieSerializer;
        }
    
    
        @Bean
        public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
            return new GenericJackson2JsonRedisSerializer();
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    package com.xxxx.gulimall.member.config;
    
    import com.xxxx.gulimall.member.interceptor.LoginUserInterceptor;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    
    
    @Configuration
    public class MemberWebConfig implements WebMvcConfigurer {
    
        @Autowired
        private LoginUserInterceptor loginUserInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**");
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    去往订单页面

    在这里插入图片描述

    package com.xxxx.gulimall.member.web;
    
    import com.alibaba.fastjson.JSON;
    import com.xxxx.common.utils.R;
    import com.xxxx.gulimall.member.feign.OrderFeignService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.HashMap;
    import java.util.Map;
    
    
    
    @Controller
    public class MemberWebController {
    
        @Autowired
        private OrderFeignService orderFeignService;
    
        @GetMapping(value = "/memberOrder.html")
        public String memberOrderPage(@RequestParam(value = "pageNum",required = false,defaultValue = "0") Integer pageNum,
                                      Model model, HttpServletRequest request) {
    
            //获取到支付宝给我们转来的所有请求数据
            //request,验证签名
    
    
            //查出当前登录用户的所有订单列表数据
            Map<String,Object> page = new HashMap<>();
            page.put("page",pageNum.toString());
    
            //远程查询订单服务订单数据
            R orderInfo = orderFeignService.listWithItem(page);
            System.out.println(JSON.toJSONString(orderInfo));
            model.addAttribute("orders",orderInfo);
    
            return "orderList";
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    远程接口

    package com.xxxx.gulimall.member.feign;
    
    import com.xxxx.common.utils.R;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    
    import java.util.Map;
    
    
    
    @FeignClient("gulimall-order")
    public interface OrderFeignService {
    
        /**
         * 分页查询当前登录用户的所有订单信息
         * @param params
         * @return
         */
        @PostMapping("/order/order/listWithItem")
        R listWithItem(@RequestBody Map<String, Object> params);
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    
        /**
         * 分页查询当前登录用户的所有订单信息
         * @param params
         * @return
         */
        @PostMapping("/listWithItem")
        //@RequiresPermissions("order:order:list")
        public R listWithItem(@RequestBody Map<String, Object> params){
            PageUtils page = orderService.queryPageWithItem(params);
    
            return R.ok().put("page", page);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    实现类

    
        /**
         * 查询当前用户所有订单数据
         * @param params
         * @return
         */
        @Override
        public PageUtils queryPageWithItem(Map<String, Object> params) {
    
            MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();
    
            IPage<OrderEntity> page = this.page(
                    new Query<OrderEntity>().getPage(params),
                    new QueryWrapper<OrderEntity>()
                            .eq("member_id",memberResponseVo.getId()).orderByDesc("create_time")
            );
    
            //遍历所有订单集合
            List<OrderEntity> orderEntityList = page.getRecords().stream().map(order -> {
                //根据订单号查询订单项里的数据
                List<OrderItemEntity> orderItemEntities = orderItemService.list(new QueryWrapper<OrderItemEntity>()
                        .eq("order_sn", order.getOrderSn()));
                order.setOrderItemEntityList(orderItemEntities);
                return order;
            }).collect(Collectors.toList());
    
            page.setRecords(orderEntityList);
    
            return new PageUtils(page);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    异步通知页面构建

    写一个监听器来监听支付宝的异步通知

    需要验证签名和处理订单结果
    在这里插入图片描述

    package com.xxxx.gulimall.order.listener;
    
    import com.alipay.api.AlipayApiException;
    import com.alipay.api.internal.util.AlipaySignature;
    import com.xxxx.gulimall.order.config.AlipayTemplate;
    import com.xxxx.gulimall.order.service.OrderService;
    import com.xxxx.gulimall.order.vo.PayAsyncVo;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.UnsupportedEncodingException;
    import java.util.HashMap;
    import java.util.Map;
    
    
    
    @RestController
    public class OrderPayedListener {
    
        @Autowired
        private OrderService orderService;
    
        @Autowired
        private AlipayTemplate alipayTemplate;
    
        @PostMapping(value = "/payed/notify")
        public String handleAlipayed(PayAsyncVo asyncVo, HttpServletRequest request) throws AlipayApiException, UnsupportedEncodingException {
            // 只要收到支付宝的异步通知,返回 success 支付宝便不再通知
            // 获取支付宝POST过来反馈信息
            //TODO 需要验签
            Map<String, String> params = new HashMap<>();
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                String[] values = requestParams.get(name);
                String valueStr = "";
                for (int i = 0; i < values.length; i++) {
                    valueStr = (i == values.length - 1) ? valueStr + values[i]
                            : valueStr + values[i] + ",";
                }
                //乱码解决,这段代码在出现乱码时使用
                // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                params.put(name, valueStr);
            }
    
            boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayTemplate.getAlipay_public_key(),
                    alipayTemplate.getCharset(), alipayTemplate.getSign_type()); //调用SDK验证签名
    
            if (signVerified) {
                System.out.println("签名验证成功...");
                //去修改订单状态
                String result = orderService.handlePayResult(asyncVo);
                return result;
            } else {
                System.out.println("签名验证失败...");
                return "error";
            }
        }
    
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    异步通知Vo

    package com.xxxx.gulimall.order.vo;
    
    import lombok.Data;
    import lombok.ToString;
    
    import java.util.Date;
    
    @ToString
    @Data
    public class PayAsyncVo {
    
        private String gmt_create;
        private String charset;
        private String gmt_payment;
        private Date notify_time;
        private String subject;
        private String sign;
        private String buyer_id;//支付者的id
        private String body;//订单的信息
        private String invoice_amount;//支付金额
        private String version;
        private String notify_id;//通知id
        private String fund_bill_list;
        private String notify_type;//通知类型; trade_status_sync
        private String out_trade_no;//订单号
        private String total_amount;//支付的总额
        private String trade_status;//交易状态  TRADE_SUCCESS
        private String trade_no;//流水号
        private String auth_app_id;//
        private String receipt_amount;//商家收到的款
        private String point_amount;//
        private String app_id;//应用id
        private String buyer_pay_amount;//最终支付的金额
        private String sign_type;//签名类型
        private String seller_id;//商家的id
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    此时访问nginx服务器会有丢失请求头的问题

    去nginx的配置文件配置这个异步通知页面的请求头信息,同时配置内网穿透的网址
    在这里插入图片描述

    在登录的拦截器里放行

    在这里插入图片描述
    处理支付结果
    需要在支付信息表保存数据

    
        /**
         * 处理支付宝的支付结果
         * @param asyncVo
         * @return
         */
        @Transactional(rollbackFor = Exception.class)
        @Override
        public String handlePayResult(PayAsyncVo asyncVo) {
    
            //保存交易流水信息
            PaymentInfoEntity paymentInfo = new PaymentInfoEntity();
            paymentInfo.setOrderSn(asyncVo.getOut_trade_no());
            paymentInfo.setAlipayTradeNo(asyncVo.getTrade_no());
            paymentInfo.setTotalAmount(new BigDecimal(asyncVo.getBuyer_pay_amount()));
            paymentInfo.setSubject(asyncVo.getBody());
            paymentInfo.setPaymentStatus(asyncVo.getTrade_status());
            paymentInfo.setCreateTime(new Date());
            paymentInfo.setCallbackTime(asyncVo.getNotify_time());
            //添加到数据库中
            this.paymentInfoService.save(paymentInfo);
    
            //修改订单状态
            //获取当前状态
            String tradeStatus = asyncVo.getTrade_status();
    
            if (tradeStatus.equals("TRADE_SUCCESS") || tradeStatus.equals("TRADE_FINISHED")) {
                //支付成功状态
                String orderSn = asyncVo.getOut_trade_no(); //获取订单号
                this.updateOrderStatus(orderSn,OrderStatusEnum.PAYED.getCode(), PayConstant.ALIPAY);
            }
    
            return "success";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    更新支付状态的方法

    
        /**
         * 修改订单状态
         * @param orderSn
         * @param code
         */
        private void updateOrderStatus(String orderSn, Integer code,Integer payType) {
    
            this.baseMapper.updateOrderStatus(orderSn,code,payType);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    sql

    	<update id="updateOrderStatus">
            UPDATE oms_order
            SET `status` = #{code},modify_time = NOW(),pay_type = #{payType},payment_time = NOW()
            WHERE order_sn = #{orderSn}
        </update>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

  • 相关阅读:
    SparkML机器学习实战:应用回归算法,预测二手房价格
    【ArcGIS Pro二次开发】:CC工具箱1.1.1更新_免费_安装即可用
    3D 生成重建006-3DFuse 形状引导一致性的文生3d的方法
    基于SSM的汽车销售管理系统
    39 WEB漏洞-XXE&XML之利用检测绕过全解
    antd5 虚拟列表原理(rc-virtual-list)
    Ubuntu22.04下安装Spark2.4.0(Local模式)
    Java面试题总结
    springboot使用@Validated校验不生效
    初识Java 18-1 泛型
  • 原文地址:https://blog.csdn.net/qq_44737138/article/details/126911231