写在前面:做开发尤其是Java开发的朋友肯定都想去阿里这样的大厂(反正作者想去哈哈~),挑战“三高”以及各种极限到不能再极限的业务!那么今天作者就从这类电商企业最基本也是最核心的业务切入,简单实现一个订单支付业务
说人话:就是把交易信息保存起来,不然您的年度账单哪来
前期准备
支付开放平台
首先需要登录支付宝的支付开放平台并生成一个沙箱支付的账号
这里需要配置自己本地的回调地址
这里作者已经注册好了,然后简单了解一下支付的开放api就可以开始了
开发三板斧:约定>配置>编码
项目结构
数据库表结构
CREATE TABLE `product_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`out_trade_no` varchar(255) DEFAULT NULL COMMENT '订单编号',
`subject` varchar(255) DEFAULT NULL COMMENT '订单名',
`total_amount` double(10,2) DEFAULT NULL COMMENT '订单金额',
`order_status` tinyint(2) DEFAULT '0' COMMENT '订单状态(0:未支付,1:已支付,3:订单超时)',
`product_code` varchar(255) DEFAULT NULL COMMENT '产品编号',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.excesys</groupId>
<artifactId>springboot-pay-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-pay-service</name>
<description>springboot集成支付</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.7.110.ALL</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置
server:
port: 8081
spring:
application:
name: springboot-pay-service
main:
allow-bean-definition-overriding: true
datasource:
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
thymeleaf:
cache: false
redis:
host: localhost
password:
database: 15
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: off
type-aliases-package: com.excesys.pojo
# ----------阿里云-支付宝相关配置-----------
alipay:
#应用ID
appId: 你的应用ID
#应用公钥
alipayPublicKey: 你的应用公钥
#应用私钥
privateKey: 你的应用私钥
#异步通知回调地址
notifyUrl: http://127.0.0.1:8081/paySuccess
#同步回调地址 - 成功回调地址
returnUrl: http://127.0.0.1:8081/paySuccess
#编码格式
charset: UTF-8
#签名类型 - 密钥凭证
signType: RSA2
#支付宝的网关地址(注:这里是开发地址,生产和开发不一样)
gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do
logging:
level:
org:
springframework: info
file:
name: /logs/${spring.application.name}.log
代码
支付配置实体类
package com.excesys.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
//对应配置文件里的前缀
@ConfigurationProperties(prefix = "alipay")
public class AliPayConfig {
private String appId;
private String privateKey;
private String alipayPublicKey;
private String notifyUrl;
private String returnUrl;
private String charset;
private String signType;
private String gatewayUrl;
}
支付客户端请求实体
package com.excesys.bean;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.excesys.config.AliPayConfig;
import com.excesys.pojo.ProductOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AliPay {
@Autowired
private AliPayConfig aliPayConfig;
public String pay(Object obj) throws AlipayApiException {
//支付网关
String serverUrl = aliPayConfig.getGatewayUrl();
// AppId
String appId = aliPayConfig.getAppId();
// 用户密钥(私钥),即PKCS8格式RSA2私钥
String privateKey = aliPayConfig.getPrivateKey();
//格式化为json格式
String format = "json";
//编码
String charset = aliPayConfig.getCharset();
//支付宝公钥,即对应Appid下的支付宝公钥
String alipayPublicKey = aliPayConfig.getAlipayPublicKey();
//签名方式
String signType = aliPayConfig.getSignType();
//页面跳转同步通知页面路径
String returnUrl = aliPayConfig.getReturnUrl();
//服务器异步通知页面路径
String notifyUrl = aliPayConfig.getNotifyUrl();
//1、获取初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient(serverUrl, appId, privateKey, format, charset, alipayPublicKey, signType);
//2、设置请求参数
AlipayTradePagePayRequest alipayTradePagePayRequest = new AlipayTradePagePayRequest();
// 页面跳转同步通知页面路径
alipayTradePagePayRequest.setReturnUrl(returnUrl);
alipayTradePagePayRequest.setNotifyUrl(notifyUrl);
//封装参数(json格式)
alipayTradePagePayRequest.setBizContent(JSON.toJSONString(obj));
//3、请求支付宝进行付款,并获取支付结果
String body = alipayClient.pageExecute(alipayTradePagePayRequest).getBody();
//放回信息
return body;
}
}
Redis配置
package com.excesys.config;
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(factory);
//redis序列化--key采用String的序列化方式,value采用json的序列化方式
// template.setKeySerializer(RedisSerializer.string());
// template.setHashKeySerializer(RedisSerializer.string());
// template.setValueSerializer(RedisSerializer.json());
// template.setHashValueSerializer(RedisSerializer.json());
//Jackson序列化--key采用String的序列化方式,value采用Jackson的序列化方式
// Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// //序列化包括类型描述 否则反向序列化实体会报错,一律都为JsonObject
// ObjectMapper mapper = new ObjectMapper();
// mapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
// mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(),ObjectMapper.DefaultTyping.NON_FINAL);
// jackson2JsonRedisSerializer.setObjectMapper(mapper);
// template.setKeySerializer(RedisSerializer.string());
// template.setHashKeySerializer(RedisSerializer.string());
// template.setValueSerializer(jackson2JsonRedisSerializer);
// template.setHashValueSerializer(jackson2JsonRedisSerializer);
//FastJson序列化--key采用String的序列化方式,value采用FastJson的序列化方式
GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
redis工具类
package com.excesys.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* Redis工具类
*/
@Component
public final class RedisUtils {
private final static Logger log = LoggerFactory.getLogger(RedisUtils.class);
@Resource
private RedisTemplate<String, Object> redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
*
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
*
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
*
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
*
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
*
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
*
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
*
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*;
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
*
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, long time) {
try {
redisTemplate.opsForHash().put(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
*
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, double by) {
return redisTemplate.opsForHash().increment(key, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
*
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* @param prefix 前缀
* @param ids id
*/
public void delByKeys(String prefix, Set<Long> ids) {
Set<Object> keys = new HashSet<>();
for (Long id : ids) {
keys.addAll(redisTemplate.keys(new StringBuffer(prefix).append(id).toString()));
}
Boolean count = redisTemplate.delete(String.valueOf(keys));
// 此处提示可自行删除
log.info("--------------------------------------------");
log.info("成功删除缓存:" + keys.toString());
log.info("缓存删除数量:" + count + "个");
log.info("--------------------------------------------");
}
/**
* @param prefix 前缀
*/
public void delByKeys(String prefix) {
Set keys = redisTemplate.keys(prefix + "*");
long count = redisTemplate.delete(keys);
// 此处提示可自行删除
log.info("--------------------------------------------");
log.info("成功删除缓存:" + keys.toString());
log.info("缓存删除数量:" + count + "个");
log.info("--------------------------------------------");
}
}
订单实体类
package com.excesys.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductOrder implements Serializable {
@TableId(type = IdType.AUTO)
private Long id;
/**
* 商户订单号
*/
@TableField("out_trade_no")
private String out_trade_no;
/**
* 订单名称
*/
private String subject;
/**
* 付款金额
*/
@TableField("total_amount")
private Double total_amount;
/**
* 订单状态
*/
@TableField("order_status")
private Integer order_status;
/**
* 产品编号
*/
private String product_code = "FAST_INSTANT_TRADE_PAY";
/**
* 创建时间
*/
private LocalDateTime create_time;
}
Dao(数据访问层)
package com.excesys.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.excesys.pojo.ProductOrder;
//这里没有mapper注解是因为启动类已经开启扫描
public interface ProductOrderMapper extends BaseMapper<ProductOrder> {
}
service及其实现(业务逻辑层)
package com.excesys.service;
import com.alipay.api.AlipayApiException;
import com.excesys.pojo.ProductOrder;
public interface AliPayService {
/**
* 支付宝支付接口
*/
String aliPay(ProductOrder order) throws AlipayApiException;
}
package com.excesys.service.impl;
import com.alipay.api.AlipayApiException;
import com.excesys.bean.AliPay;
import com.excesys.pojo.ProductOrder;
import com.excesys.service.AliPayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AliPayServiceImpl implements AliPayService {
@Autowired
private AliPay aliPay;
@Override
public String aliPay(ProductOrder order) throws AlipayApiException {
System.out.println("order----service--:" + order);
return aliPay.pay(order);
}
}
Controller(控制层)
package com.excesys.controller;
import cn.hutool.core.util.IdUtil;
import com.alipay.api.AlipayApiException;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.excesys.mapper.ProductOrderMapper;
import com.excesys.pojo.ProductOrder;
import com.excesys.service.AliPayService;
import com.excesys.util.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
@RestController
public class AliPayController {
@Autowired
private AliPayService aliPayService;
@Autowired
private RedisUtils redisUtils;
@Resource
private ProductOrderMapper productOrderMapper;
/**
* 进入支付主页
*
* @return
*/
@GetMapping("/")
public ModelAndView index() {
ModelAndView mav = new ModelAndView();
mav.setViewName("testAliPay");
return mav;
}
/**
* 提交订单
*
* @param subject
* @param totalAmount
* @return
* @throws AlipayApiException
*/
@PostMapping("/order/alipay")
public String aliPay(String subject, Double totalAmount) throws AlipayApiException {
System.out.println("--------进入提交支付----------");
ProductOrder order = new ProductOrder();
order.setOut_trade_no(IdUtil.getSnowflakeNextIdStr());
order.setSubject(subject);
order.setTotal_amount(totalAmount);
order.setCreate_time(LocalDateTime.now());
productOrderMapper.insert(order);
//同时将订单信息存入redis并设置过期时间
redisUtils.set(order.getOut_trade_no(), order, 60);
//如果在过期时间内订单未支付则删除redis中的订单信息
System.out.println("order-----:" + order);
return aliPayService.aliPay(order);
}
/**
* 支付成功
*
* @return
*/
@GetMapping("/paySuccess")
public ModelAndView paySuccess(HttpServletRequest request) {
System.out.println("--------支付成功----------");
//如果支付成功,订单就要新增到数据库
//如果取消支付,也要新增订单到数据库,取消订单-状态
//订单查询能够查出订单状态 信息
String out_trade_no = request.getParameter("out_trade_no");
UpdateWrapper uw = new UpdateWrapper();
uw.eq("out_trade_no", out_trade_no);
uw.set("order_status", 1);
int flag = productOrderMapper.update(new ProductOrder(), uw);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("out_trade_no", out_trade_no);
modelAndView.setViewName("success");
System.out.println("支付成功" + flag);
return modelAndView;
}
}
mapper(mybatis-plus对单表操作来说xml可以省略)
testAliPay.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>订单支付</title>
</head>
<body>
<table border="1" style="margin: auto;">
<form th:action="@{/order/alipay}" th:method="post" style="margin: auto;">
<tr>
<td>商品名称:</td>
<td>
<input type="text" name="subject" value="">
</td>
</tr>
<tr>
<td>商品价格:</td>
<td>
<input type="text" name="totalAmount" value="">
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center;">
<input type="submit" value="立即支付">
</td>
</tr>
</form>
</table>
</body>
</html>
success.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>支付成功</title>
</head>
<body>
<h2><span th:text="${ out_trade_no } "></span>支付成功</h2>
</body>
</html>
启动类
package com.excesys;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableAsync
@EnableScheduling
@SpringBootApplication
@MapperScan("com.excesys.mapper")
public class SpringbootPayServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootPayServiceApplication.class, args);
}
}
订单超时处理的定时任务
package com.excesys.task;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.excesys.mapper.ProductOrderMapper;
import com.excesys.pojo.ProductOrder;
import com.excesys.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
@Slf4j
@Component
public class MyOrderTask {
@Resource
private ProductOrderMapper productOrderMapper;
@Autowired
private RedisUtils redisUtils;
@Scheduled(cron = "0 0/1 * * * ?")
public void orderSync() {
QueryWrapper<ProductOrder> qw = new QueryWrapper<>();
List<ProductOrder> productOrders = productOrderMapper.selectList(qw);
for (int i = 0; i < productOrders.size(); i++) {
//如果在过期时间内订单未支付则删除redis中的订单信息
long expire = redisUtils.getExpire(productOrders.get(i).getOut_trade_no());
log.info(productOrders.get(i).getOut_trade_no() + "过期时间" + expire);
Integer order_status = productOrders.get(i).getOrder_status();
if (expire <= 0 && order_status == 0) {
redisUtils.del(productOrders.get(i).getOut_trade_no());
UpdateWrapper uw = new UpdateWrapper();
uw.eq("out_trade_no", productOrders.get(i).getOut_trade_no());
uw.set("order_status", 3);
int flag = productOrderMapper.update(new ProductOrder(), uw);
log.info(productOrders.get(i).getOut_trade_no() + "超时订单修改" + flag);
}
}
}
}
效果
启动主启动类访问http://localhost:8081/
填入订单信息,此时数据库生成一条订单数据状态为0(未支付)
输入沙箱买家信息和密码并支付
支付成功商家加钱买家减钱,数据库此时订单状态变成1(已支付)
继续访问http://localhost:8081/并填写订单信息,每条订单信息都会放入Redis缓存起来
超出一段时间不进行支付操作(作者这里是1分钟),则定时任务会扫描到该订单并先删除Redis数据之后再修改数据库订单状态为3(超时)
写在最后:上面就是一个订单和支付业务,但是是不在高并发状态下的简单实现机制,如果是在高并发状态下大厂的选择会更多(核心是锁),难怪12306要请阿里给他们重构系统,年关将近,祝大家阖家欢乐,万事如意,路漫漫其修远兮,吾将上下而求索!~
原文地址:https://blog.csdn.net/chengxuyuanWJ/article/details/135994647
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。