首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【项目日志 | 苍穹外卖】 Day8:微信支付流程

【项目日志 | 苍穹外卖】 Day8:微信支付流程

作者头像
超级苦力怕
发布2025-12-23 17:58:10
发布2025-12-23 17:58:10
3690
举报

本章深入探讨微信支付流程实现,涵盖订单支付功能、内网穿透技术应用,以及如何绕过支付验证进行开发测试。通过实际代码演示,掌握支付接口集成与商户系统通信的核心技术要点。

在这里插入图片描述
在这里插入图片描述


完成任务清单

  • 导入地址簿相关源码
  • 用户下单功能
  • 阅读微信支付源码
  • 跳过订单支付功能

主要功能展示

这里由于我们没有正规的营业执照,所以我们并不能开通支付权限。所以只需要了解微信支付流程即可,同时如何访问到商户系统主要涉及到了内网穿透的知识点。 最后,我们需要把前端的部分代码改掉,以此来绕过微信支付


1.微信支付流程

我将微信支付全流程拆分成以下四个阶段,每个阶段多个步骤,以便于快速理解。

用户下单:分别为 用户进入小程序、 下单请求、 返回订单号 这三个步骤.

申请支付:分别为 申请微信支付, 调用微信下单接口、 返回预支付交易标识, 将组合数据再次签名, 返回支付参数 这五个步骤。

用户支付:分别为 用户确认支付、调起微信支付、返回支付结果、显示支付结果 这四个步骤。

支付结果同时与订单更新:分别为 推送支付结果,更新订单状态 这两个步骤。


微信支付全流程图

在这里插入图片描述
在这里插入图片描述
支付流程详解

第一阶段:用户下单

1.用户在微信小程序中选择商品,填写订单信息(微信用户 -> 微信小程序) 2.小程序向商户系统发送下单请求(微信小程序 -> 商户系统) 3.商户系统处理订单,返回订单号等信息(商户系统 -> 微信小程序)

相关代码 下单请求代码

代码语言:javascript
复制
@PostMapping("/submit")
@ApiOperation("用户下单功能")
public Result<OrderSubmitVO> submit(@RequestBody OrdersSubmitDTO ordersSubmitDTO){
    log.info("用户功能下单{}",ordersSubmitDTO);
    OrderSubmitVO vo=orderService.submit(ordersSubmitDTO);
    return Result.success(vo);
}

返回订单号信息代码

代码语言:javascript
复制
OrderSubmitVO build = OrderSubmitVO.builder()
    .id(order.getId())
    .orderTime(order.getOrderTime())
    .orderNumber(order.getNumber())  // 返回订单号
    .orderAmount(order.getAmount())
    .build();
return build;

第二阶段:申请支付

4.用户点击支付,小程序请求支付参数(微信小程序 -> 商户系统) 5.商户系统调用微信统一下单接口(商户系统 -> 微信后台) 6.微信返回prepay_id(预支付交易标识) 7.使用prepay_id等参数进行二次签名(商户系统内部处理) 8.返回小程序调起支付所需参数(商户系统 -> 微信小程序)

相关代码 申请微信支付代码

代码语言:javascript
复制
// sky-server/src/main/java/com/sky/controller/user/OrderController.java
@PutMapping("/payment")
@ApiOperation("订单支付")
public Result<String> payment(@RequestBody OrdersPaymentDTO ordersPaymentDTO) throws Exception {
    log.info("订单支付:{}", ordersPaymentDTO);
    orderService.payment(ordersPaymentDTO);
    return Result.success();
}

微信下单接口

代码语言:javascript
复制
private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception {
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("appid", weChatProperties.getAppid());
    jsonObject.put("mchid", weChatProperties.getMchid());
    jsonObject.put("description", description);
    jsonObject.put("out_trade_no", orderNum);
    jsonObject.put("notify_url", weChatProperties.getNotifyUrl());

    JSONObject amount = new JSONObject();
    amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
    amount.put("currency", "CNY");

    jsonObject.put("amount", amount);

    JSONObject payer = new JSONObject();
    payer.put("openid", openid);

    jsonObject.put("payer", payer);

    String body = jsonObject.toJSONString();
    return post(JSAPI, body);
}

预支付交易表示的数据格式

代码语言:javascript
复制
{
    "prepay_id": "wx201410272009395522657a690389285100",
    "code": "SUCCESS"
}

组合数据签名

代码语言:javascript
复制
public JSONObject pay(String orderNum, BigDecimal total, String description, String openid) throws Exception {
    // 统一下单,生成预支付交易单
    String bodyAsString = jsapi(orderNum, total, description, openid);
    JSONObject jsonObject = JSON.parseObject(bodyAsString);
    
    String prepayId = jsonObject.getString("prepay_id");
    if (prepayId != null) {
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = RandomStringUtils.randomNumeric(32);
        
        // 构造签名数据
        ArrayList<Object> list = new ArrayList<>();
        list.add(weChatProperties.getAppid());
        list.add(timeStamp);
        list.add(nonceStr);
        list.add("prepay_id=" + prepayId);
        
        // 二次签名
        StringBuilder stringBuilder = new StringBuilder();
        for (Object o : list) {
            stringBuilder.append(o).append("\n");
        }
        String signMessage = stringBuilder.toString();
        byte[] message = signMessage.getBytes();
        
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath()))));
        signature.update(message);
        String packageSign = Base64.getEncoder().encodeToString(signature.sign());
        
        // 构造返回数据
        JSONObject jo = new JSONObject();
        jo.put("timeStamp", timeStamp);
        jo.put("nonceStr", nonceStr);
        jo.put("package", "prepay_id=" + prepayId);
        jo.put("signType", "RSA");
        jo.put("paySign", packageSign);
        
        return jo;
    }
    return jsonObject;
}

返回小程序调起支付所需的参数

参数名

说明

timeStamp

时间戳

nonceStr

随机字符串

package

统一下单接口返回的 prepay_id 参数值

signType

签名算法

paySign

签名

第三阶段:用户支付

9.用户在支付界面确认支付(微信用户) 10.使用支付参数调起微信支付(微信小程序) 11.微信处理支付,返回结果给小程序(微信后台 -> 微信小程序) 12.小程序向用户显示支付结果(微信小程序 -> 微信用户)

第四阶段:支付结果通知与订单更新

13.微信异步推送支付结果到商户系统(微信后台 -> 商户系统) 14.根据支付结果更新订单状态(商户系统内部处理)

相关代码 支付结果通知

代码语言:javascript
复制
@RequestMapping("/paySuccess")
public void paySuccessNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 读取数据
    String body = readData(request);
    log.info("支付成功回调:{}", body);
    
    // 数据解密
    String plainText = decryptData(body);
    log.info("解密后的文本:{}", plainText);
    
    JSONObject jsonObject = JSON.parseObject(plainText);
    String outTradeNo = jsonObject.getString("out_trade_no");//商户平台订单号
    String transactionId = jsonObject.getString("transaction_id");//微信支付交易号
    
    // 业务处理,修改订单状态、来单提醒
    orderService.paySuccess(outTradeNo);
    
    // 给微信响应
    responseToWeixin(response);
}

更新订单状态

代码语言:javascript
复制
public void paySuccess(String outTradeNo) {
    // 根据订单号查询订单
    Orders ordersDB = orderMapper.getByNumber(outTradeNo);
    
    // 根据订单id更新订单的状态、支付方式、支付状态、结账时间
    Orders orders = Orders.builder()
        .id(ordersDB.getId())
        .status(Orders.TO_BE_CONFIRMED)  // 更新订单状态
        .payStatus(Orders.PAID)          // 更新支付状态
        .checkoutTime(LocalDateTime.now())
        .build();
    
    orderMapper.update(orders);
    
    // 通过websocket向客户端浏览器推送消息
    Map map = new HashMap();
    map.put("type", 1);  // 1 来单提醒 2 客户催单
    map.put("orderId", ordersDB.getId());
    map.put("content", "订单号" + outTradeNo);
    String jsonString = JSON.toJSONString(map);
    webSocketServer.sendToAllClient(jsonString);
}

2.微信支付流程涉及知识点
内网穿透

内网穿透(NAT traversal)是一种技术,用于实现公网与内网之间的通信连接。 当内网中的设备无法直接从公网访问时,内网穿透技术可以通过一些手段,让公网上的设备能够穿透到内网中的设备,建立起通信连接。

业务场景 微信后台会调用到商户系统给推送支付的结果,但由于未上线的缘故,微信后台无法请求到商户系统。

解决思路 通过cpolar软件可以获得一个临时域名,而这个临时域名是一个公网ip,这样,微信后台就可以请求到商户系统了。

实现流程 在命令提示符中输入cpolar.exe http 后台服务端口后,就会获取临时域名,使用临时域名访问成功即可。

在这里插入图片描述
在这里插入图片描述
绕过微信支付

先到小程序开发工具,在pages下的index.js的第226行,将下面的框注释掉

在这里插入图片描述
在这里插入图片描述

并将下面的重定向的代码给取消注释

在这里插入图片描述
在这里插入图片描述

这样,当成功支付时,就不会再调用微信支付的API了,并直接回调

拓充知识点

为什么前端有了相应的验证,而后端仍需编写

前端主要是逻辑验证,后端是严格验证

由于前端主要是在用户浏览器/小程序中执行,因此在前端有着许多的逻辑验证,这些主要是为了减少无效请求,从而达到提升用户体验提供及时反馈的作用。(前端验证的作用)

但同时,前端可以通过如Postman为例的接口测试工具,去进行恶意攻击或绕过验证,如伪造支付回调调用支付接口等。

后端验证是在服务端执行,无法被用户修改,确保了数据安全性和业务逻辑正确性


本文为苍穹外卖项目学习笔记,持续更新中…

如果我的内容对你有帮助,希望可以收获你的点赞、评论、收藏。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 完成任务清单
  • 主要功能展示
    • 1.微信支付流程
      • 支付流程详解
    • 2.微信支付流程涉及知识点
      • 内网穿透
      • 绕过微信支付
  • 拓充知识点
    • 为什么前端有了相应的验证,而后端仍需编写
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档