java电商项目(十一)

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
实时计算 Flink 版,5000CU*H 3个月
实时数仓Hologres,5000CU*H 100GB 3个月
简介: 本文接续前几个文章的项目进行讲解!

1

[TOC]

支付宝支付

我们之前有专题课程讲解支付宝支付,在我们项目中,不再重复讲解支付宝支付知识点,我们来简单回顾下基本支付流程

为了保证交易双方(商户和支付宝)的身份和数据安全,开发者在调用接口前,需要配置双方密钥,对交易数据进行双方校验。密钥包含应用私钥(APP_PRIVATE_KEY)和应用公钥(APP_PUBLIC_KEY)。生成密钥后,开发者需要在开放平台开发者中心进行密钥配置,配置完成后可以获取支付宝公钥(ALIPAY_PUBLIC_KEY)。密钥的配置旨在对交易数据进行双方校验。具体流程如下图所示:

支付的完整流程如下:

这里需要重点强调下同步通知和异步通知,同步通知和异步通知发送的数据没有本质的区别。
同步通知有2个作用:

  • 第一是从支付宝的页面上返回自己的网站继续后续操作;
  • 第二是携带支付状态的get参数;让自己的网站用于验证;

异步通知作用:

同步通知后;还需要异步通知主要是为了防止出现意外情况;因为涉及到金钱;这是一个对安全和稳定要求比较严格的场景;如果同步通知的过程中;用户不小心关闭了浏览器;或者浏览器卡死了;异步也能收到通知;记录支付状态;即便是用户端没问题;万一自己的服务器网络异常了一下呢?如果自己的服务器没有正确返回接受到通知的状态;支付宝的服务器会在一段时间内持续的往自己的服务器发送异步通知;一直到成功,一下是官网文档解释的异步通知发送的频率

程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是 success 这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);

所以我们支付成功,修改订单状态,最好在异步通知中处理,以确保订单状态的更改

2 开发准备

2.1 支付宝配置

正式的支付功能上线,需要使用企业的信息,所以我们这里使用支付宝提供的沙箱功能实现测试,如何访问沙箱呢?下面看一下实现步骤:

访问支付宝首页,选择角色:"我是开发者"

image-20200911112926288

使用手机上的支付宝,扫码登录到系统。登录后选择左上角“控制台”,然后在页面下方有“开发服务”

image-20200911113801822

沙箱的使用步骤:

步骤1:查看信息

image-20200911114203633

步骤2:设置秘钥

image-20200911114337706

大家第一次使用时,文本框内是空的,点击“支付宝密钥生成器”,下载exe文件,并安装。image-20200911114516666

运行程序,生成密钥:

image-20200911114722534

把这里的“应用公钥”复制到“加签管理”中的公钥位置。设置完毕后,密钥位置会变成“设置/查看”

image-20200911115118517

步骤3:下载沙箱钱包进行测试,注意这里必须使用沙箱版钱包进行测试,账户信息已给定。此软件只支持安卓系统

image-20200911115526829

步骤4:测试账户的信息

image-20200911115645079

2.2 内网穿透

因为需要支付宝调用我们的地址,进行同步和异步通知,这里需要进行内网穿透配置,这里我们使用natapp进行内网穿透

注册登录https://natapp.cn/

启动natapp本地客户端:

使用上面authtoken启动本地natapp客户端

natapp -authtoken=token

2.3 安装RabbitMQ

使用docker安装rabbitmq非常简单,虚拟机中执行下面命令即可

# 拉取镜像
docker pull rabbitmq:management

# 创建容器
docker run -d --hostname my-rabbit --name rabbit -p 5672:5672 -p 15672:15672 rabbitmq:management

3 订单支付流程

1558490059984

如上图,步骤分析如下:

  1. 用户下单之后,订单数据会存入到MySQL中
  2. 用户下单后,进入支付页面,支付页面调用支付系统,并在页面生成支付二维码。
  3. 用户扫码支付后,支付宝服务器会通调用前预留的回调地址,并携带支付状态信息。
  4. 支付系统接到支付状态信息后,将支付状态信息发送给RabbitMQ
  5. 订单系统监听RabbitMQ中的消息获取支付状态,并根据支付状态修改订单状态

4 支付微服务

我们的支付微服务,直接使用支付宝demo基础上开发,也就是之前大家在学习支付专题课程中的使用的案例代码

4.1 搭建支付微服务

创建legou-pay微服务工程:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>legou-parent</artifactId>
        <groupId>com.lxs</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>legou-pay</artifactId>

    <dependencies>
        <!-- 支付功能SDK -->
        <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.10.124.ALL</version>
        </dependency>

        <!-- amqp -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

    </dependencies>


</project>

legou-pay/src/main/resources/bootstrap.yml

spring:
  application:
    name: pay-service

config-repo/pay-service.yml

server:
  port: 9010

logging:
  #file: demo.log
  pattern:
    console: "%d - %msg%n"
  level:
    org.springframework.web: debug
    com.lxs: debug
spring:
  rabbitmq:
    host: 192.168.220.110
    port: 5672

mq:
  pay:
    exchange:
      order: exchange.pay.order
    queue:
      order: queue.pay.order
    routing:
      key: routing.pay.order

legou-pay/src/main/java/com/lxs/legou/pay/PayApplication.java

package com.lxs.legou.pay;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class PayApplication {
    public static void main(String[] args) {
        SpringApplication.run(PayApplication.class,args);
    }

}

4.2 导入支付demo代码

这里我们直接拷贝支付宝提供的二维码支付demo案例代码。

4.2.1 支付宝配置类

修改以上4个地方,改成自己的配置,其他不变

4.2.2 支付对象

@Configuration
public class AlipayConfig {

    /**
     * 支付客户端对象
     * @return
     */
    @Bean
    public AlipayClient alipayClient(){
        return new DefaultAlipayClient(AlipayUtils.gatewayUrl, AlipayUtils.app_id, AlipayUtils.merchant_private_key,"json", AlipayUtils.charset,
                AlipayUtils.alipay_public_key, AlipayUtils.sign_type);
    }

    /**
     * 支付请求对象
     * @return
     */
    @Bean
    public AlipayTradePagePayRequest  alipayTradePagePayRequest(){
        return  new AlipayTradePagePayRequest();
    }


}

4.2.3 异步通知

4.2.4 生成支付二维码

5 测试订单支付

使用postman添加商品到购物车,产生订单

把订单号,支付金额拷贝到支付页面

生成支付二维码,使用支付宝手机沙箱客户端,扫码支付

6 支付完成修改订单状态

6.1 RabbitMQ配置类

legou-pay/src/main/java/com/lxs/legou/pay/config/MqConfig.java

package com.lxs.legou.pay.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

@Configuration
public class MqConfig {

    @Autowired
    private Environment env;

    /**
     * 声明交换机
     * @return
     */
    @Bean
    public Exchange orderExchange() {
        return ExchangeBuilder.topicExchange(env.getProperty("mq.pay.exchange.order")).durable(true).build();
    }

    @Bean
    public Queue orderQueue() {
        return QueueBuilder.durable(env.getProperty("mq.pay.queue.order")).build();
    }

    @Bean
    public Binding orderBinding(@Qualifier("orderQueue") Queue queue, @Qualifier("orderExchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(env.getProperty("mq.pay.routing.key")).noargs();
    }


}

6.2 发送支付完成消息

在支付异步通知中发送支付完成消息到MQ

6.2 监听支付完成消息

在订单微服务工程中监听支付完成消息,修改订单状态为“已支付”

6.2.1 添加依赖

<!-- amqp -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

6.2.2 service中修改订单状态

OrderServiceImpl

@Override
public void updateStatus(String out_trade_no, String transaction_id) {
    //1.根据id 获取订单的数据
    Order order = getBaseMapper().selectById(Long.parseLong(out_trade_no));
    //2.更新
    order.setUpdateTime(new Date());

    //  支付的时间  从微信的参数中获取
    order.setPayTime(new Date());
    order.setOrderStatus("1");
    order.setPayStatus("1");
    order.setTransactionId(transaction_id);
    //3.更新到数据库
    getBaseMapper().updateById(order);
}

6.2.3 监听支付完成消息

legou-order/legou-order-service/src/main/java/com/lxs/legou/order/listener/OrderUpdateListener.java

package com.lxs.legou.order.listener;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lxs.legou.order.service.OrderService;
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;
import java.util.Map;

@Component
@RabbitListener(queues = "${mq.pay.queue.order}")
public class OrderUpdateListener {

    @Autowired
    private OrderService orderService;

    @Autowired
    private ObjectMapper objectMapper;

    @RabbitHandler
    public void handlerData(String msg) {
        //1.接收消息(有订单的ID  有transaction_id )
        Map<String, String> map = null;
        try {
            map = objectMapper.readValue(msg, Map.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //2.更新对营的订单的状态
        if (map != null) {
            if (map.get("trade_status").equals("TRADE_SUCCESS")) {
                orderService.updateStatus(map.get("out_trade_no"), map.get("trade_no"));
            } else {
                //删除订单 支付失败.....
            }
        }
    }
}

7 延迟30分钟取消订单(作业)

商城下订单后30分钟后没有完成支付,取消订单,流程分析

7.1 RabbitMQ延迟队列

RabbitMQ实现延迟队列可以借助TTL+死信队列实现

死信队列,英文缩写:DLX 。Dead Letter Exchange(死信交换机),当消息成为Dead message后,可以被重新发送到另一个交换机,这个交换机就是DLX。

消息成为死信的三种情况:

  1. 队列消息长度到达限制;

  2. 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;

  3. 原队列存在消息过期设置,消息到达超时时间未被消费;

队列绑定死信交换机:

给队列设置参数: x-dead-letter-exchange 和 x-dead-letter-routing-key

7.2 代码实现

配置文件

mq:
  pay:
    exchange:
      order: exchange.pay.order
    queue:
      order: queue.pay.order
    routing:
      key: routing.pay.order
  order:
    exchange:
      ttl: exchange.order.ttl
      dlx: exchange.order.dlx
    queue:
      ttl: queue.order.ttl
      dlx: queue.order.dlx
    routing:
      ttl: routing.order.ttl
      dlx: routing.order.dlx

RabbitMQ配置类

legou-order/legou-order-service/src/main/java/com/lxs/legou/order/config/MqConfig.java

package com.lxs.legou.order.config;

import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
 * 延迟30分钟取消订单
 * @Des 乐购商城项目
 */
@Configuration
public class MqConfig {

    @Autowired
    private Environment env;

    /**
     * 正常交换机
     * @return
     */
    @Bean
    public Exchange ttlExchange() {
        return ExchangeBuilder.directExchange(env.getProperty("mq.order.exchange.ttl")).durable(true).build();
    }

    /**
     * 私信交换机
     * @return
     */
    @Bean
    public Exchange dlxExchange() {
        return ExchangeBuilder.directExchange(env.getProperty("mq.order.exchange.dlx")).durable(true).build();
    }

    /**
     * 正常队列
     * @return
     */
    @Bean
    public Queue ttlQueue() {
        return QueueBuilder
                .durable(env.getProperty("mq.order.queue.ttl"))
                //死信交换机名称
                .withArgument("x-dead-letter-exchange", env.getProperty("mq.order.exchange.dlx"))
                //发送给私信交换机的routingkey
                .withArgument("x-dead-letter-routing-key", env.getProperty("mq.order.routing.dlx"))
                //队列过期时间,10秒过期
                .withArgument("x-message-ttl", 10000)
                .build();
    }

    /**
     * 死信队列
     * @return
     */
    @Bean
    public Queue dlxQueue() {
        return QueueBuilder.durable(env.getProperty("mq.order.queue.dlx")).build();
    }

    /**
     * 正常交换机,队列绑定
     * @param queue
     * @param exchange
     * @return
     */
    @Bean
    public Binding ttlBinding(@Qualifier("ttlQueue") Queue queue, @Qualifier("ttlExchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(env.getProperty("mq.order.routing.ttl")).noargs();
    }

    /**
     * 死信交换机,队列绑定
     * @param queue
     * @param exchange
     * @return
     */
    @Bean
    public Binding dlxBinding(@Qualifier("dlxQueue") Queue queue, @Qualifier("dlxExchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(env.getProperty("mq.order.routing.dlx")).noargs();
    }

}

在下单服务中发送延迟消息

        //发送延迟30分钟取消订单消息
rabbitTemplate.convertAndSend(env.getProperty("mq.order.exchange.ttl"), env.getProperty("mq.order.routing.ttl"), order.getId().toString());

监听延迟消息

legou-order/legou-order-service/src/main/java/com/lxs/legou/order/listener/OrderLazyListener.java

package com.lxs.legou.order.listener;

import com.lxs.legou.order.po.Order;
import com.lxs.legou.order.service.OrderService;
import org.apache.commons.lang3.StringUtils;
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;

@Component
@RabbitListener(queues = "${mq.order.queue.dlx}")
public class OrderLazyListener {

    @Autowired
    private OrderService orderService;

    @RabbitHandler
    public void handlerData(String msg) {
        System.out.println(msg);
        if (StringUtils.isNotEmpty(msg)) {
            Long id = Long.parseLong(msg);
            Order order = orderService.getById(id);
            order.setOrderStatus("3");
            orderService.updateById(order); //修改订单装填

            //回滚库存(作业)
        }
    }
}
相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
21天前
|
安全 NoSQL Java
java电商项目(十)
本文介绍了电商系统中订单结算和下单流程的实现。主要包括: 1. **订单结页**: - **收件地址分析**:用户从购物车页面跳转到订单结算页,加载用户收件地址。地址信息存储在 `address_` 表中。 - **实现用户收件地址查询**:通过用户登录名查询收件地址,涉及实体类、DAO、Service 和 Controller 的实现。 2. **下单**: - **业务分析**:下单时创建订单数据,包括订单表 `order_` 和订单明细表 `order_item_`,同时修改商品库存、增加用户积分并删除购物车数据。
30 3
|
21天前
|
缓存 NoSQL Java
java电商项目(十二)
本文接续前几个文章的项目进行讲解
84 1
|
21天前
|
存储 NoSQL Java
java电商项目(九)
本文介绍了购物车功能的实现过程,包括用户登录后将商品添加至购物车、购物车列表展示及微服务之间的认证机制。具体步骤如下: 1. **购物车功能**: - 用户选择商品并点击“加入购物车”,系统将商品信息存储到Redis中。 2. **微服务之间认证**: - **传递管理员令牌**:在授权中心微服务调用用户微服务时,生成管理员令牌并通过Header传递。 - **传递当前用户令牌**:用户登录后,通过Feign拦截器将用户令牌传递到其他微服务。 - **获取用户数据**:通过`SecurityContextHolder`获取用户信息,并使用公钥解密令牌以验证用户
26 1
|
21天前
|
监控 算法 Java
java电商项目(七)
微服务网关作为系统唯一对外的入口,位于客户端和服务端之间,处理非业务功能,如路由请求、鉴权、监控、缓存、限流等。它解决了客户端直接调用多个微服务带来的复杂性、跨域请求、认证复杂、难以重构等问题。常用的微服务网关技术有Nginx、Zuul和Spring Cloud Gateway。Spring Cloud Gateway因其集成断路器、路径重写和较好的性能而被广泛使用。本文介绍了如何使用Spring Cloud Gateway搭建后台网关系统,包括引入依赖、配置文件、跨域配置、路由过滤配置、负载均衡、限流等。此外,还详细讲解了RBAC权限数据管理、组织机构管理单点登录(SSO)及JWT鉴权的实现
28 1
|
21天前
|
canal 监控 JavaScript
java电商项目(六)
Thymeleaf 是一个类似于 FreeMarker 的模板引擎,能够完全替代 JSP。它支持动静结合,无网络时显示静态内容,有网络时用后台数据替换静态内容,并且与 Spring Boot 完美整合。本文介绍了如何使用 Thymeleaf 生成商品详情页的静态页面。具体步骤包括创建商品静态化微服务、配置项目依赖、创建 Controller 和 Service、生成静态页面、模板填充、静态资源过滤以及启动测试。此外,还介绍了如何通过 Canal 监听商品数据变化,自动触发静态页面的生成或删除。
28 1
|
21天前
|
SQL 自然语言处理 Java
java电商项目(五)
本文介绍了如何构建一个基于Elasticsearch的商品搜索微服务,主要包括以下几个部分: 1. **数据导入ES**: - 搭建搜索工程,创建`legou-search`项目,提供搜索服务和索引数据更新操作。 - 配置`pom.xml`文件,引入必要的依赖。 - 创建启动器和配置文件,配置Elasticsearch连接信息。 - 分析索引库数据格式,确定需要存储的数据字段。 - 实现商品微服务接口,调用其他微服务获取所需数据。 - 创建索引并导入数据,将SPU和SKU数据转换为索引库中的Goods对象。 2. **商品搜索*
25 1
|
21天前
|
canal NoSQL 关系型数据库
java电商项目(四)
本章介绍了如何通过Lua、OpenResty、Nginx限流及Canal的使用,实现电商门户首页的高并发解决方案。主要内容包括: 1. **商城门户搭建**:使用Vue和iView构建前端门户项目,介绍如何展示商品分类和广告数据,并通过Redis缓存提升访问速度。 2. **Lua基础**:介绍Lua的基本概念、特性、应用场景及安装步骤,并通过示例展示了Lua的基本语法和常用功能。 3. **OpenResty介绍**:详细说明OpenResty的特性和优势,包括如何安装OpenResty和配置Nginx,以及如何使用Lua脚本操作Nginx缓存和数据库。
20 1
|
21天前
|
存储 前端开发 JavaScript
java电商项目(二)
本文档详细介绍了商品分类和规格参数的实现过程。商品分类分为三级管理,主要用于首页商品导航和后台商品管理,采用树状结构存储。规格参数则用于描述商品的具体属性,包括SPU和SKU的定义,规格参数与分类绑定,支持搜索过滤。文档涵盖了表结构设计、实体类、持久层、业务层、控制层的实现,并提供了前端组件的示例代码,确保前后端无缝对接。
30 1
|
21天前
|
存储 安全 Java
java电商项目(八)
OAuth 2.0 是一种开放标准,允许用户授权第三方应用访问其在某一网站上的私密资源,而无需提供用户名和密码。它通过提供一个令牌(token)来实现这一功能。OAuth 2.0 主要包括四种授权模式:授权码模式、简化模式、密码模式和客户端模式。授权码模式是最常用的一种,适用于第三方平台登录功能。Spring Security OAuth 2.0 提供了强大的工具来实现授权服务器和资源服务器的集成,支持多种授权模式和令牌存储方式,如内存、数据库、JWT 和
39 0
|
21天前
|
前端开发 算法 JavaScript
java电商项目(三)
本文介绍了乐购商城的商品数据分析和管理功能。首先解释了SPU(标准产品单位)和SKU(库存量单位)的概念,以及它们在商品管理和销售中的作用。接着详细分析了SPU、SPU详情和SKU三个表的结构及其关系。文章还介绍了商品管理的需求分析、实现思路和后台代码,包括实体类、持久层、业务层和控制层的实现。最后,文章讲解了前端组件的设计和实现,包括列表组件、添加修改组件、商品描述、通用规格、SKU特有规格和SKU列表的处理。通过这些内容,读者可以全面了解乐购商城的商品管理和数据分析系统。
30 0
下一篇
无影云桌面