整合微信支付功能
程序员文章站
2022-06-19 14:46:42
一、课程支付功能需求描述...
一、课程支付功能需求描述
二、课程支付功能开发
准备工作:引入依赖
<dependencies>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<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、根据订单id查询订单信息接口
package com.atguigu.eduorder.controller;
import com.atguigu.common.R;
import com.atguigu.common.jwt.JwtUtils;
import com.atguigu.eduorder.entity.TOrder;
import com.atguigu.eduorder.service.TOrderService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
/**
* <p>
* 订单 前端控制器
* </p>
*
* @author Panghl
* @since 2021-02-21
*/
@RestController
@RequestMapping("/eduorder/t-order")
@Api(tags = {"订单管理"})
public class TOrderController {
@Autowired
private TOrderService orderService;
@ApiOperation("生成订单方法")
@PostMapping("/createOrder/{courseId}")
public R saveOrder(@PathVariable String courseId, HttpServletRequest request) {
String memberId = JwtUtils.getMemberIdByJwtToken(request);
//生成订单号
String orderNo = orderService.createOrder(courseId, memberId);
return R.ok().data("orderNo", orderNo);
}
@ApiOperation("根据订单id查询订单信息")
@GetMapping("/getOrdersInfo/{orderNo}")
public R getOrdersInfo(@PathVariable("orderNo") String orderNo) {
TOrder order = orderService.getByOrderNo(orderNo);
return R.ok().data("order", order);
}
}
package com.atguigu.eduorder.feign;
import com.atguigu.common.ordervo.CourseOrderWebVo;
import com.atguigu.common.ordervo.UcenterMemberOrder;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
/**
* @Author panghl
* @Date 2021/2/21 13:05
* @Version 1.0
* @Description 服务调用
**/
@Component
@FeignClient("service-edu")
public interface EduClient {
@PostMapping(value = "/eduservice/coursefront/getCourseInfoOrder/{id}")
public CourseOrderWebVo getCourseInfoOrder(@PathVariable("id") String id);
}
package com.atguigu.eduorder.feign;
import com.atguigu.common.ordervo.UcenterMemberOrder;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
/**
* @Author panghl
* @Date 2021/2/21 13:07
* @Version 1.0
* @Description 服务调用
**/
@Component
@FeignClient("service-ucenter")
public interface UcenterClient {
@PostMapping(value = "/educenter/member/getUserInfoOrder/{id}")
public UcenterMemberOrder getUserInfoOrder(@PathVariable("id") String id);
}
package com.atguigu.eduorder.service.impl;
import com.atguigu.common.ordervo.CourseOrderWebVo;
import com.atguigu.common.ordervo.UcenterMemberOrder;
import com.atguigu.common.uuid.IdUtils;
import com.atguigu.eduorder.entity.TOrder;
import com.atguigu.eduorder.feign.EduClient;
import com.atguigu.eduorder.feign.UcenterClient;
import com.atguigu.eduorder.mapper.TOrderMapper;
import com.atguigu.eduorder.service.TOrderService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* 订单 服务实现类
* </p>
*
* @author Panghl
* @since 2021-02-21
*/
@Service
public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements TOrderService {
@Autowired
private EduClient eduClient;
@Autowired
private UcenterClient ucenterClient;
@Override
public String createOrder(String courseId, String memberId) {
UcenterMemberOrder userInfoOrder = ucenterClient.getUserInfoOrder(memberId);
CourseOrderWebVo courseInfoOrder = eduClient.getCourseInfoOrder(courseId);
TOrder order = copyProperties(courseInfoOrder,userInfoOrder);
baseMapper.insert(order);
return order.getOrderNo();
}
@Override
public TOrder getByOrderNo(String orderNo) {
QueryWrapper<TOrder> qw = new QueryWrapper<>();
qw.eq("order_no",orderNo).last("limit 1");
return baseMapper.selectOne(qw);
}
private TOrder copyProperties( CourseOrderWebVo courseInfoOrder,UcenterMemberOrder userInfoOrder){
TOrder order = new TOrder();
order.setOrderNo(IdUtils.getOrderNo()); //订单号
order.setCourseId(courseInfoOrder.getId());
order.setCourseTitle(courseInfoOrder.getTitle());
order.setCourseCover(courseInfoOrder.getCover());
order.setTeacherName(courseInfoOrder.getTeacherName());
order.setTotalFee(courseInfoOrder.getPrice());
order.setMemberId(userInfoOrder.getId());
order.setMobile(userInfoOrder.getMobile());
order.setNickname(userInfoOrder.getNickname());
order.setStatus(0);
order.setPayType(1);
return order;
}
}
3、生成微信支付的二维码接口
4、查询订单支付状态接口
package com.atguigu.eduorder.controller;
import com.atguigu.common.R;
import com.atguigu.eduorder.service.TPayLogService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* 支付日志表 前端控制器
* </p>
*
* @author Panghl
* @since 2021-02-21
*/
@RestController
@RequestMapping("/eduorder/t-pay-log")
@Api(tags = {"订单支付日志管理"})
public class TPayLogController {
@Autowired
private TPayLogService payLogService;
@ApiOperation(value = "生成二维码")
@GetMapping("/createNative/{orderNo}")
public R createNative(@PathVariable("orderNo") String orderNo){
//返回信息,包含二维码地址,还有其他需要的信息
Map<String,Object> map = payLogService.createNative(orderNo);
System.out.println("***返回二维码map集合***:"+map);
return R.ok().data(map);
}
@ApiOperation(value = "获取查询订单状态")
@GetMapping("/queryPayStatus/{orderNo}")
public R queryPayStatus(@PathVariable("orderNo") String orderNo){
Map<String,String> map = payLogService.queryPayStatus(orderNo);
System.out.println("***获取查询订单map集合***:"+map);
if (map == null){
return R.error().message("支付出错了");
}
if (map.get("trade_state").equals("SUCCESS")){
//支付成功,修改订单状态
payLogService.updateOrderStatus(map);
return R.ok().message("支付成功");
}
return R.error().code(25000).message("支付中...");
}
}
package com.atguigu.eduorder.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.eduorder.entity.TOrder;
import com.atguigu.eduorder.entity.TPayLog;
import com.atguigu.eduorder.enums.PayType;
import com.atguigu.eduorder.mapper.TPayLogMapper;
import com.atguigu.eduorder.service.TOrderService;
import com.atguigu.eduorder.service.TPayLogService;
import com.atguigu.eduorder.util.ConstantPropertiesUtils;
import com.atguigu.eduorder.util.HttpClient;
import com.atguigu.servicebase.exceptionhandler.ELeanExeception;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.wxpay.sdk.WXPayUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* 支付日志表 服务实现类
* </p>
*
* @author Panghl
* @since 2021-02-21
*/
@Service
public class TPayLogServiceImpl extends ServiceImpl<TPayLogMapper, TPayLog> implements TPayLogService {
@Autowired
private TOrderService orderService;
/**
* 生成支付二维码
*
* @param orderNo
* @return
*/
@Override
public Map createNative(String orderNo) {
//1、根据订单号查询订单信息
TOrder order = orderService.getByOrderNo(orderNo);
//2、设置支付参数
Map<String, String> m = new HashMap<>();
m.put("appid", ConstantPropertiesUtils.APP_ID);
m.put("mch_id", ConstantPropertiesUtils.PARTNER);
m.put("nonce_str", WXPayUtil.generateNonceStr());
m.put("body", order.getCourseTitle()); //课程标题
m.put("out_trade_no", orderNo); //订单号
m.put("total_fee", order.getTotalFee().multiply(new BigDecimal("100")).longValue() + "");
m.put("spbill_create_ip", "127.0.0.1");
m.put("notify_url", ConstantPropertiesUtils.NOTIFY_URL);
m.put("trade_type", "NATIVE");
//3、HTTPClient来根据URL访问第三方接口并且传递参数
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
try {
//设置xml格式的参数
client.setXmlParam(WXPayUtil.generateSignedXml(m,ConstantPropertiesUtils.PARTNER_KEY ));
client.setHttps(true);
//执行post请求发送
client.post();
//4、返回第三方的数据
//返回内容,是使用xml格式返回
String xml = client.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
Map map = new HashMap<>();
map.put("out_trade_no", orderNo);
map.put("course_id", order.getCourseId());
map.put("total_fee", order.getTotalFee());
map.put("result_code", resultMap.get("result_code"));
map.put("code_url", resultMap.get("code_url"));
//微信支付二维码2小时过期,可采取2小时未支付取消订单
//redisTemplate.opsForValue().set(orderNo, map, 120, TimeUnit.MINUTES);
return map;
} catch (Exception e) {
throw new ELeanExeception(20001,"生成二维码失败");
}
}
/**
* 获取订单状态
*
* @param orderNo
* @return
*/
@Override
public Map<String, String> queryPayStatus(String orderNo) {
try {
//1、封装参数
Map m = new HashMap<>();
m.put("appid", ConstantPropertiesUtils.APP_ID);
m.put("mch_id", ConstantPropertiesUtils.PARTNER);
m.put("out_trade_no", orderNo);
m.put("nonce_str", WXPayUtil.generateNonceStr());
//2、设置请求
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
client.setXmlParam(WXPayUtil.generateSignedXml(m, ConstantPropertiesUtils.PARTNER_KEY));
client.setHttps(true);
client.post();
//3、返回第三方的数据
String xml = client.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
return resultMap;
} catch (Exception e) {
throw new ELeanExeception(20001,"获取订单信息失败");
}
}
/**
* 修改订单状态
*
* @param map
*/
@Override
public void updateOrderStatus(Map<String, String> map) {
//获取订单号
String orderNo = map.get("out_trade_no");
//根据订单id查询订单信息
TOrder order = orderService.getByOrderNo(orderNo);
if (order.getStatus().intValue() == 1) {return;}
order.setStatus(1);
orderService.updateById(order);
TPayLog payLog = copyProperties(order,map);
baseMapper.insert(payLog);
}
private TPayLog copyProperties(TOrder order, Map<String, String> map) {
//记录支付日志
TPayLog payLog=new TPayLog();
payLog.setOrderNo(order.getOrderNo());//支付订单号
payLog.setPayTime(new Date());
payLog.setPayType(PayType.WXPay.getValue());//支付类型
payLog.setTotalFee(order.getTotalFee());//总金额(分)
payLog.setTradeState(map.get("trade_state"));//支付状态
payLog.setTransactionId(map.get("transaction_id")); //微信订单号
payLog.setAttr(JSONObject.toJSONString(map));
return payLog;
}
}
整合前端
下载qriously组件
安装命令:npm install vue-qriously --save-dev
在request.js中配置response 拦截器
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import cookie from 'js-cookie'
// 创建axios实例
const service = axios.create({
baseURL: 'http://localhost:9912', // api的base_url
withCredentials: true, // send cookies when cross-domain requests
timeout: 20000 // 请求超时时间
})
//第三步:创建拦截器
service.interceptors.request.use(
config => {
if (cookie.get('guli_token')) {
config.headers['token'] = cookie.get('guli_token');
}
return config;
},
err => {
return Promise.reject(err);
}
)
// http response 拦截器
service.interceptors.response.use(
response => {
if (response.data.code == 28004) {
console.log("response.data.resultCode是28004")
// 返回 错误代码-1 清除ticket信息并跳转到登录页面
//debugger
window.location.href = "/login"
return
} else {
if (response.data.code !== 20000) {
//25000:订单支付中,不做任何提示
if (response.data.code != 25000) {
Message({
message: response.data.message || 'error',
type: 'error',
duration: 5 * 1000
})
}
} else {
return response;
}
}
},
error => {
return Promise.reject(error.response) // 返回接口返回的错误信息
});
export default service
在pages/orders文件夹下创建_oid.vue文件
<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 orderApi from "@/api/order/order";
export default {
asyncData({ params, error }) {
return orderApi.getOrdersInfo(params.oid).then((res) => {
return {
order: res.data.data.order,
};
});
},
methods: {
//去支付
toPay() {
this.$router.push({ path: "/pay/" + this.order.orderNo });
},
},
};
</script>
在pages/pay文件夹下创建_pid.vue文件
<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 orderApi from "@/api/order/order";
export default {
asyncData({ params, error }) {
return orderApi.createNative(params.pid).then((res) => {
return {
payObj: res.data.data,
};
});
},
data() {
return {
timer1: "",
};
},
//每隔三秒调用一次查询订单状态的方法
mounted() {
//页面渲染之后执行
this.timer1 = setInterval(() => {
this.queryOrderStatus(this.payObj.out_trade_no);
}, 3000);
},
methods: {
queryOrderStatus(orderNo) {
orderApi.queryPayStatus(orderNo).then((res) => {
if (res.data.success) {
//支付成功,清除定时器
clearInterval(this.timer1);
//提示
this.$message({
type: "success",
message: "支付成功!",
});
//跳转回到课程详情页面
this.$router.push({ path: "/course/" + this.payObj.course_id });
}
});
},
},
};
</script>
本文地址:https://blog.csdn.net/qq_45441466/article/details/113916646