接上一篇《vue+element+SpringBoot+OAuth2+Spring Security+Redis+mybatis-plus+mysql+swagger模仿商城,前后端分离实现》。
上篇文章介绍了:
上一篇文章有说过已实现单一订单下单,即只能一个一个商品去下单购买,所以这次进行了优化完善,把单一订单改成了合并订单,即一个订单下面可包含多个订单行项目,也就是一次可购买多个商品,统一生成一个订单,然后进行扫码支付;
大订单表:
- CREATE TABLE `product_order`
- (
- `id` bigint NOT NULL COMMENT '主键',
- `order_num` varchar(100) DEFAULT NULL COMMENT '订单号',
- `user_id` bigint NOT NULL COMMENT '所属用户',
- `pay_amount` decimal(10, 2) NOT NULL DEFAULT 0 COMMENT '支付金额',
- `receiver_name` varchar(225) NOT NULL COMMENT '收货人姓名',
- `receiver_phone` varchar(225) NOT NULL COMMENT '收货人联系电话',
- `receiver_addr` varchar(225) NOT NULL COMMENT '收货人地址',
- `receiver_remark` varchar(225) DEFAULT NULL COMMENT '收货人备注',
- `pay_status` TINYINT(1) DEFAULT 1 COMMENT '支付状态 1待支付 2待发货 3已发货 4已完成 5已取消',
- `deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标记 是否已删除: 0否 1是',
- `create_time` datetime(0) COMMENT '创建时间',
- `update_time` datetime(0) COMMENT '更新时间',
- PRIMARY KEY (`id`) USING BTREE,
- UNIQUE KEY `order_num` (`order_num`) USING BTREE
- ) ENGINE = InnoDB
- DEFAULT CHARSET = utf8mb4 COMMENT ='订单';
订单行项目表:
- -- 订单行项目
- CREATE TABLE `product_order_item`
- (
- `id` bigint NOT NULL COMMENT '主键',
- `parent_id` bigint NOT NULL COMMENT '订单主键',
- `order_num` varchar(100) DEFAULT NULL COMMENT '订单号',
- `product_id` bigint NOT NULL COMMENT '商品主键',
- `product_name` varchar(225) NOT NULL COMMENT '商品名称',
- `product_des` varchar(225) DEFAULT NULL COMMENT '商品描述',
- `price` decimal(10, 2) NOT NULL DEFAULT 0 COMMENT '商品单价',
- `original_price` decimal(10, 2) NOT NULL DEFAULT 0 COMMENT '商品原价',
- `cover_path` varchar(500) DEFAULT NULL COMMENT '商品封面图片',
- `product_num` int(11) NULL DEFAULT 0 COMMENT '商品购买数量',
- `deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除标记 是否已删除: 0否 1是',
- `create_time` datetime(0) COMMENT '创建时间',
- `update_time` datetime(0) COMMENT '更新时间',
- PRIMARY KEY (`id`) USING BTREE,
- ) ENGINE = InnoDB
- DEFAULT CHARSET = utf8mb4 COMMENT ='订单行项目';
controller:
- /**
- * 微信支付->扫码支付(模式二)->统一下单->微信二维码
- */
- @GetMapping("/getQrcode")
- public void wxPayPay(@Param("订单号") @RequestParam("orderNum") String orderNum, HttpServletResponse response) {
- wxPayService.getPayQrcode(orderNum,response);
- }
service:
- /**
- * 微信支付->扫码支付(模式二)->统一下单->微信二维码
- *
- * @param orderNum 订单号
- * @param response 返回
- */
- @Override
- public void getPayQrcode(String orderNum, HttpServletResponse response) {
- String urlCode;
- // 获取订单信息
- WxpayVO vo = new WxpayVO();
- String outTradeNo = UUID.randomUUID().toString().replace("-", "");
- vo.setOutTradeNo(outTradeNo);
- // 账号信息
- vo.setAppId(wxPayConfig.getAppId());
- vo.setMchId(wxPayConfig.getMchId());
- vo.setKey(wxPayConfig.getKey());
- String currTime = PayToolUtil.getCurrTime();
- vo.setCurrTime(currTime);
- String strTime = currTime.substring(8, currTime.length());
- vo.setStrTime(strTime);
- String strRandom = String.valueOf(PayToolUtil.buildRandom(4));
- vo.setStrRandom(strRandom);
- String nonceStr = strTime + strRandom;
- vo.setNonceStr(nonceStr);
- vo.setSpbillCreateIp(wxPayConfig.getSpbillCreateIp());
- vo.setNotifyUrl(wxPayConfig.getNotifyUrl());
- vo.setTradeType("NATIVE");
- String totalFee = "1";
- vo.setTotalFee(totalFee);//价格的单位为分
-
- SortedMap
- packageParams.put("appid", wxPayConfig.getAppId());//公众账号ID
- packageParams.put("mch_id", wxPayConfig.getMchId());//商户号
- packageParams.put("nonce_str", wxPayConfig.getNonceStr());//随机字符串
- packageParams.put("body", "资源"); //商品描述
- packageParams.put("out_trade_no", wxPayConfig.getNonceStr());//商户订单号
- packageParams.put("total_fee", totalFee); //标价金额 订单总金额,单位为分
- packageParams.put("spbill_create_ip", wxPayConfig.getSpbillCreateIp());//终端IP APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
- packageParams.put("notify_url", wxPayConfig.getNotifyUrl());//通知地址 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
- packageParams.put("trade_type", "NATIVE");//交易类型 NATIVE 扫码支付
- // 签名
- String sign = PayToolUtil.createSign("UTF-8", packageParams, wxPayConfig.getKey());
- packageParams.put("sign", sign);
-
- // 将请求参数转换为xml格式的string
- String requestXML = PayToolUtil.getRequestXml(packageParams);
- log.info("requestXML:{}", requestXML);
-
- // 调用微信支付统一下单接口
- String resXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);
- log.info("resXml: {}", resXml);
-
- // 解析微信支付结果
- Map map = null;
- try {
- map = XMLUtil4jdom.doXMLParse(resXml);
- log.info("map: {}", map);
- } catch (JDOMException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- // 返回微信支付的二维码连接
- urlCode = (String) map.get("code_url");
- log.info("urlCode:{}", urlCode);
- if(urlCode == null){//测试
- urlCode = "http://192.168.50.231:8089/order/updatePayStatus?orderNum=" + orderNum + "&payStatus=" + PayStatus.WAIT_SEND.getValue();
- }
-
- try {
- int width = 300;
- int height = 300;
- //二维码的图片格式
- String format = "gif";
- Hashtable hints = new Hashtable();
- //内容所使用编码
- hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
- BitMatrix bitMatrix;
- bitMatrix = new MultiFormatWriter().encode(urlCode, BarcodeFormat.QR_CODE, width, height, hints);
- QRUtil.writeToStream(bitMatrix, format, response.getOutputStream());
- } catch (WriterException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
注意:
urlCode = "http://192.168.50.231:8089/order/updatePayStatus?orderNum=" + orderNum + "&payStatus=" + PayStatus.WAIT_SEND.getValue();
这一句是为了本地测试加的,因为微信支付首先要去微信申请开通微信支付:
二维码代表的内容就是urlCode的具体值,扫码模拟微信支付就会直接请求路径:http://192.168.50.231:8089/order/updatePayStatus?orderNum=" + orderNum + "&payStatus=" + PayStatus.WAIT_SEND.getValue(),进行订单状态更新,所以接口要开放白名单,即是无需登录即可请求。
当然如果是真实的微信支付,则需要配置支付回调地址,由微信官方通知服务器订单支付的状态,同样也要配置开放白名单,否则微信无法请求到我们的服务器。
微信回调:
controller:
- /**
- * 微信支付-回调
- * @param request request请求
- * @param response response返回
- */
- @PostMapping("/notify")
- public String wxPayNotify(HttpServletRequest request, HttpServletResponse response) {
- return wxPayService.wxPayNotify(request,response);
- }
service:
- /**
- * 微信支付回调
- *
- * @param request 请求
- * @param response 返回
- * @return 结果
- */
- @Override
- public String wxPayNotify(HttpServletRequest request, HttpServletResponse response) {
- //读取参数
- InputStream inputStream ;
- StringBuffer sb = null;
- try {
- sb = new StringBuffer();
- inputStream = request.getInputStream();
- String s ;
- BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
- while ((s = in.readLine()) != null){
- sb.append(s);
- }
- in.close();
- inputStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- //解析xml成map
- Map
map = new HashMap(); - try {
- map = XMLUtil4jdom.doXMLParse(sb.toString());
- } catch (JDOMException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- //过滤空 设置 TreeMap
- SortedMap
packageParams = new TreeMap(); - Iterator it = map.keySet().iterator();
- while (it.hasNext()) {
- String parameter = (String) it.next();
- String parameterValue = map.get(parameter);
- String v = "";
- if(null != parameterValue) {
- v = parameterValue.trim();
- }
- packageParams.put(parameter, v);
- }
-
- //判断签名是否正确
- if(PayToolUtil.isTenpaySign("UTF-8", packageParams, wxPayConfig.getKey())) {
- //------------------------------
- //处理业务开始
- //------------------------------
- String resXml = "";
- if("SUCCESS".equals((String)packageParams.get("result_code"))){
- // 这里是支付成功
- //执行自己的业务逻辑
- String mch_id = (String)packageParams.get("mch_id");
- String openid = (String)packageParams.get("openid");
- String is_subscribe = (String)packageParams.get("is_subscribe");
- String out_trade_no = (String)packageParams.get("out_trade_no");
-
- String total_fee = (String)packageParams.get("total_fee");
-
- //执行自己的业务逻辑
- productOrderService.updatePayStatus(out_trade_no,PayStatus.WAIT_SEND.getValue());
-
- log.info("支付成功");
-
- //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
- resXml = "
" + " " - + "
" + " "; -
- } else {
- resXml = "
" + " " - + "
" + " "; - return ("fail");
- }
- //------------------------------
- //处理业务完毕
- //------------------------------
- try {
- BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
- out.write(resXml.getBytes());
- out.flush();
- out.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- } else{
- log.info("通知签名验证失败");
- }
- return ("success");
- }
1.单一商品下单,微信扫一扫:
轮询查询订单状态:
当订单状态为完成支付时,倒计时5秒跳转:
- getByOrderNumFun(){
- getByOrderNum(this.orderNum).then(res => {
- if(res.code === 200){
- if(res.data.payStatus === 1){//待支付
- if(this.url === ''){
- this.url = '/api/wxPay/getQrcode?orderNum=' + this.orderNum;
- }
- }else if(res.data.payStatus === 2){//已支付
- clearInterval(this.inter);
- this.tipColor = '#67C23A';
- this.tip = '支付成功,正在为您跳转订单页面~';
- this.countDownFun(5,'/order/index');
- }else if(res.data.payStatus === 4){//已取消订单
- clearInterval(this.inter);
- this.tip = '订单已取消,正在为您跳转首页~';
- this.countDownFun(5,'/home/index');
- }else{
- clearInterval(this.inter);
- }
-
- }else{
- clearInterval(this.inter);
- }
- },error => {
- clearInterval(this.inter);
- })
- },
多个商品下单:
看到订单列表超过两个商品时显示样式有问题,修正优化~:
待支付,可以点击进行支付:
已付款,待发货:
可取消订单:
微信模拟扫码截图(忽略提示乱码):
感谢看到这里,需要源码或有什么问题和想说的请私聊!