目录
前言:
完整代码:
前端代码:
后端代码:
所需依赖:
代码部分:
config:
service:
创建订单功能详解:
创建二维码核心功能:
返回订单状态核心功能:
成功结果返回核心功能:
controller层:
以上的代码还包含与实际业务有关的其他功能 比如rabbitmq的死信队列 分布式feign调用等,可以注释掉不使用
点击购买按钮,创建二维码,让用户可以进行扫码支付。具体流程如下图
我们主要负责商户后台系统的编写。
微信扫一扫支付
成就自己
¥{{course.discounts}}
¥
{{course.price}}
// 购买课程
buy() {
if( this.user != null ){
this.dialogFormVisible = true; //显示提示框
//微信下单
console.log("user.id:"+this.user.id)
console.log("course.id:"+this.course.id)
console.log("activityId:"+this.course.activity.id)
let args = {"userId":this.user.id,"courseId":this.course.id,"activityId":this.course.activity.id,"price":1};
this.axios.post("/wxpay/makeOrder",this.qs.stringify(args))
.then(res => {
console.log(res.data);
//利用返回的code_url,显示支付二维码
this.wxpayUrl = "http://localhost:9000/wxpay/code?url="+res.data.code_url;
console.log(this.wxpayUrl);
//检查后台返回的订单号支付状态
this.checkOrder(res.data.trade_no);
});
}else{
this.$message.error("购买失败,请先登录!");
}
},
//检查订单号支付状态 websocket
checkOrder(tradeNo){
console.log("订单的tradeno:"+tradeNo)
let count = 0;
//定时1分钟轮询检查
let timer = setInterval(() =>{
this.axios.get("/wxpay/checkOrder?tradeNo="+tradeNo)
.then(res => {
console.log(res.data +"," + count);
if(res.data == "SUCCESS"){
clearInterval(timer);
this.dialogFormVisible = false;
}
});
count++;
if(count == 10){
clearInterval(timer);
this.dialogFormVisible = false;
this.$message.error("支付超时");
}
},6000);
},
this.axios.post("/wxpay/makeOrder",this.qs.stringify(args))
.then(res => {
console.log(res.data);
//利用返回的code_url,显示支付二维码
this.wxpayUrl = "http://localhost:9000/wxpay/code?url="+res.data.code_url;
console.log(this.wxpayUrl);
//检查后台返回的订单号支付状态
this.checkOrder(res.data.trade_no);
});
注:估计现在很多人一脸懵逼,二维码怎么莫名其妙生成了,记得往后面看,前端只不过是调接口,后端才是满满干货。
后端代码:
所需依赖:
4.0.0
com.dmdd
educate_paopao
0.0.1-SNAPSHOT
com.example
edu-pay-service
0.0.1-SNAPSHOT
edu-pay-service
edu-pay-service
8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
com.github.wxpay
wxpay-sdk
0.0.3
com.google.zxing
core
3.3.3
com.google.zxing
javase
3.3.3
org.springframework.cloud
spring-cloud-config-client
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.dmdd
common-api
0.0.1-SNAPSHOT
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
org.springframework.boot
spring-boot-starter-amqp
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.boot
spring-boot-maven-plugin
我用的功能比较多,所以依赖有点杂,我把只跟微信支付相关的依赖列出来,并讲解
com.github.wxpay
wxpay-sdk
0.0.3
调用微信的api就需要这依赖
com.google.zxing
core
3.3.3
com.google.zxing
javase
3.3.3
这两个依赖是用于生成二维码的,二维码只不过是一个链接,我们扫码,就等于访问一个链接
代码部分:
config:
package com.dmdd.edupayservice.config;
import com.github.wxpay.sdk.WXPayConfig;
import java.io.InputStream;
public class MyWXPayConfig implements WXPayConfig {
@Override
public String getAppID() {
return "wx30************e";
}
@Override
public String getMchID() {
return "15**********81";
}
@Override
public String getKey() {
return "HJd*******************g";
}
@Override
public InputStream getCertStream() {
return null;
}
@Override
public int getHttpConnectTimeoutMs() {
return 0;
}
@Override
public int getHttpReadTimeoutMs() {
return 0;
}
}
需要向微信申请才可获得
service:
package com.dmdd.edupayservice.service.impl;
import com.dmdd.common.entity.UserCourseOrder;
import com.dmdd.edupayservice.config.MyWXPayConfig;
import com.dmdd.edupayservice.config.RabbitMQConfig;
import com.dmdd.edupayservice.feign.CourseServiceFeignClient;
import com.dmdd.edupayservice.feign.OrderServiceFeignClient;
import com.dmdd.edupayservice.service.IWxPayService;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayUtil;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.util.Streams;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Console;
import java.io.IOException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* 微信支付Service
*/
@Slf4j
@Service
public class WxPayServiceImpl implements IWxPayService {
@Autowired
private CourseServiceFeignClient courseServiceFeignClient;
@Autowired
private OrderServiceFeignClient orderServiceFeignClient;
@Autowired
private RabbitTemplate rabbitTemplate;
public static final String DEVICE_INFO = "WEB";
public static final String BODY = "dmdd";
public static final String FEE_TYPE = "CNY";
//回调接口的URL
public static final String NOTIFY_URL = "http://ygp8pz4342342ee.cc/wxpay/callback";
public static final String TRADE_TYPE = "NATIVE";
public static final String SIGN = "5E00F9F72173C9449F802411E362012312312317078";
/**
* 下单
*/
public Map makeWxOrder(long userId, long courseId, long activityId, long price) throws Exception {
//创建支付对象
MyWXPayConfig config = new MyWXPayConfig();
WXPay wxPay = new WXPay(config);
//配置微信支付
Map map = new HashMap<>();
//应用id
map.put("appid", config.getAppID());
//商户id
map.put("mch_id", config.getMchID());
//device_info web
map.put("device_info", DEVICE_INFO);
map.put("nonce_str", WXPayUtil.generateNonceStr());
map.put("body", BODY);
//创建随机订单号
String tradeNo = UUID.randomUUID().toString().replace("-", "");
map.put("out_trade_no", tradeNo);
//FEE_TYPE=CNY 代表人民币
map.put("fee_type", FEE_TYPE);
//价格
map.put("total_fee", String.valueOf(price));
//微信对商户后台的回调接口,更新订单状态
map.put("notify_url", NOTIFY_URL);
map.put("trade_type", TRADE_TYPE);
map.put("product_id", String.valueOf(activityId));
//执行统一下单
Map result = wxPay.unifiedOrder(map);
log.info("微信下单:{}", result);
if (result != null) {
//保存订单号
result.put("trade_no", tradeNo);
//创建课程订单
Timestamp now = Timestamp.valueOf(LocalDateTime.now());
UserCourseOrder order = new UserCourseOrder(System.currentTimeMillis(), userId, courseId, activityId, 1L, 0, tradeNo, price, now, now, 0);
orderServiceFeignClient.makeOrder(order);
//减少课程库存
courseServiceFeignClient.reduceStock(activityId);
//发送订单号,超时会进入死信队列
rabbitTemplate.convertAndSend(RabbitMQConfig.ORDER_EXCHANGE, RabbitMQConfig.ORDER_QUEUE_KEY, tradeNo,
//消息的后置处理
message -> {
message.getMessageProperties().setMessageId(UUID.randomUUID().toString());
//设置消息超时
message.getMessageProperties().setExpiration(String.valueOf(30 * 1000));
return message;
});
log.info("完成下单,订单:{}", result);
}
return result;
}
@Override
public void makeQRCode(String url, HttpServletResponse response) {
//通过支付链接生成二维码
HashMap hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
hints.put(EncodeHintType.MARGIN, 2);
try {
//把code_url包装到二维码图片
BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, 200, 200, hints);
MatrixToImageWriter.writeToStream(bitMatrix, "PNG", response.getOutputStream());
log.info("创建二维码完成");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
/**
* 检查订单状态
*/
public String checkWxOrder(String tradeNo) throws Exception {
MyWXPayConfig config = new MyWXPayConfig();
WXPay pay = new WXPay(config);
Map map = new HashMap<>();
map.put("appid", config.getAppID());
map.put("mch_id", config.getMchID());
map.put("nonce_str", WXPayUtil.generateNonceStr());
map.put("out_trade_no", tradeNo);
map.put("sign", SIGN);
//查询订单
Map res = pay.orderQuery(map);
String state = res.get("trade_state");
log.info("订单" + tradeNo + ",状态" + state);
return state;
}
@Override
public void wxpayCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
//获得微信传来的xml字符串
String str = Streams.asString(request.getInputStream());
//将字符串xml转换为Map
Map map = WXPayUtil.xmlToMap(str);
//给微信发送消息
response.getWriter().println(" ");
//读取订单号
String tradeNo = map.get("out_trade_no");
//查询课程id
ResponseEntity entity = orderServiceFeignClient.getOrderByTradeNo(tradeNo);
UserCourseOrder order = entity.getBody();
//增加课程销量
log.info("课程id为" + order.getCourseId());
log.info("order:" + order);
courseServiceFeignClient.addCourseSales(order.getCourseId());
// //修改订单状态为已支付
order.setStatus(1);
orderServiceFeignClient.changeOrderStatus(order);
log.info("支付成功:{}", order);
}
}
创建订单功能详解:
public static final String BODY = "dmdd";
app的名字,随便取。
public static final String FEE_TYPE = "CNY";
表示支付的货币类型是人民币。
//回调接口的URL
public static final String NOTIFY_URL = "http://ygp8pz.natappfree.cc/wxpay/callback";
微信服务会跟我们的后端发送一个一个请求,告诉我们商户支付结果,因为企业内部都是把服务部署到内网里面,所以想要微信访问就得用内网穿透这种技术,顾名思义就是外网可以访问内网。
//微信对商户后台的回调接口,更新订单状态
map.put("notify_url", NOTIFY_URL);
创建订单的时候把回调url的信息也存入订单中
那么如何内网穿透?
到natapp官网申请隧道,下载软件
"http://ygp8pz.natappfree.cc/wxpay/callback"
表示从外网访问 内网7777服务器内的wxpay/callback的请求。
嫌麻烦这一块也可以不搞,不影响微信支付的功能。
订单创建返回结果如下,红色框框勾出来的url就是调用微信支付功能的url,我们需要把这个url转换成二维码
创建二维码核心功能:
//把code_url包装到二维码图片
BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, 200, 200, hints);
MatrixToImageWriter.writeToStream(bitMatrix, "PNG", response.getOutputStream());
log.info("创建二维码完成");
创建二维码图片的核心功能。
返回订单状态核心功能:
public static final String SIGN = "5E00F9************************8";
认证签名,只有正确的签名wx才会接受你的返回
WXPay pay = new WXPay(config);
//查询订单
Map res = pay.orderQuery(map);
得到wx返回给我们的支付状态,不懂?看下面
支付有没有成功,看上面的打印就知道了。玩过连连看的,应该都看得懂。
注:大伙应该都点过外卖,在支付超时的时间之内,每隔一段时间就会通知我们支付结果。
成功结果返回核心功能:
根据自己的实际需求来。
controller层:
package com.dmdd.edupayservice.controller;
import com.dmdd.edupayservice.service.IWxPayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
@RestController
@Slf4j
@RequestMapping("wxpay")
public class WXOrderController {
@Autowired
private IWxPayService payService;
/**
* 微信下单
*/
@PostMapping("makeOrder")
public Map makeOrder(Long userId, Long courseId, Long activityId, Long price) throws Exception {
//微信下单
return payService.makeWxOrder(userId,courseId, activityId, price);
}
/**
* 生成支付二维码
* @param url
* @param response
*/
@RequestMapping("code")
public void createWxPayCode(String url, HttpServletResponse response){
//创建支付二维码
payService.makeQRCode(url,response);
log.info("生成微信支付二维码:{}",url);
}
/**
* 检查订单状态
*/
@RequestMapping("checkOrder")
public String checkOrder(String tradeNo) throws Exception {
if (StringUtils.isEmpty(tradeNo)) {
return null;
}
String status = payService.checkWxOrder(tradeNo);
log.info("检查订单{} 状态:{}",tradeNo,status);
return status;
}
/**
* 微信支付平台支付成功的回调
* @param request
* @param response
* @throws Exception
*/
@RequestMapping("callback")
public void paySuccessCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
payService.wxpayCallback(request,response);
}
}
以上的代码还包含与实际业务有关的其他功能 比如rabbitmq的死信队列 分布式feign调用等,可以注释掉不使用