当前位置:网站首页>支付模块实现
支付模块实现
2022-07-31 21:33:00 【Fairy要carry】
目录
场景:
课程分为免费课程和付费课程,如果是免费课程可以直接观看,如果是付费观看的课程,用户需下单支付后才可以观看

2.付费课程流程
2.1如果是付费课程,在用户选择课程,进入到课程详情页面时候,会显示 “立即购买”

2.2 点击“立即购买”,会生成课程的订单,跳转到订单页面

2.3点击“去支付”,会跳转到支付页面,生成微信扫描的二维码

使用微信扫描支付后,会跳转回到课程详情页面,同时显示“立即观看”

支付模块接口实现
1.导入依赖和配置文件并且明确sql表大概思路
<dependencies>
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>1.2两个表一个订单表,当支付成功,订单状态发送改变,并且向支付日志表插入新的数据

1.3配置文件
# 服务端口
server.port=8007
# 服务名
spring.application.name=service-order
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/atguigu/orderservice/mapper/xml/*.xml
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
#开启熔断机制
feign.hystrix.enabled=true
# 设置hystrix超时时间,默认1000ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000创建订单
1.编写控制层(创建订单)
思路:很简单,像订单这种肯定是关于用户和课程的,所以下单的话需要根据课程id和用户id创建订单返回订单id(利用了feign的远程调用)
/**
* 1.生成订单的方法
*/
@GetMapping("createOrder/{courseId}")
public R saveOrder(@PathVariable String courseId, HttpServletRequest request){
//1.生成订单号
String orderNo=orderService.createOrders(courseId,JwtUtils.getMemberIdByJwtToken(request));
return R.ok().data("orderId",orderNo);
}2.生成订单的业务实现
思路:1.首先是通过用户id远程调用用户模块得到用户信息,然后课程模块类似——>2.创建Order对象,向里面添加数据,然后进行插入,返回订单号(这个定义了一个时间类OrderNoUtil根据时间生成)
/**
* 1.生成订单的方法,通过远程调用两个feign方法(课程信息和人物信息)结合到Order中为订单
* @param courseId
* @param memberId
* @return
*/
@Override
public String createOrders(String courseId, String memberId) {
//1.通过远程调用根据用户id获取用户信息
UcenterMemberOrder userInfoOrder = ucenterClient.getUserInfoOrder(memberId);
//2.通过远程调用根据课程id获取课程信息
CourseWebVoOrder courseInfoOrder = eduClient.getCourseInfoOrder(courseId);
//3.创建order对象,向order对象里面设置需要的数据
Order order = new Order();
order.setOrderNo(OrderNoUtil.getOrderNo());
order.setCourseId(courseId); //课程id
order.setCourseTitle(courseInfoOrder.getTitle());
order.setCourseCover(courseInfoOrder.getCover());
order.setTeacherName(courseInfoOrder.getTeacherName());
order.setTotalFee(courseInfoOrder.getPrice());
order.setMemberId(memberId);
order.setMobile(userInfoOrder.getMobile());
order.setNickname(userInfoOrder.getNickname());
order.setStatus(0);//订单状态(0:未支付 1:已支付)
order.setPayType(1);//支付类型 微信1
baseMapper.insert(order);//插入
return order.getOrderNo();
}3.订单号生成类
package com.atguigu.eduorder.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 订单号工具类
*
* @author qy
* @since 1.0
*/
public class OrderNoUtil {
/**
* 获取订单号
* @return
*/
public static String getOrderNo() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
String newDate = sdf.format(new Date());
String result = "";
Random random = new Random();
for (int i = 0; i < 3; i++) {
result += random.nextInt(10);
}
return newDate + result;
}
}
4.远程调用的用户和课程信息
用户:通过request对里面token进行解析得到用户id然后根据id获取用户信息
/**
* 3.根据token获取用户信息
*/
@GetMapping("getMemberInfo")
public R getMemberInfo(HttpServletRequest request){
//getMemberIdByJwtToken根据请求头中的token得到用户信息(token得到userid->取出信息)
String memberId = JwtUtils.getMemberIdByJwtToken(request);
//根据用户id查询用户信息
UcenterMember member = memberService.getById(memberId);
return R.ok().data("userInfo",member);
}
/**
* 2.根据课程id查询课程基本信息
*/
@GetMapping("getCourseInfo/{courseId}")
public R getCourseInfo(@PathVariable String courseId){
CourseInfoVo courseInfoVo=courseService.getCourseInfo(courseId);
return R.ok().data("courseInfo",courseInfoVo);
}根据订单id查询订单信息
/**
* 2.根据订单id查询订单信息
*/
@GetMapping("getOrderInfo/{orderId}")
public R getOrderInfo(@PathVariable String orderId){//订单id查询
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("order_no",orderId);
Order order = orderService.getOne(wrapper);
return R.ok().data("item",order);
}
判断是否购买课程
思路:根据课程id和人物id还有课程状态进行判断,然后根据这些条件进行查询如果>0代表已经支付
@GetMapping("isBuyCourse/{courseId}/{memberId}")
public boolean isBuyCourse(@PathVariable String courseId,@PathVariable String memberId){
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("course_id",courseId);
wrapper.eq("member_id",memberId);
wrapper.eq("status",1);
int count = orderService.count(wrapper);
if(count>0){
//代表已经支付
return true;
}else{
return false;
}
}支付生成微信支付的二维码接口
1.控制层,返回二维码集合
/**
* 1.生成微信支付的二维码接口
*/
@GetMapping("createNative/{orderNo}")
public R createNative(@PathVariable String orderNo) {
//1.返回信息含有二维码的地址还有其他信息
Map map = payLogService.createNative(orderNo);
System.out.println("*****返回二维码map集合****:"+map);
return R.ok().data(map);
}
2.业务实现层
2.1首先根据订单id查询订单信息
2.2然后我们在map设置二维码参数
2.3根据微信提供的固定地址生成HttpClient
2.4因为我们httpClient发送的请求需要时xml格式,所以需要将map转一下
2.5发送请求得到请求返回的结果,然后将结果转为map格式
2.6前面只是将map数据给到http生成对应二维码,我们还有一些提示信息,所以定义一个最终结果map,将之前的map二维码地址,状态码啥的还有订单的价格及相关数据封装到结果集中,然后返回
/**
* 1.返回二维码地址
* @param orderNo
* @return
*/
@Override
public Map createNative(String orderNo) {
try {
//1.根据订单id获取订单信息
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("order_no", orderNo);
Order order = orderService.getOne(wrapper);
//2.使用map设置二维码需要的参数
HashMap map = new HashMap();
map.put("appid","wx74862e0dfcf69954");
map.put("mch_id", "1558950191");
map.put("nonce_str", WXPayUtil.generateNonceStr());//随机生成一个二维码
map.put("body", order.getCourseTitle());
map.put("out_trade_no", orderNo);//二维码标识订单号
map.put("total_fee", order.getTotalFee().multiply(new BigDecimal("100")).longValue()+"");//价格
map.put("spbill_create_ip", "127.0.0.1");
map.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify\n");
map.put("trade_type", "NATIVE");
//3.发送httpclient请求,传递参数按照xml格式,微信支付提供固定地址
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
//设置参数,我们的商户key会对这些map参数进行加密->将map根据key进行加密并且传送到请求中去
client.setXmlParam(WXPayUtil.generateSignedXml(map,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
client.setHttps(true);
//执行请求发送
client.post();
//4.得到发送请求返回的结果,返回的内容是xml格式返回的(之前传参数也是xml格式)
String content = client.getContent();
//5.再把xml转为map
Map<String,String>resultMap=WXPayUtil.xmlToMap(content);
//6.前面的map只是为了给到http生成二维码,需要转为xml格式,现在我们这个结果resultMap就是二维码了
//还需要一些关于订单的提示信息
Map res = new HashMap<>();
res.put("out_trade_no", orderNo);
res.put("course_id", order.getCourseId());
res.put("total_fee", order.getTotalFee());
res.put("result_code", resultMap.get("result_code"));//状态码
res.put("code_url", resultMap.get("code_url"));//二维码地址
return res;
} catch (Exception e) {
throw new GuliException(20001,"生成二维码失败");
}
}支付之后的后续操作
1.控制层
思路:1.支付之后需要查询支付状态(二维码中的参数),如果状态SUCCESS,就将我们的支付记录添加到支付表中——>2.并且支付成功后还要修改订单表的状态,表示已经支付
/**
* 2.查询订单状态
* @param orderNo
* @return
*/
@GetMapping("queryPayStatus/{orderNo}")
public R queryPayStatus(@PathVariable String orderNo){
Map<String,String>map= payLogService.queryPayStatus(orderNo);
System.out.println("返回二维码状态:"+map);
//1.根据查询出来的订单状态进行判断
if(map==null){
return R.error().message("支付出错了...");
}
//2.如果返回的map不为空,从这里面获取订单状态
if(map.get("trade_state").equals("SUCCESS")){//支付成功
//3.添加记录到支付表中,并且更新订单表的状态
payLogService.updateOrdersStatus(map);
return R.ok();
}
return R.ok().code(25000).message("支付中");
}2.业务层
1.还是一样的思路根据orderNo查询订单支付状态queryPayStatus()
,封装订单号微信id密钥,然后设置httpClient发送请求,并且利用商户key进行加密——>2.然后请求之后返回的内容再转为map返回——>3.根据返回map中get的状态决定是否添加支付记录和更新订单状态updateOrdersStatus()方法
/**
* 1.查询订单支付状态
* @param orderNo
* @return
*/
@Override
public Map<String, String> queryPayStatus(String orderNo) {
//1.封装参数
try {
HashMap map = new HashMap();
map.put("appid", "wx74862e0dfcf69954");
map.put("mch_id", "1558950191");
map.put("out_trade_no", orderNo);
map.put("nonce_str", WXPayUtil.generateNonceStr());
//2.设置请求,利用xml进行请求
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
client.setXmlParam(WXPayUtil.generateSignedXml(map,"T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
client.setHttps(true);
client.post();
//3.返回第三方的数据
String xml = client.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);//将xml转为map数据
return resultMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
支付成功后添加支付记录和更新订单状态
1.从map中获取订单号,然后根据订单号得到订单数据,修改订单状态(记得先判断状态提高效率)——>2.然后向支付记录表中设置支付记录
/**
* 2.添加支付记录和更新订单状态
* @param map
*/
@Override
public void updateOrdersStatus(Map<String, String> map) {
//1.从map中获取订单号
String orderNo = map.get("out_trade_no");
//2.根据订单号查询订单信息
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("order_no",orderNo);
Order order = orderService.getOne(wrapper);
//3.更新订单表的订单状态
if(order.getStatus().intValue()==1){
return;//说明支付过了
}
order.setStatus(1);
orderService.updateById(order);
//4.向支付表中添加支付记录
PayLog payLog = new PayLog();
payLog.setOrderNo(orderNo);
payLog.setPayTime(new Date());
payLog.setPayType(1);//支付类型 1wx
payLog.setTotalFee(order.getTotalFee());//总金额(分)
payLog.setTradeState(map.get("trade_state"));//支付状态
payLog.setTransactionId(map.get("transaction_id"));//账单流水号
payLog.setAttr(JSONObject.toJSONString(map));
baseMapper.insert(payLog);//插入到支付日志表
}
边栏推荐
- NVIDIA has begun testing graphics products with AD106 and AD107 GPU cores
- ReentrantLock原理(未完待续)
- Mobile web development 02
- Routing interception of WeChat applet
- 角色妆容的实现
- ECCV 2022 Huake & ETH propose OSFormer, the first one-stage Transformer framework for camouflaging instance segmentation!The code is open source!...
- Returns a zero-length array or empty collection, do not return null
- 基于STM32 环形队列来实现串口接收数据
- npm 更改为淘宝镜像的方法[通俗易懂]
- 全网一触即发,自媒体人的内容分发全能助手——融媒宝
猜你喜欢

The principle of ReentrantLock (to be continued)

Judging decimal points and rounding of decimal operations in Golang

嵌入式开发没有激情了,正常吗?

架构实战营模块八作业

角色妆容的实现

What's wrong with the sql syntax in my sql

Financial profitability and solvency indicators

Made with Flutter and Firebase!counter application

idea中搜索具体的字符内容的快捷方式

【论文精读】iNeRF
随机推荐
leetcode 665. Non-decreasing Array
Short-circuit characteristics and protection of SiC MOSFETs
Structure of the actual combat battalion module eight operations
利用反射实现一个管理对象信息的简单框架
AI automatic code writing plugin Copilot (co-pilot)
【AcWing】第 62 场周赛 【2022.07.30】
Qualcomm cDSP simple programming example (to query Qualcomm cDSP usage, signature), RK3588 npu usage query
Arduino框架下STM32全系列开发固件安装指南
给定一个ip地址,子网掩码怎么算网络号(如何获取ip地址和子网掩码)
Returns a zero-length array or empty collection, do not return null
leetcode 665. Non-decreasing Array
BOW/DOM (top)
PCB stackup design
Made with Flutter and Firebase!counter application
Carbon教程之 基本语法入门大全 (教程)
UVM RAL model and built-in seq
Efficient Concurrency: A Detailed Explanation of Synchornized's Lock Optimization
Transfer Learning - Domain Adaptation
关注!海泰方圆加入《个人信息保护自律公约》
JS basic exercises