一、分析
需要写4个接口:
生成订单接口、根据订单ID查询订单信息接口、生成微信支付二维码接口、查询订单支付状态并且更新订单接口。
课程详情页面调用生成订单接口,来到订单详情页面,调用根据订单ID查询订单信息接口渲染页面。点击去支付来到支付页面,调用生成二维码接口,扫描支付后需要判断订单是否支付,根据微信官方API文档可以写出最后一个接口。
二、后端接口
1.controller
OrderController
@RestController
@RequestMapping("/eduorder/order")
@CrossOrigin
public class OrderController {
@Autowired
private OrderService orderService;
//1 生成订单
@PostMapping("/generateOrder/{courseId}")
public R generateOrder(@PathVariable String courseId, HttpServletRequest request){
String orderId = orderService.generateOrder(courseId, JwtUtils.getMemberIdByJwtToken(request));
return R.ok().data("orderId", orderId);
}
//2 根据订单ID查询订单信息
@GetMapping("/getOrderInfoById/{orderId}")
@ApiOperation("根据订单ID查询订单信息")
public R getOrderInfoById(@PathVariable String orderId){
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("order_no", orderId);
Order order = orderService.getOne(wrapper);
System.out.println(order);
return R.ok().data("orderInfo", order);
}
}
PayLogController
@RestController
@RequestMapping("/eduorder/paylog")
@CrossOrigin
@Api(description = "课程支付模块")
public class PayLogController {
@Autowired
private PayLogService payLogService;
//生成微信二维码
@GetMapping("/generateWXCode/{orderId}")
@ApiOperation("生成微信二维码")
public R generateWXCode(@PathVariable String orderId){
//返回信息:包含二维码地址,还有其他信息
Map<String, Object> map = payLogService.generateWXCode(orderId);
System.out.println("二维码信息:" + map);
return R.ok().data(map);
}
//查询订单支付状态并且更新订单
@GetMapping("/findPayStatus/{orderId}")
@ApiOperation("查询订单状态")
public R findPayStatus(@PathVariable String orderId){
Map<String,String> map = payLogService.findPayStatus(orderId);
System.out.println("订单信息:" + map);
if (map == null){
return R.error().message("支付出错了!").data("status",0);
}
//如果返回map里面不为空,通过map获取订单状态
if (map.get("trade_state").equals("SUCCESS")){
//向支付表中添加记录,更新订单状态
payLogService.updateOrderStatus(map);
return R.ok().message("支付成功").data("status",1);
}
return R.ok().code(25000).message("正在支付中").data("status",2);
}
}
2.service
OrderServiceImpl
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
@Autowired
private EduClient eduClient;
@Autowired
private UCenterClient uCenterClient;
//生成订单
@Override
public String generateOrder(String courseId, String memberId) {
//1 通过远程调用 调用根据课程ID查询课程信息
CourseWebVoOrder courseWebVoOrder = eduClient.getCourseInfoById(courseId);
//2 通过远程调用 调用根据用户ID查询用户信息
UcenterMemberOrder ucenterMemberOrder = uCenterClient.getMemberInfoById(memberId);
//3 创建order对象,向order对象set
Order order = new Order();
order.setOrderNo(UUID.randomUUID().toString().replaceAll("-",""));//订单号
order.setCourseId(courseId);//课程ID
order.setCourseTitle(courseWebVoOrder.getTitle());//课程标题
order.setCourseCover(courseWebVoOrder.getCover());//课程封面
order.setTeacherName(courseWebVoOrder.getTeacherName());//教师名称
order.setTotalFee(courseWebVoOrder.getPrice());//课程价格
order.setMemberId(memberId);//会员ID
order.setMobile(ucenterMemberOrder.getMobile());//会员手机号
order.setNickname(ucenterMemberOrder.getNickname());//会员姓名
order.setStatus(0);//订单状态 未支付状态
order.setPayType(1);//支付方式 1 微信支付 2 支付宝
//4 加入数据库
baseMapper.insert(order);
return order.getOrderNo();
}
}
PayLogServiceImpl
@Service
public class PayLogServiceImpl extends ServiceImpl<PayLogMapper, PayLog> implements PayLogService {
@Autowired
private OrderService orderService;
//生成二维码
@Override
public Map<String, Object> generateWXCode(String orderId) {
try {
//1 根据订单号查询订单信息
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("order_no", orderId);
Order order = orderService.getOne(wrapper);
//2 使用map设置生成二维码需要参数
Map<String,String> paramsMap = new HashMap<>();
paramsMap.put("appid","appid");
paramsMap.put("mch_id", "mch_id");
paramsMap.put("nonce_str", WXPayUtil.generateNonceStr());
paramsMap.put("body", order.getCourseTitle()); //课程标题
paramsMap.put("out_trade_no", orderId); //订单号
paramsMap.put("total_fee", order.getTotalFee().multiply(new BigDecimal("100")).longValue()+"");
paramsMap.put("spbill_create_ip", "127.0.0.1");
paramsMap.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify\n");
paramsMap.put("trade_type", "NATIVE");
//3 发送httpclient请求,传递参数xml格式,微信支付提供的固定的地址
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
//设置xml格式的参数
client.setXmlParam(WXPayUtil.generateSignedXml(paramsMap, "key"));
client.setHttps(true);
//4 执行请求发送
client.post();
//5 得到发送请求返回的内容
String xmlContent = client.getContent();//注意,这个格式是xml格式的
Map<String,String> resultMap = WXPayUtil.xmlToMap(xmlContent);
//6 最终返回数据的封装
Map<String,Object> dataMap = new HashMap<>();
dataMap.put("out_trade_no", orderId);
dataMap.put("course_id", order.getCourseId());
dataMap.put("total_fee", order.getTotalFee());
dataMap.put("result_code", resultMap.get("result_code")); //返回二维码操作状态码
dataMap.put("code_url", resultMap.get("code_url")); //二维码地址
return dataMap;
}catch (Exception e){
e.printStackTrace();
throw new GuliException(20001, "生成二维码失败!");
}
}
//查询订单支付状态
@Override
public Map<String, String> findPayStatus(String orderId) {
try {
//1、封装参数
Map<String,String> m = new HashMap<>();
m.put("appid", "appid");
m.put("mch_id", "mch_id");
m.put("out_trade_no", orderId);
m.put("nonce_str", WXPayUtil.generateNonceStr());
//2 发送httpclient
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
client.setXmlParam(WXPayUtil.generateSignedXml(m,"key"));
client.setHttps(true);
client.post();
//3 得到请求返回内容
String xml = client.getContent();
//6、转成Map再返回
return WXPayUtil.xmlToMap(xml);
}catch(Exception e) {
return null;
}
}
//根据id更新订单状态,同时新增一条支付记录
@Override
public void updateOrderStatus(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() != 1){
order.setStatus(1);
boolean update = orderService.updateById(order);
if (!update){
throw new GuliException(20001, "添加失败!");
}
}
//4 向支付表中添加支付记录
PayLog payLog = new PayLog();
payLog.setOrderNo(orderNo);//订单号
payLog.setPayTime(new Date());//支付时间
payLog.setPayType(1);//支付类型 微信
payLog.setTotalFee(order.getTotalFee());//总金额
payLog.setTradeState(map.get("trade_state"));//交易状态
payLog.setTransactionId(map.get("transaction_id"));//交易流水号
payLog.setAttr(JSONObject.toJSONString(map));//fastjson
int insert = baseMapper.insert(payLog);
if (insert == 0){
throw new GuliException(20001, "添加支付记录失败!");
}
}
}
三、前端页面
order.js
import request from '@/utils/request'
export default{
//1 生成订单
generateOrder(courseId){
return request ({
url: `/eduorder/order/generateOrder/${courseId}`,
method: 'post'
})
},
//2 根据订单ID查询订单信息
getOrderInfoById(orderId){
return request ({
url: `/eduorder/order/getOrderInfoById/${orderId}`,
method: 'get'
})
},
//3 生成微信二维码
generateWXCode(orderId){
return request({
url: `/eduorder/paylog/generateWXCode/${orderId}`,
method: 'get'
})
},
//4 根据订单ID查询订单支付状态并且更新订单
findPayStatus(orderId){
return request({
url: `/eduorder/paylog/findPayStatus/${orderId}`,
method: 'get'
})
}
}
course/_id.vue js部分代码
<script>
import courseApi from '@/api/course'
import orderApi from '@/api/order'
import memberApi from '@/api/member'
import cookie from 'js-cookie';
export default {
//异步调用
asyncData({ params, error }){
return courseApi.getCourseInfo(params.id).then(response => {
return {
courseWebVo: response.data.data.courseWebVo,
chapterVideoList: response.data.data.chapterVideoList,
courseId: params.id
}
})
},
methods: {
//生成订单
purchase(){
orderApi.generateOrder(this.courseId).then(response => {
//获取订单号
const orderId = response.data.data.orderId
alert(orderId)
//生成订单后,跳转到订单详情页面
this.$router.push({ path: '/order/' + orderId })
})
}
}
}
</script>
order/_id.vue js部分代码
<script>
import orderApi from '@/api/order'
export default {
asyncData({ params, error }) {
return orderApi.getOrderInfoById(params.id)
.then(response => {
return {
order: response.data.data.orderInfo
}
})
},
methods:{
//去支付
toPay(){
alert(this.order)
this.$router.push({ path: `/pay/` + this.order.orderNo })
}
}
}
</script>
pay/_pid.vue js部分代码
<script>
import orderApi from '@/api/order'
export default {
asyncData({params,error}){
return orderApi.generateWXCode(params.pid).then(response => {
return {
payObj: response.data.data
}
})
},
data(){
return {
timer: ''
}
},
//在页面渲染之后执行
mounted(){
//定时器,每隔三秒调用一次查询订单状态的方法
this.timer = setInterval(() => {
this.queryOrderStatus(this.payObj.out_trade_no)
},3000)
},
methods:{
//查询订单状态
queryOrderStatus(orderNo){
orderApi.findPayStatus(orderNo).then(response => {
console.log(response.data)
//清除定时器
clearInterval(this.timer)
//提示信息
this.$message({
type: 'success',
message: '支付成功!'
})
//跳转回到课程详情页面
this.$router.push({path: '/course/' + this.payObj.course_id})
})
}
}
}
</script>
四、效果
1 在课程详情页面点击立即购买来到订单详情页面
2 在订单详情页面,点击去支付来到支付页面
3 在支付页面扫描二维码支付成功回到课程页面