1.建表语句,一个支付日志,一个订单表
# Host: localhost (Version 5.7.19)
# Date: 2019-11-18 15:49:50
# Generator: MySQL-Front 6.1 (Build 1.26)
#
# Structure for table "t_order"
#
CREATE TABLE `t_order` (
`id` char(19) NOT NULL DEFAULT '',
`order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号',
`course_id` varchar(19) NOT NULL DEFAULT '' COMMENT '课程id',
`course_title` varchar(100) DEFAULT NULL COMMENT '课程名称',
`course_cover` varchar(255) DEFAULT NULL COMMENT '课程封面',
`teacher_name` varchar(20) DEFAULT NULL COMMENT '讲师名称',
`member_id` varchar(19) NOT NULL DEFAULT '' COMMENT '会员id',
`nickname` varchar(50) DEFAULT NULL COMMENT '会员昵称',
`mobile` varchar(11) DEFAULT NULL COMMENT '会员手机',
`total_fee` decimal(10,2) DEFAULT '0.01' COMMENT '订单金额(分)',
`pay_type` tinyint(3) DEFAULT NULL COMMENT '支付类型(1:微信 2:支付宝)',
`status` tinyint(3) DEFAULT NULL COMMENT '订单状态(0:未支付 1:已支付)',
`is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `ux_order_no` (`order_no`),
KEY `idx_course_id` (`course_id`),
KEY `idx_member_id` (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单';
#
# Data for table "t_order"
#
INSERT INTO `t_order` VALUES ('1195693605513891841','1195693605555834880','18','Java精品课程','http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/06/866e9aca-b530-4f71-a690-72d4a4bfd1e7.jpg','晴天','1','小三1231','13700000001',1,NULL,0,0,'2019-11-16 21:22:25','2019-11-16 21:22:25'),('1195694200178118657','1195694200186507264','18','Java精品课程','http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/06/866e9aca-b530-4f71-a690-72d4a4bfd1e7.jpg','晴天','1','小三1231','13700000001',1,NULL,0,0,'2019-11-16 21:24:47','2019-11-16 21:24:47'),('1196264007411744769','1196264005255872512','1192252213659774977','java基础课程:test','https://guli-file-190513.oss-cn-beijing.aliyuncs.com/cover/default.gif','晴天','1','小三1231','13700000001',1,NULL,1,0,'2019-11-18 11:09:00','2019-11-18 11:10:14'),('1196265495278174209','1196265495273979904','18','Java精品课程','http://guli-file.oss-cn-beijing.aliyuncs.com/cover/2019/03/06/866e9aca-b530-4f71-a690-72d4a4bfd1e7.jpg','晴天','1','小三1231','13700000001',1,NULL,0,0,'2019-11-18 11:14:54','2019-11-18 11:14:54');
#
# Structure for table "t_pay_log"
#
CREATE TABLE `t_pay_log` (
`id` char(19) NOT NULL DEFAULT '',
`order_no` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号',
`pay_time` datetime DEFAULT NULL COMMENT '支付完成时间',
`total_fee` decimal(10,2) DEFAULT '0.01' COMMENT '支付金额(分)',
`transaction_id` varchar(30) DEFAULT NULL COMMENT '交易流水号',
`trade_state` char(20) DEFAULT NULL COMMENT '交易状态',
`pay_type` tinyint(3) NOT NULL DEFAULT '0' COMMENT '支付类型(1:微信 2:支付宝)',
`attr` text COMMENT '其他属性',
`is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付日志表';
#
# Data for table "t_pay_log"
#
INSERT INTO `t_pay_log` VALUES ('1194498446013001730','1194498300579704832','2019-11-13 14:13:17',1,'4200000469201911130676624386','SUCCESS',1,'{\"transaction_id\":\"4200000469201911130676624386\",\"nonce_str\":\"2Lc23ILl231It53M\",\"trade_state\":\"SUCCESS\",\"bank_type\":\"CFT\",\"openid\":\"oNpSGwR-QGG5DaZtDkh2UZlsFDQE\",\"sign\":\"5404850AA3ED0E844DE104651477F07A\",\"return_msg\":\"OK\",\"fee_type\":\"CNY\",\"mch_id\":\"1473426802\",\"cash_fee\":\"1\",\"out_trade_no\":\"1194498300579704832\",\"cash_fee_type\":\"CNY\",\"appid\":\"wx8397f8696b538317\",\"total_fee\":\"1\",\"trade_state_desc\":\"支付成功\",\"trade_type\":\"NATIVE\",\"result_code\":\"SUCCESS\",\"attach\":\"\",\"time_end\":\"20191113141314\",\"is_subscribe\":\"N\",\"return_code\":\"SUCCESS\"}',0,'2019-11-13 14:13:17','2019-11-13 14:13:17'),('1195253787449430017','1195253049260314624','2019-11-15 16:14:44',1,'4200000454201911150981874895','SUCCESS',1,'{\"transaction_id\":\"4200000454201911150981874895\",\"nonce_str\":\"MAM5UM4Xhv1lItvO\",\"trade_state\":\"SUCCESS\",\"bank_type\":\"CFT\",\"openid\":\"oNpSGwR-QGG5DaZtDkh2UZlsFDQE\",\"sign\":\"7DBDCAF4A078B30BB3EF073E6A238C20\",\"return_msg\":\"OK\",\"fee_type\":\"CNY\",\"mch_id\":\"1473426802\",\"cash_fee\":\"1\",\"out_trade_no\":\"1195253049260314624\",\"cash_fee_type\":\"CNY\",\"appid\":\"wx8397f8696b538317\",\"total_fee\":\"1\",\"trade_state_desc\":\"支付成功\",\"trade_type\":\"NATIVE\",\"result_code\":\"SUCCESS\",\"attach\":\"\",\"time_end\":\"20191115161440\",\"is_subscribe\":\"N\",\"return_code\":\"SUCCESS\"}',0,'2019-11-15 16:14:44','2019-11-15 16:14:44'),('1196264321397342210','1196264005255872512','2019-11-18 11:10:14',1,'4200000453201911184025781554','SUCCESS',1,'{\"transaction_id\":\"4200000453201911184025781554\",\"nonce_str\":\"D1dHexCLIFIxAAg2\",\"trade_state\":\"SUCCESS\",\"bank_type\":\"CFT\",\"openid\":\"oNpSGwR-QGG5DaZtDkh2UZlsFDQE\",\"sign\":\"C9F5CA1EE49EA7891736D73BEB423962\",\"return_msg\":\"OK\",\"fee_type\":\"CNY\",\"mch_id\":\"1473426802\",\"cash_fee\":\"1\",\"out_trade_no\":\"1196264005255872512\",\"cash_fee_type\":\"CNY\",\"appid\":\"wx8397f8696b538317\",\"total_fee\":\"1\",\"trade_state_desc\":\"支付成功\",\"trade_type\":\"NATIVE\",\"result_code\":\"SUCCESS\",\"attach\":\"\",\"time_end\":\"20191118111011\",\"is_subscribe\":\"N\",\"return_code\":\"SUCCESS\"}',0,'2019-11-18 11:10:14','2019-11-18 11:10:14');
2.需要接口:
1》生成订单,返回订单的订单编号
import java.util.UUID;
/**
* <p>
* 订单 服务实现类
* </p>
*
* @author zyk
* @since 2023-05-28
*/
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
@Autowired
EduClinent eduClinent;
@Autowired
UserClient userClient;
// 生成订单
@Override
public R saveOrder(String courseId, String userId) {
// 通过远程调用获取用户信息
UcenterMemberOrder userInfoOrder = userClient.getUserInfoOrder(userId);
// 通过远程调用获取课程信息
CourseWebVoOrder courseInfoOrder = eduClinent.queryCourse(courseId);
Order order=new Order();
String orderNo = UUID.randomUUID().toString().substring(0,20);
order.setOrderNo(orderNo);//订单号
order.setCourseId(courseId); //课程id
order.setCourseTitle(courseInfoOrder.getTitle());//价格
order.setCourseCover(courseInfoOrder.getCover());//课程封面
order.setTeacherName(courseInfoOrder.getTeacherName());//讲师名字
order.setTotalFee(courseInfoOrder.getPrice());//价格
order.setMemberId(userId);//用户id
order.setMobile(userInfoOrder.getMobile());//用户手机号
order.setNickname(userInfoOrder.getNickname());//用户名
order.setStatus(0); //订单状态(0:未支付 1:已支付)
order.setPayType(1); //支付类型 ,微信1
baseMapper.insert(order);
return R.ok().data("orderNo",orderNo);
}
}
2》根据订单编号,生成微信支付二维码(返回二维码的链接,返回给前端,由前端处理,显示二维码)
/**
* 根据订单编号,生成支付二维码
* @param orderNo
* @return
*/
@Override
public Map creatQrCode(String orderNo) {
try {
// 1.根据订单编号查询订单信息
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("order_no", orderNo);
Order order = orderService.getOne(queryWrapper);
// 2.使用map设置生成二维码需要的参数
Map map = new HashMap();
map.put("appid","wx74862e0dfcf69954"); //支付id
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"); //进行支付的ip地址,实际项目使用项目的域名
map.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify"); //支付后回调地址
map.put("trade_type", "NATIVE"); //支付类型,NATIVE:知道价格,生成二维码
// 3.发送httpclient请求,(需要xml格式参数) 地址是微信支付的固定地址
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
client.setXmlParam(WXPayUtil.generateSignedXml(map, "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
client.setHttps(true);
//执行post请求发送
client.post();
// 4.得到返回结果,结果是xml格式的
String xml = client.getContent();
// 将结果转为map
Map<String, String> map2 = WXPayUtil.xmlToMap(xml);
System.out.println("生成二维码,返回的结果"+map2);
// 最终返回数据的封装
Map resultMap = new HashMap();
resultMap.put("out_trade_no", orderNo);//订单编号
resultMap.put("course_id", order.getCourseId()); //课程编号
resultMap.put("total_fee", order.getTotalFee() + ""); // 价格
resultMap.put("result_code", map2.get("result_code")); //返回二维码状态码
resultMap.put("code_url", map2.get("code_url")); //二维码地址
return resultMap;
} catch (Exception e) {
e.printStackTrace();
throw new GuLiExeception(20001, "生成二维码时发生错误");
}
}
3》查询订单支付状态
/**
* 通过订单编号查询支付状态
* @param orderNo
* @return
*/
@Override
public Map<String, String> querypayStatus(String orderNo) {
try {
// 1.使用map设置生成二维码需要的参数
Map m = new HashMap<>();
m.put("appid", "wx74862e0dfcf69954");
m.put("mch_id", "1558950191");
m.put("nonce_str", WXPayUtil.generateNonceStr());
m.put("out_trade_no", orderNo);//订单编号
// 2.发送httpClient请求
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
client.setXmlParam(WXPayUtil.generateSignedXml(m, "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
client.setHttps(true);
client.post();
// 3.得到返回内容
String xml = client.getContent();
Map<String, String> map = WXPayUtil.xmlToMap(xml);
System.out.println("查询订单支付状态接口返回:"+map);
return map;
} catch (Exception e) {
e.printStackTrace();
throw new GuLiExeception(20001, "在查询订单状态时发生异常");
}
}
/**
* 在支付成功后,更新订单表订单状态、向支付记录表添加支付记录
* @param map
*/
@Override
public void updateOrdersStatus(Map<String, String> map) {
//从map获取订单号
String orderNo =map.get("out_trade_no");
//根据订单号查询订单信息
QueryWrapper<Order> wrapper =new QueryWrapper<>();
wrapper.eq("order_no",orderNo);
Order order=orderService.getOne(wrapper);
// 如果该订单已支付,则直接返回
if(order.getStatus()==1)
{
return;
}
// 更新订单表订单状态
order.setStatus(1);
orderService.updateById(order);
//向支付记录表中添加支付记录
PayLog payLog =new PayLog();
payLog.setOrderNo(orderNo);
payLog.setPayTime(new Date());
payLog.setPayType(1);//支付类型 1微信
payLog.setTotalFee(order.getTotalFee());//总价格
payLog.setAttr(JSONObject.toJSONString(map));//将其他属性存储到数据库
baseMapper.insert(payLog);
}
controller
// 查询订单状态(是否支付成功)
@GetMapping("queryPayStatus/{orderNo}")
public R queryStatus(@PathVariable String orderNo) throws Exception {
Map<String, String> map = baseService.querypayStatus(orderNo);
//如果map返回值为空,那绝对是订单支付出错了
if (map == null) {
return R.error().message("订单支付出错了!");
}
//如果支付成功
if (map.get("trade_state").equals("SUCCESS")) {
//添加支付记录到支付日志表,更新订单表订单状态
baseService.updateOrdersStatus(map);
return R.ok().message("支付成功");
}
//如果未支付成功
return R.ok().code(25000).message("支付中");
}
4.前端
点击支付按钮后,访问生成二维码的接口,跳转到支付页面,并每三秒查询一次支付状态接口,如果支付成功,就跳转到课程详情界面,如果正在支付则不处理。
<template>
<div class="Page Confirm">
<div class="Title">
<h1 class="fl f18">订单确认</h1>
<img src="~/assets/img/cart_setp2.png" class="fr">
<div class="clear"></div>
</div>
<form name="flowForm" id="flowForm" method="post" action="">
<table class="GoodList">
<tbody>
<tr>
<th class="name">商品</th>
<th class="price">原价</th>
<th class="priceNew">价格</th>
</tr>
</tbody>
<tbody>
<!-- <tr>
<td colspan="3" class="Title red f18 fb"><p>限时折扣</p></td>
</tr> -->
<tr>
<td colspan="3" class="teacher">讲师:{
{order.teacherName}}</td>
</tr>
<tr class="good">
<td class="name First">
<a target="_blank" :href="'https://localhost:3000/course/'+order.courseId">
<img :src="order.courseCover"></a>
<div class="goodInfo">
<input type="hidden" class="ids ids_14502" value="14502">
<a target="_blank" :href="'https://localhost:3000/course/'+ order.courseId">{
{order.courseTitle}}</a>
</div>
</td>
<td class="price">
<p>¥<strong>{
{order.totalFee}}</strong></p>
<!-- <span class="discName red">限时8折</span> -->
</td>
<td class="red priceNew Last">¥<strong>{
{order.totalFee}}</strong></td>
</tr>
<tr>
<td class="Billing tr" colspan="3">
<div class="tr">
<p>共 <strong class="red">1</strong> 件商品,合计<span
class="red f20">¥<strong>{
{order.totalFee}}</strong></span></p>
</div>
</td>
</tr>
</tbody>
</table>
<div class="Finish">
<div class="fr" id="AgreeDiv">
<label for="Agree"><p class="on"><input type="checkbox" checked="checked">我已阅读并同意<a href="javascript:" target="_blank">《谷粒学院购买协议》</a></p></label>
</div>
<div class="clear"></div>
<div class="Main fl">
<div class="fl">
<a :href="'/course/'+order.courseId">返回课程详情页</a>
</div>
<div class="fr">
<p>共 <strong class="red">1</strong> 件商品,合计<span class="red f20">¥<strong
id="AllPrice">{
{order.totalFee}}</strong></span></p>
</div>
</div>
<input name="score" value="0" type="hidden" id="usedScore">
<button class="fr redb" type="button" id="submitPay" @click="toPay()">去支付</button>
<div class="clear"></div>
</div>
</form>
</div>
</template>
<script>
import orders from '@/api/orders'
export default {
data() {
return {
order:''
}
},
//根据订单id获取订单信息
//异步调用的方式
asyncData({params, error}) {
return orders.getOrderInfo(params.oid).then(response => {
return {
order: response.data.data.data
}
})
},
methods: {
//点击去支付,跳转到支付页面
toPay() {
this.$router.push({path: '/pay/' + this.order.orderNo})
}
}
}
</script>
<template>
<div class="cart py-container">
<!--主内容-->
<div class="checkout py-container pay">
<div class="checkout-tit">
<h4 class="fl tit-txt"><span class="success-icon"></span><span class="success-info">订单提交成功,请您及时付款!订单号:{
{payObj.out_trade_no}}</span>
</h4>
<span class="fr"><em class="sui-lead">应付金额:</em><em class="orange money">¥{
{payObj.total_fee}}</em></span>
<div class="clearfix"></div>
</div>
<div class="checkout-steps">
<div class="fl weixin">微信支付</div>
<div class="fl sao">
<p class="red">请使用微信扫一扫。</p>
<div class="fl code">
<!-- <img id="qrious" src="~/assets/img/erweima.png" alt=""> -->
<!-- <qriously value="weixin://wxpay/bizpayurl?pr=R7tnDpZ" :size="338"/> -->
<qriously :value="payObj.code_url" :size="338"/>
<div class="saosao">
<p>请使用微信扫一扫</p>
<p>扫描二维码支付</p>
</div>
</div>
</div>
<div class="clearfix"></div>
<!-- <p><a href="pay.html" target="_blank">> 其他支付方式</a></p> -->
</div>
</div>
</div>
</template>
<script>
import orders from '@/api/orders'
export default {
//根据订单id生成微信支付二维码
asyncData({params, error}) {
return orders.createNative(params.pid).then(response => {
return {//异步请求需要return
payObj: response.data.data
}
})
},
data() {
return {
timer: null, // 定时器名称
}
},
mounted() {
//在页面渲染之后执行
//每隔三秒,去查询一次支付状态
this.timer1 = setInterval(() => {
this.queryPayStatus(this.payObj.out_trade_no)
}, 3000);
},
methods: {
//查询支付状态的方法
queryPayStatus(out_trade_no) {
orders.queryPayStatus(out_trade_no).then(response => {
if (response.data.success) {
//如果支付成功,清除定时器
clearInterval(this.timer1)
this.$message({
type: 'success',
message: '支付成功!'
})
//跳转到课程详情页面观看视频
this.$router.push({path: '/course/' + this.payObj.course_id})
}
})
}
}
}
</script>