目录
项目源码:GitHub - Atopos-suyu/dewu: 得物后端模块
三、订单模块
1、判断用户是否登录
我们在用户购买商品的时候要判断用户是否登录,所以我们要在UserApi添加一个接口判断用户是否登录
第一步我们完成USerService的开发
public interface UserService {
/**
* 判断是否登录
*
* @param session
* @return
*/
public Boolean checkLogin(HttpSession session);
}
第二步我们完成USerApi的开发
- URL:
Get /api/user/checklogin
- Request Param:
"request":"HttpServletRequest"
- Response:
Result<Boolean>
2、订单模型设计
我们要开发完整的购物系统也必须要有订单
第一步我们要完成订单的模型设计(主要有以下思路)
1.存储主要信息
2.能够从订单信息看出这个人购买情况(购买了什么商品,购买时间等)
通常有订单号,订单价格,订单的状态等这些不可缺少的信息,根据业务的实际场景来分析
模型设计如下:
1.完成右侧工程model包的Order模型创建(对应领域模型)
//对订单模块属性进行封装
@Data
public class Order {
/**
* 主键id
*/
private String id;
/**
* 订单编号
*/
private String orderNumber;
/**
* 用户id
*/
private String userId;
/**
* 用户模型
*/
private User user;
/**
* 商品
*/
private String productDetailId;
/**
* 商品
*/
private ProductDetail productDetail;
/**
* 订单总价格
*/
private Double totalPrice;
/**
* 订单状态
*/
private OrderStatus status;
/**
* 创建时间
*/
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private Date gmtCreated;
/**
* 修改时间
*/
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private Date gmtModified;
2.完成右侧工程dataobject包的OrderDO的创建(对应数据库的表结构)
@Data
public class OrderDO {
/**
* 主键id
*/
private String id;
/**
* 订单编号
*/
private String orderNumber;
/**
* 用户id
*/
private String userId;
/**
* 商品id
*/
private String productDetailId;
/**
* 订单总价格
*/
private Double totalPrice;
/**
* 订单状态
*/
private String status;
/**
* 创建时间
*/
private Date gmtCreated;
/**
* 修改时间
*/
private Date gmtModified;
3.在OrderDO类完成Order和OrderDO的模型转换方法
//构造函数将Order对象转换为OrderDO对象
public OrderDO(Order order) {
BeanUtils.copyProperties(order, this); //将Order对象的属性复制给OrderDO对象
if (order.getStatus() != null) {
this.setStatus(order.getStatus().toString()); //将order对象的状态转换为字符串并设置给当前对象this的状态属性
}
}
public Order convertToModel() {
Order order = new Order();
BeanUtils.copyProperties(this, order);
if (!StringUtils.isEmpty(this.getStatus())) {
order.setStatus(OrderStatus.valueOf(this.getStatus())); //将状态属性值从字符串类型转换为枚举类型并设置给order对象的状态属性
}
return order;
}
}
完善右侧工程的dao包下的OrderDAO接口里面的insert方法实现
@Mapper
public interface OrderDAO {
int insert(OrderDO order);
int selectCounts(QueryOrderParam param);
List<OrderDO> pageQuery(QueryOrderParam param);
OrderDO selectByOrderNumber(String orderNumber);
int update(OrderDO orderDO);
}
3、下单服务
在用户选择好购买的产品的时候,我们会为该用户生成一条这个商品的订单(简单来说就是往数据库插入一条购买记录)
组成订单记录
1.用户选择商品的Id
2.用户ld
完成下单服务,设计一下OrderService
public interface OrderService {
/**
* 下单
*
* @param order 接收的Order模型
* @return
*/
public Order add(Order order);
}
完成OrderService实现类OrderServicelmpl的创建
@Service
public class OrderServiceImpl implements OrderService {
@Override
public Order add(Order order) {
if (order == null) {
return null;
}
order.setId(UUIDUtils.getUUID());
order.setStatus(OrderStatus.WAIT_BUYER_PAY); //表示待买家付款
//生成唯一订单号
order.setOrderNumber(createOrderNumber());
OrderDO orderDO = new OrderDO(order);
int insert = orderDAO.insert(orderDO); //插入订单数据到数据库中
if (insert == 1) {
return order;
}
return null;
}
}
开发ProductDetailService新增一个接口根据ProductDetail
public interface ProductDetailService {
/**
* 添加或者删除商品详情
*
* @param productDetail 商品详情
* @return int
*/
ProductDetail save(ProductDetail productDetail);
}
主键Id查询ProductDetail
/**
* 根据主键id查询订单详情信息
*
* @param id 主键id
* @return ProductDetail
*/
ProductDetail get(String id);
OrderAPI定义
添加订单服务
- URL:
Post /api/order/add
- Request Param:
"order'":"Order模型"
- Response:
Result<order>
请根据上面的服务描述,完成API开发
注意要在api判断登录信息获取userld放到订单里面
@Controller
@RequestMapping(path = "/api/order")
public class OrderApi {
@Resource
private OrderService orderService;
/**
* 生成订单Api。将订单信息添加到数据库中,并返回包含操作结果和订单信息的Result对象
*
* @return Result
*/
@PostMapping(path = "/add")
@ResponseBody
public Result<Order> payOrder(@RequestBody Order order) {
Result<Order> result = new Result();
result.setSuccess(true);
if (order == null) {
result.setSuccess(false);
result.setMessage("order is null");
return result;
}
Order orderResult = orderService.add(order); //将订单信息添加到数据库中,并将返回的订单对象赋给orderResult
result.setData(orderResult);
return result;
}
/**
* 查询支付成功订单APi
*
* @return Result
*/
@GetMapping(path = "/queryrecentpaysuccess")
@ResponseBody
public Result<Paging<Order>> queryRecentPaySuccess(@RequestBody QueryOrderParam queryOrderParam) {
Result<Paging<Order>> result = new Result();
result.setSuccess(true);
if (queryOrderParam == null) {
result.setSuccess(false);
result.setMessage("queryOrderParam is null");
return result;
}
result.setData(orderService.queryRecentPaySuccess(queryOrderParam));
return result;
}
}
4、订单查询服务
这些信息分别存储在user表,productDetail表,order表
大体步骤:
1、我们这个服务应该是一个分页服务,如果订单过多,一次性全部查出会导致接口很慢
2、接收的参数分析,(要查询某一个商品最近的购买记录,近多少天的购买记录)通过这两个条件我们可以分析出参数是(一个商品Id,和一个时间)
3、要先查寻最近购买的订单
4、根据定单的商品d查询出这些商品
5、根据订单的用户1d查询出这些用户
6、将商品用户组装到对应的订单模型里返回给前端
流程图:
完成订单服务,设计一下OrderService
public interface OrderService {
/**
* 查询订单
*
* @param param 查询参数
* @return
*/
public Paging<Order> queryRecentPaySuccess(QueryOrderParam param);
}
1.完成QueryOrderParam参数类的创建
//用于表示查询订单的参数 继承自Paging类表示该类还具有分页相关的属性和行为,可以实现对查询结果进行分页展示
@Data
public class QueryOrderParam extends Paging {
/**
* 商品Id
*/
private String productDetailId;
/**
* 查询近几天的购买记录
*/
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
private Date time;
2.实现OrderServicelmpl里面的方法提示
@Override
public Paging<Order> queryRecentPaySuccess(QueryOrderParam queryOrderParam) {
}
3.要在ProductDetailService开发一个接口(根据多个商品详情主键Id查询出多个商品详情)
public interface ProductDetailService {
/**
* 获取多个商品详情
*
* @param productDetailIds 查询参数
* @return
*/
public List<ProductDetail> queryProductDetail(List<String> productDetailIds);
}
4.要在UserService开发一个接口(根据多个Userld查询多个用户)
public interface UserService {
/**
* 获取多个用户信息
*
* @param userIds 查询参数
* @return
*/
public List<User> queryUser(List<Long> userIds);
}
OrderApi的查询服务
/**
* 查询支付成功订单APi
*
* @return Result
*/
@GetMapping(path = "/queryrecentpaysuccess")
@ResponseBody
public Result<Paging<Order>> queryRecentPaySuccess(@RequestBody QueryOrderParam queryOrderParam) {
Result<Paging<Order>> result = new Result();
result.setSuccess(true);
if (queryOrderParam == null) {
result.setSuccess(false);
result.setMessage("queryOrderParam is null");
return result;
}
result.setData(orderService.queryRecentPaySuccess(queryOrderParam));
return result;
}
四、支付模块
1、支付宝支付流程介绍
扫码 -> 确认付款 -> 支付完成
- 支付宝调用时序图:
支付宝调用流程图:
用户、商户系统和支付宝系统关系
- 用户
就是我们操作App的人。
- 商户系统
就是App上入驻的商家店铺提供日常服务的平台系统,例如淘宝商家依托淘宝平台系统、饿了么依托饿了么平台系统等等。
- 支付宝系统
就是提供支付宝支付的功能系统,支付宝相关功能都通过支付宝系统来实现。
三者联系:
- 用户在商户系统中执行相关操作。
- 商户系统集成支付宝系统,使其具备支付功能。
- 支付宝系统反馈信息给商家系统,实时同步支付状态给商户系统。
return._url和notify_url介绍
- return_url
return_url是指商户系统调用支付系统成功支付后,支付宝系统反馈给商户系统的前端页面的展示地址。例如我们通常用支付宝支付完成后有个支付成功页面。这个页面地址就是return_url地址。
- notify_url
notify_.url是指商户系统调用支付系统成功支付后,支付宝系统反馈给商户系统的后端接口回调地址。例如在我们支付成功之后会更新我们的订单状态等等。这些更新一系列操作就是在后端回调接口中执行的。
2、支付宝支付模型个绍
请求和响应概要模型介绍
- 支付宝手机网站支付请求的概要模型:
- 支付宝手机网站支付响应的概要模型:
3、得物支付模型设计
支付请求模型:
支付流水记录模型:
//展示了一个枚举类型PayType,定义两个枚举常量,防止输入错误
public enum PayType {
/**
* 微信
*/
WEIXIN,
/**
* 支付宝
*/
ALIPAY
}
//对支付记录各属性值进行封装
@Data
public class PaymentRecord {
/**
* 主键id
*/
private String id;
/**
* 用户id
*/
private String userId;
/**
* 订单号
*/
private String orderNumber;
/**
* 外部支付渠道主键id
*/
private String channelPaymentId;
/**
* 渠道类型
*/
private String channelType;
/**
* 支付金额
*/
private Double amount;
/**
* 支付类型
*/
private PayType payType;
/**
* 支付状态
*/
private PaymentStatus payStatus;
/**
* 订单额外信息
*/
private String extendStr;
/**
* 支付完成时间
*/
private String payEndTime;
/**
* 创建时间
*/
private Date gmtCreated;
/**
* 修改时间
*/
private Date gmtModified;
//定义一个枚举类型 PaymentStatus,表示支付状态
public enum PaymentStatus {
/**
* 支付中
*/
PENDING,
/**
* 支付成功
*/
SUCCESS,
/**
* 支付失败
*/
FAILURE,
/**
* 退款成功
*/
REFUND_SUCCESS,
/**
* 退款失败
*/
REFUND_FAILED
}
4、得物支付表结构设计
创建表名
我们表名一般我们采用公司名缩写或者项目名缩写加上下划线在加上对应的数据库名例如我们将流水记录表名定义为:ykd_payment_record
表结构特征:
5、支付宝SDK配置
什么是SDK?
SDK一般是一些被软件工程师用于为特定的软件包、软件框架、硬件平台等建立应用软件的开发工具的集合。支付宝系统也为开发者提供了对应的SDK,同学们在开发具体功能之前需要集成一下SDK环境
如何集成SDK环境?
我们集成SDK环境主要分为以下几步:
- 引入对应依赖文件
- 实例化客户端
- 组装Request,,调用接口生成表单
引入依赖文件
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.10.38.ALL</version>
</dependency>
实例化客户端
@Service
public class PayServiceImpl{
@Value("${alipay.app.id:2021001172660270}")
private String aliPayAppId;
@Value("${alipay.app.privatekey:MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCnv9kfis56VG2XVZxAq5S10aEumZgV3VvoOD5VdpMrd9wfRuRRI7fMsDB0HXo5uHkJZ+SXzaJh1CQl5R6HrSX64EZy9/5sHyFRKabPY4Nxrrzi++tS4C/SFYYT6TYEKAzMk7wWBP0NvFhbNfVpne8dE6K98NYLkz5kAXXHHQWyPQqxTDN/Iq6+0ZJa6o+7JXYO3CEC8+qEvYCo478G9PA0ooWvEVargdHPavXBwHH0TfXyxwS3prG2LUmhf+a9qUxbtN1BKsujtm+ff//S5RY1TDwwhR0GnLUGZKlf+WeYJjIBAHUX+zEPOm9FRixdIeHgnmF3cAuGb+R9jAenxVTBAgMBAAECggEAO0p398ocCOjmg2LjA4ih21Ho4ouvUasX3RBkF9j9U5Pd3cA02ukBAfwUZDY3CUfGoCh0h6NLDcDptesxy0rL7cxvmhtFdfna0NEkAJFv2DKm2KOqHXTX8i1hYpA/Y2C0hWqCRFYnCz/TCwobX+VOqrxR/UiunxDAMKDDfEkpxkFw8A8tiQBay+ZlGmUpydc4Hl0/26lpKnxPWypfhIFVB8zEwWnl8OGMosPCeMa0ByXloDbKo4GMMGTpw9HvatbhCSZPWmLIcytZI61e/7ziGsIP2sAfsnHhNg7W88AAGzvZV98S305+98+6ZUtk1Fv6gwQBMTwn0UiFuqc0ALd+sQKBgQDwpxYO/R4Bw/4gUj9//5pcycgCRo7Gr2TO7fPq9Fq5uAIMrnpi0e+AX4dY+sNEoCTAqwRGpdvcXBsj/AWsenYqiKwWfqxIWQNvZnf/xxsJe1TLk4BYmqAYXevmHGV1/SoUXgsfiEyPKu43VTCanXc/l46dvUFAkwBUHv/ttuiUowKBgQCycomhkPBubLumuCSgm79q5tv/VtayiH/L81NDcPlUDGkqBnvnwDPgTyCxMd4L+gCphSiyBRpdQ/SX3jKfFsp/JN4n37Dr0ZNYeENXCEEIh+D0QFyqwO8tTXMuevryUjisHgZSy004Y7chNFHBUVFwCJ/2xaldqA9WVdQKSuqjSwKBgGONS0PCE/K5CFyIibpCm7G8y1+dnpy0m+g6aYgNs6ZWZ4qldv2ASSp62jvF6JdwBCQr2uX64Nvkwll9fT9fnZh013OqzUxUfmZMJmIKFLY0bdyVVSfSN149JEQaBSLtKsYoLUPLF5i2MrtzI1sivtzwrk+0pdS3uxARjt/gpZAvAoGBALBEM29EnDp3bWThwExljE8se2Ndg/YWnyX21OhpT9+V4suAXCQv1w5bGw/tEkkCSmUpA2nVYJV/6ruY4KgE+0FcSZVZgIlwGvvoz5vIq3Shw7OBYAfLTHaTapMfJ4L1dMWPYu+lokFxPhOuepNan/bqjhhUZ1f8Cipd3XXNSrjBAoGAQ2pc7xh1jxCTTtN+EmxQRGjwid0AcDEZAn4m00mCFkRhvVfa4Df5GcAgNajTBsttdi/DXtu216whQ7ceYKAPsKzEO4w7R7DoMzhnL9NqNtL9uogwwYO72uPEVn4c+hQS/qFRmSsfIkCrT2J5sGuXU7+4gUO8V8Ox+RIOt6kkG6Y=}")
private String aliPayAppPrivateKey;
@Value("${alipay.publickey:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkJAtCWlSvkZ05srfAmvOt/XU701GmpF3aO7XozmmZbzjTOUAcc8BzrAsqIeXJVOPRJz75fCVZ6rcsx4P2PWGHCoB293RPJpBnDT1VBVMq7k8Hw9VOJRuq56L0PZxtVHYjUA8i4vUmXc8j5K4rLGp+PC9VqNVJpj8Njv2R3ZeLndAd0B1//73SfKRSRZMNoPAl/XPSY7MAfGLzNm3B3FPVbJIEt9TM+/ijtlLpzTFCDLaLvy8EFsvoZwgpVkbxT9iRFvFWomz29/oH4xkSsZFaTMeswPUyERoMXhqMmW8hmVT/yBjiEx/NNC32Bu0Ic4DD01JZXDr/jDDjh1IA2uYNQIDAQAB}")
private String aliPayPublicKey;
public Result aliPay(String orderId, PaymentParam paymentParam) {
Result result = new Result();
result.setSuccess(true);
//实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", this.aliPayAppId,
this.aliPayAppPrivateKey, "json", "UTF-8",
this.aliPayPublicKey, "RSA2");
}
}
上面就是初始化支付宝客户端的实例方法,第一个参数是支付宝网关地址,第二个参数是APPID,在生成应用时会提供,这边我们申请好了,第三个参数表示应用公钥,第四个参数表示格式是JSON格式,第五个参数表名编码格式是UTF-8,第六个参数是支付宝公钥,第七个表示签名方式
组装Request,调用接口生成表单
@Service
public class PayServiceImpl{
private static final Logger logger = LoggerFactory.getLogger(PayServiceImpl.class);
private ObjectMapper objectMapper = new ObjectMapper();
@Value("${alipay.app.id:2021001172660270}")
private String aliPayAppId;
@Value("${alipay.app.privatekey:MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCnv9kfis56VG2XVZxAq5S10aEumZgV3VvoOD5VdpMrd9wfRuRRI7fMsDB0HXo5uHkJZ+SXzaJh1CQl5R6HrSX64EZy9/5sHyFRKabPY4Nxrrzi++tS4C/SFYYT6TYEKAzMk7wWBP0NvFhbNfVpne8dE6K98NYLkz5kAXXHHQWyPQqxTDN/Iq6+0ZJa6o+7JXYO3CEC8+qEvYCo478G9PA0ooWvEVargdHPavXBwHH0TfXyxwS3prG2LUmhf+a9qUxbtN1BKsujtm+ff//S5RY1TDwwhR0GnLUGZKlf+WeYJjIBAHUX+zEPOm9FRixdIeHgnmF3cAuGb+R9jAenxVTBAgMBAAECggEAO0p398ocCOjmg2LjA4ih21Ho4ouvUasX3RBkF9j9U5Pd3cA02ukBAfwUZDY3CUfGoCh0h6NLDcDptesxy0rL7cxvmhtFdfna0NEkAJFv2DKm2KOqHXTX8i1hYpA/Y2C0hWqCRFYnCz/TCwobX+VOqrxR/UiunxDAMKDDfEkpxkFw8A8tiQBay+ZlGmUpydc4Hl0/26lpKnxPWypfhIFVB8zEwWnl8OGMosPCeMa0ByXloDbKo4GMMGTpw9HvatbhCSZPWmLIcytZI61e/7ziGsIP2sAfsnHhNg7W88AAGzvZV98S305+98+6ZUtk1Fv6gwQBMTwn0UiFuqc0ALd+sQKBgQDwpxYO/R4Bw/4gUj9//5pcycgCRo7Gr2TO7fPq9Fq5uAIMrnpi0e+AX4dY+sNEoCTAqwRGpdvcXBsj/AWsenYqiKwWfqxIWQNvZnf/xxsJe1TLk4BYmqAYXevmHGV1/SoUXgsfiEyPKu43VTCanXc/l46dvUFAkwBUHv/ttuiUowKBgQCycomhkPBubLumuCSgm79q5tv/VtayiH/L81NDcPlUDGkqBnvnwDPgTyCxMd4L+gCphSiyBRpdQ/SX3jKfFsp/JN4n37Dr0ZNYeENXCEEIh+D0QFyqwO8tTXMuevryUjisHgZSy004Y7chNFHBUVFwCJ/2xaldqA9WVdQKSuqjSwKBgGONS0PCE/K5CFyIibpCm7G8y1+dnpy0m+g6aYgNs6ZWZ4qldv2ASSp62jvF6JdwBCQr2uX64Nvkwll9fT9fnZh013OqzUxUfmZMJmIKFLY0bdyVVSfSN149JEQaBSLtKsYoLUPLF5i2MrtzI1sivtzwrk+0pdS3uxARjt/gpZAvAoGBALBEM29EnDp3bWThwExljE8se2Ndg/YWnyX21OhpT9+V4suAXCQv1w5bGw/tEkkCSmUpA2nVYJV/6ruY4KgE+0FcSZVZgIlwGvvoz5vIq3Shw7OBYAfLTHaTapMfJ4L1dMWPYu+lokFxPhOuepNan/bqjhhUZ1f8Cipd3XXNSrjBAoGAQ2pc7xh1jxCTTtN+EmxQRGjwid0AcDEZAn4m00mCFkRhvVfa4Df5GcAgNajTBsttdi/DXtu216whQ7ceYKAPsKzEO4w7R7DoMzhnL9NqNtL9uogwwYO72uPEVn4c+hQS/qFRmSsfIkCrT2J5sGuXU7+4gUO8V8Ox+RIOt6kkG6Y=}")
private String aliPayAppPrivateKey;
@Value("${alipay.publickey:MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkJAtCWlSvkZ05srfAmvOt/XU701GmpF3aO7XozmmZbzjTOUAcc8BzrAsqIeXJVOPRJz75fCVZ6rcsx4P2PWGHCoB293RPJpBnDT1VBVMq7k8Hw9VOJRuq56L0PZxtVHYjUA8i4vUmXc8j5K4rLGp+PC9VqNVJpj8Njv2R3ZeLndAd0B1//73SfKRSRZMNoPAl/XPSY7MAfGLzNm3B3FPVbJIEt9TM+/ijtlLpzTFCDLaLvy8EFsvoZwgpVkbxT9iRFvFWomz29/oH4xkSsZFaTMeswPUyERoMXhqMmW8hmVT/yBjiEx/NNC32Bu0Ic4DD01JZXDr/jDDjh1IA2uYNQIDAQAB}")
private String aliPayPublicKey;
public Result aliPay(String orderId, PaymentParam paymentParam) {
Result result = new Result();
result.setSuccess(true);
//实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", this.aliPayAppId,
this.aliPayAppPrivateKey, "json", "UTF-8",
this.aliPayPublicKey, "RSA2");
//创建API对应的request
AlipayTradeWapPayRequest request = getAlipayTradeWapPayRequest(orderId, paymentParam);
AlipayTradeWapPayResponse response = null;
try {
//调用SDK生成表单
response = alipayClient.pageExecute(request);
} catch (AlipayApiException e) {
logger.error("e is:", e);
}
if (response.isSuccess()) {
logger.error("支付成功:");
}
return result;
}
/**
* 组装支付宝支付参数
*
* @param orderId 订单号
* @param paymentParam 支付参数
* @return AlipayTradeWapPayRequest
*/
private AlipayTradeWapPayRequest getAlipayTradeWapPayRequest(String orderId, PaymentParam paymentParam) {
Map<String, Object> bizContentMap = new HashMap<>();
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
//api版本号
request.setApiVersion("1.0");
//返回的url
request.setReturnUrl("");
//通知的url
request.setNotifyUrl("");
//订单号
bizContentMap.put("out_trade_no", orderId);
//总金额
bizContentMap.put("total_amount", paymentParam.getPayAmount());
//退出的url
bizContentMap.put("quit_url", "www.youkeda.com");
//产品码
bizContentMap.put("product_code", "QUICK_WAP_WAY");
try {
request.setBizContent(objectMapper.writeValueAsString(bizContentMap));
} catch (JsonProcessingException e) {
logger.error("e is:", e);
}
return request;
}
}
return_url 和 notify_url 暂时先不设置,这个在后面我们会单独设置对应内容。
6、得物支付接口实现
对应接口类图
完成支付接口相关Service层服务
订单Service新增接口
public interface PayService {
/**
* 支付订单
*
* @param paymentParam 支付所需参数
* @return Result
*/
Result payOrder(PaymentParam paymentParam);
public interface PaymentRecordService {
/**
* 添加或修改支付记录
*
* @return PaymentRecord
*/
PaymentRecord save(PaymentRecord paymentRecord);
/**
* 查询支付记录
*
* @param queryParam 查询参数
* @return List<PaymentRecord>
*/
List<PaymentRecord> query(PaymentRecordQueryParam queryParam);
//方便地封装支付记录的查询参数
@Data
public class PaymentRecordQueryParam {
/**
* 用户id
*/
private String userId;
/**
* 订单号
*/
private String orderNumber;
/**
* 支付类型
*/
private PayType payType;
/**
* 支付状态
*/
private PaymentStatus paymentStatus;
public interface OrderService {
/**
* 查询订单
*
* @param queryOrderParam 查询参数
* @return
*/
Paging<Order> queryRecentPaySuccess(QueryOrderParam queryOrderParam);
}
@Controller
@RequestMapping(path = "/api/alipay")
public class PaymentApi {
@PostMapping(path = "/pay")
@ResponseBody
public Result payOrder(@RequestBody PaymentParam paymentParam) {
Result result = new Result();
result.setSuccess(true);
if (StringUtils.isEmpty(paymentParam.getUserId())) {
result.setSuccess(false);
result.setMessage("userId is null");
return result;
}
if (paymentParam.getPayAmount() <= 0) {
result.setMessage("支付金额不能小于0");
return result;
}
PaymentRecord paymentRecord = initPaymentRecord(paymentParam);
PaymentRecord saveResult = paymentRecordService.save(paymentRecord);
if (saveResult == null) {
logger.error("支付流水记录插入失败!");
}
return payService.payOrder(paymentParam);
} //接受支付参数,验证参数的合法性,保支付记录,调用支付服务进行订单支付操作并返回支付结果
}
//param包主要是前端向后端的自定义对象
public class PaymentParam {
/**
* 订单号
*/
private String orderNumber;
/**
* 用户Id
*/
private String userId;
/**
* 总金额,单位分
*/
private double totalAmount;
/**
* 待支付金额
*/
private double payAmount;
/**
* 支付类型
*/
private PayType payType = PayType.ALIPAY;
7、得物支付回调接口实现
- return_url
之前我们介绍了return_url的意思,它通常是返回支付宝支付成功页面地址
@Controller
@RequestMapping(path = "/controller/pay")
public class PaymentController {
@GetMapping(path = "/alipayreturnurl")
String alipayReturnUrl() {
return "alipayurl";
}
}
- notify._url
我们知道notify_url表示支付完成之后支付宝回调给服务器的地址,它需要在服务器上配置对应的地址内容
@Controller
@RequestMapping(path = "/controller/pay")
public class PaymentController {
@PostMapping(path = "/alipaycallback")
void alipaycallback(HttpServletRequest request, HttpServletResponse response) {
}
public interface PayService {
/**
* 支付订单
*
* @param paymentParam 支付所需参数
* @return Result
*/
Result payOrder(PaymentParam paymentParam);
/**
* 支付宝回调接口
*
* @param mapParam 支付宝参数
* @return Result
*/
Result alipayCallBack(Map<String, String> mapParam);
}
@Controller
@RequestMapping(path = "/controller/pay")
public class PaymentController {
private static final Logger logger = LoggerFactory.getLogger(PaymentController.class);
@Resource
private PayService payService;
@GetMapping(path = "/alipayreturnurl")
String alipayReturnUrl() {
return "alipayurl";
}
@PostMapping(path = "/alipaycallback")
void alipaycallback(HttpServletRequest request, HttpServletResponse response) {
//获取支付宝POST过来反馈信息
Map<String, String> params = new HashMap<String, String>(); //HashMap对象的键值对类型为<String,String> 表示键和值都是字符串类型
Map requestParams = request.getParameterMap(); //返回一个Map类型包含了请求参数中所有参数名和对应的参数值
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = (String)iter.next();
String[] values = (String[])requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
params.put(name, valueStr);
} //将请求参数的多个值拼接成一个字符串,并将参数名和拼接后的值存储在params HashMap中,以便后续处理和验证
Result payResult = payService.alipayCallBack(params);
try {
if (payResult.isSuccess()) {
response.getWriter().print("success");
response.getWriter().flush();//可以立即将缓冲区中的内容发送给客户端,而不需要等到缓冲区满或请求处理完成。这对于需要实时地将内容输出给客户端的情况非常有用
}
} catch (IOException e) {
logger.error("", e);
}
} //处理支付宝回调,解析支付宝返回的参数,调用支付服务进行相应的处理,根据处理结果给予响应
}
8、得物支付结果调整
支付回调后具体流程
新增一个更新订单状态接口
/**
* 更新订单状态
*
* @param orderNumber 订单号
* @param orderStatus 订单状态
* @return Order
*/
Order updateOrderStatus(String orderNumber, OrderStatus orderStatus);
/**
* 更新状态
*
* @param paymentRecord 支付记录
* @return PaymentRecord
*/
PaymentRecord updateStatus(PaymentRecord paymentRecord);
五、项目总结
1、商品整体模型总结
1.项目内容
- 项目如何设计的,有哪些模型、哪些接口?
- 业务流程是什么?
2.项目成果
所谓成果,项目中用什么技术解决了重要、核心、有难度的问题。这个问题,可以是业务问题,也可以是技术问题。
2、订单整体模型总结
一般是先画出有多少类,然后每个类中添加属性和方法,最后画出类与
类之间的关系。