news 2026/9/29 16:54:53

外卖商城微信小程序开发全攻略:从登录到支付的核心实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
外卖商城微信小程序开发全攻略:从登录到支付的核心实践

在做 weixin129 外卖商城平台时,我做的第一件事不是搭项目骨架,而是先和团队吵了一架:到底做 App 还是做微信小程序?当时外卖业务刚起步,老板觉得做 App 更像一个“平台”,但我很清楚,对大多数本地商户和用户来说,微信小程序才是那个能先跑通的入口。实际运营数据后来也支持了这个判断,所以这篇文章就围绕 weixin129 外卖商城平台的微信小程序开发过程,聊聊业务模块、登录体系、购物车、微信支付、审核维护这些关键环节。如果你是第一次做外卖类小程序,或者想做一套能落地的微信小程序外卖商城,这篇文章基本可以当成一份踩坑地图来用。

1. 为什么 weixin129 一上来就锚定了微信小程序

1.1 项目背景:本地商户的态度决定了入口选择

weixin129 是我手里一个外卖商城平台项目的内部代号,面向的是本地几家餐饮和生鲜商户。用户通过微信小程序完成浏览、点餐、支付、订单跟踪,不需要额外下载 App。项目启动时,团队对“平台”二字有执念,总觉得必须有独立应用才算完整,但我的结论恰恰相反:先做微信小程序。

原因很简单:外卖用户的决策链路很短,大多数人是看到商户桌贴、朋友圈转发、公众号推文里的二维码,扫码进来下单。如果让他们去应用商店搜索、下载、注册、登录,这一步就能过滤掉一大半用户。微信小程序天然处在这个链路里:扫码即开、用完即走,不用安装,也省去了“新用户下载 App”的决策成本。后来首月数据显示,超七成复购用户是从分享卡片和小程序码进来的,App 版本根本排不上优先级。

1.2 比起 Android/iOS/鸿蒙三端,微信小程序起步成本低太多

如果按原生方式同时做 Android、iOS、鸿蒙,对一个小团队来说非常吃力:三套客户端、三套发布流程、三套适配规则,光维护成本就够喝一壶。weixin129 前期只有两名前端,根本撑不起多端原生开发。微信小程序虽然也是“一个新端”,但它有完整的开发、审核、发布和运营体系,一套代码就能覆盖微信内用户,支付和订阅消息也跟微信生态打通。

有人会提 uniapp,说做一套代码可以同时编译小程序、App、H5。这是个好选项,但不是没有代价。uniapp 的抽象层在多数业务场景很顺畅,可一旦遇到“微信原生能力较深”的功能,比如自定义导航栏、手机号授权、订阅消息,经常要写条件编译或者调用平台特有 API,调试成本比纯微信小程序高。weixin129 最终选择的是微信原生小程序,因为核心订单、支付、配送通知全部依赖微信生态,短期内也不准备发布苹果和安卓。如果未来需要多端,再迁移或借助 uniapp 重新包装,比一开始硬上多端框架省心。

1.3 角色端拆分:先想清楚谁在用这套平台

这个平台并非只有用户端微信小程序,还涉及商家端、骑手端和后台管理。不过项目标题是“外卖商城平台的微信小程序”,所以用户端小程序是主战场。商家端也可以用小程序给商家接单,骑手端初期甚至可以用 H5 简化。

角色使用端核心职责
用户微信小程序选购、下单、支付、订单跟踪、评价
商家微信小程序/H5菜品管理、接单、出餐、门店信息
骑手H5/微信小程序接单、导航、送达确认
平台运营Web 管理后台商品审核、优惠券、结算、数据

这种拆分意味着用户端小程序要承担最高频的交易动作,稳定性比功能丰富更优先。开发时我会把核心下单链路单独拉出来做专项测试,而不是把时间平均花在所有页面上。

2. 外卖商城的模块拆解与订单主链路

2.1 用户端:首页、菜单、购物车、订单至少四大块

用户端小程序第一屏是首页,通常包含定位商圈、商家分类、商家列表和轮播位。商家列表要支持按距离、销量、评分排序;如果用户从某篇推广文章点进某个商家,还需要通过页面路径参数shopId直达,不能只落在一个通用首页上。

第二层是商家详情页,这里最容易做砸的是“菜单加载”和“购物车”联动。菜单要按分类分组,每个菜品包含价格、月售、图片、规格选项;加购时不能只传“菜名+数量”,必须把 skuId、规格、加料、备注一起带上。我的习惯是菜单数据请求完成后做一个本地增量缓存,用户重复进入时省掉一次 loading 时间,但价格和库存每次进入商家页都要求后台刷新。

订单页需要按状态分 Tab:待支付、待接单、配送中、已完成、售后。外卖订单的状态变化快,前端要配合下拉刷新和定时轮询。在用户端,尤其要避免“支付成功后页面卡在待支付”这种体验,这非常影响信任感。

2.2 商家端和配送环节

商家端虽然也是小程序,但它的定位不是“展示”,而是“操作台”:接单、拒单、设置营业状态、上下架菜品、打印小票。外卖平台的出餐效率很大程度取决于商家端能否在第一时间收到新订单。这里的方案有两种:商家在小程序里轮询新订单,或者后端通过微信订阅消息推送“新订单提醒”。对于单量高的商家,我更推荐接入第三方点单打印机,用 WebSocket 或 MQTT 推送,但复杂度会更高。weixin129 初版先用了订阅消息加轮询兜底,等功能稳定后再接打印机。

骑手端如果刚开始没有自建运力,可以先用第三方同城配送回调来简化。weixin129 初版没有独立骑手 App,直接由商家自己选择跑腿平台,后台记录配送单号,状态更新靠电话沟通。等订单量上来,再去考虑自建调度系统。

2.3 一个完整的订单链路

用户从打开小程序到订单完成,主链路大致如下:

  1. 用户定位、选择门店;
  2. 选择菜品、规格,加入购物车;
  3. 确认购物车、地址、配送时间;
  4. 后端创建订单,状态变为“待支付”;
  5. 微信支付回调成功后,状态变为“已支付/待接单”;
  6. 商家端接单,开始备餐;
  7. 备餐完成,配送员取餐,状态变为“配送中”;
  8. 用户确认送达,订单变为“已完成”;
  9. 如果超时未支付,定时器自动关闭订单;商家未接单超时,系统自动取消或转单。

这条链路是所有订单业务的地基。我们在这条路上吃过不少亏:支付回调重复、商家拒单后金额退回、状态跳转不确定。解决办法是:每个状态变更都记录操作人和触发源,不允许前端直接改订单状态,所有状态变更必须由后端在带校验的接口里完成。

3. 登录、Token、请求封装和缓存,这层不能省

3.1 wx.login 与手机号授权要分开看

外卖平台需要用户手机号,目的是联系配送和售后。微信小程序常见做法是:用wx.login拿到的 code 换 openid,然后在小程序里用<button open-type="getPhoneNumber">获取手机号;手机号不能直接由前端获得,更不能把用户手机号密码暴露给客户端。

实际开发中有个坑:很多人以为调用一次wx.login后,后端再拿 code 换 session_key,就能永久识别用户。实际上 session_key 会过期,换到的 openid 才是稳定的。为了避免每次登录都重新生成业务 Token,我们通常这样设计:首次进入用wx.login换 code,后端用 code 换取 openid 并生成平台 Token 返回给前端;后续接口请求带上 Token,后端不再依赖微信的 code。需要手机号时,再通过手机号快速验证组件把得到的动态 code 发给后端,后端调微信接口换取真实手机号,并和用户账号绑定。

3.2 Token 过期、静默刷新与并发重放

Token 一定会过期,尤其是微信小程序这种“即用即走”的场景,用户可能隔几小时再回来。如果后端返回 401 后前端直接弹窗让用户重新登录,体验会非常差。最好做成静默刷新。

请求模块需要统一处理 401:收到 401 后,判断是否有 refreshToken;如果有,先暂停当前请求队列,调用刷新 Token 接口,拿到新 Token 后重放刚才失败的请求。这里必须处理并发问题:如果同时有 5 个请求都返回 401,5 个请求都去调用刷新接口,会造成重复刷新。常见做法是用一个isRefreshing标志位加一个等待数组:

let isRefreshing = false let refreshQueue = [] function handle401(request) { if (!isRefreshing) { isRefreshing = true return refreshToken().finally(() => { isRefreshing = false refreshQueue.forEach(cb => cb()) refreshQueue = [] }) } return new Promise(resolve => refreshQueue.push(resolve)) }

这样只有第一个 401 会触发刷新,其他请求等待刷新完成后再重放,避免并发刷新导致多个 Token 同时失效。

3.3 请求封装需要处理的三类场景

外卖商城接口少说也有几十个,如果每个页面直接写wx.request,后续切域名、加鉴权、统一错误处理都会非常痛苦。我把请求封装收敛到一个request.js里,统一处理三类异常:

  • 网络不通:wx.request的fail回调,提示“网络连接失败”,但不要频繁 toast;
  • 超时:设置timeout,普通查询 10 秒,下单接口 15 秒;下单时按钮要防重复提交,用户点击后立刻置灰;
  • 业务码异常:后端返回code != 0,不能统一弹“系统错误”,要根据码表给可读文案。比如SOLD_OUT提示“菜品已售罄”,ORDER_FINISHED提示“订单已完成”。

另外,每个请求都要带一个请求序列号或时间戳,方便联调时在后端日志里定位问题。微信小程序联调时,如果后端看不到请求 ID,排查问题会非常难受。

3.4 缓存时间的设计:不是所有数据都能缓存

微信小程序 API 里只有wx.setStorageSync,没有自带过期机制。我们封装了一个带 TTL 的缓存方法,存值时写入expireAt,读取时判断是否过期。外卖商城里,商家列表可以缓存 5 分钟,菜单缓存 2 分钟,首页轮播缓存 10 分钟,但购物车和订单状态绝对不能缓存,必须实时请求。

为什么要注意这个?因为外卖的库存、价格、营业状态变化很快。如果菜单缓存时间设置太长,用户看到有货但下单时提示已售罄,会对平台失去信任。而且微信小程序本地缓存有体积上限,过期的数据要及时清理,避免后续写入失败。对网络质量差的用户,页面可以先显示旧缓存,再后台刷新新数据;如果刷新失败,至少还有内容可看,不会白屏。

4. 购物车、SKU 规格和订单状态机

4.1 购物车不能只存一个“数量”

加购看似简单,实际在外卖场景很复杂。不同门店的菜不能放在同一个订单里,所以购物车首先要按shopId分组。同一个菜品可能有不同规格:大杯、小杯、加冰、少冰,不同规格价格不同。购物车item的结构至少是这样:

{ shopId: "shop_001", shopName: "某茶饮", items: [{ skuId: "sku_1001", spec: ["大杯", "少冰"], extraIds: ["珍珠", "奶盖"], price: 1800, // 单位分 count: 1 }] }

加购、减购时,不要通过遍历所有商品找匹配项,最好为每个商品生成一个唯一的cartKey(skuId + spec + extraIds + 备注),用 Map 做加减。最后下单时,后端还要重新校验价格和库存,不能信任前端传来的金额。

4.2 SKU 库存与超卖

外卖菜品库存和电商 SKU 不太一样,很多餐饮店希望“上午设置当日库存,卖完即止”。项目里用后端库存扣减,不是前端控制按钮。当用户提交订单时,后端检查库存;扣库存时使用条件更新:

UPDATE product_sku SET stock = stock - #{count} WHERE sku_id = #{skuId} AND stock >= #{count}

如果影响行数为 0,说明库存不足,立即返回售罄。订单支付超时后,需要做反向操作把库存释放。这里要注意:预占库存的订单如果一直不支付,会造成其他用户无法购买,所以必须有一个定时任务关闭超时未支付订单并回补库存。

4.3 订单状态机的核心流转

用表格列一下基础状态:

状态值含义触发者前置条件
0待支付用户提交订单购物车有效
1已支付待接单支付回调支付验签通过
2已接单备餐中商家操作状态为1
3配送中骑手/商家操作状态为2
4已完成用户确认/系统自动状态为3
-1已取消用户/超时状态为0
-2已退款平台/售后状态可为1/2/3

这个状态机不允许任意跳转,比如用户不能从“备餐中”直接取消,只能申请退款。支付回调触发 0 到 1 时必须幂等,否则回调重复到达,订单状态会被覆盖。我们的做法:记录微信侧transaction_id,更新订单时带上条件WHERE status = 0,如果更新行数为 0,说明订单已经处理过,直接返回成功。

5. 微信支付、退款与结算流程里的那些坑

5.1 金额单位与支付参数

微信支付和相关接口的金额单位都是“分”。前端展示用“元”,后端算费用时必须转成整数分,并且避免浮点数运算。比如用户订单金额 28.9 元,后端要按 2890 分传给支付接口。很多初学者直接把total_fee设为29.00,最后对不上账。

预支付订单参数中会用到openid、out_trade_no、total_fee、notify_url。out_trade_no要用后端生成的唯一订单号,不要用前端传过来的订单号拼接,否则容易被伪造。签名和证书一定只放在服务端,小程序端只需要拿wx.requestPayment所需的支付参数。如果后端在处理回调时发现金额不一致,要主动调微信支付接口查询真实支付金额,再去更新订单,不能直接信任回调里带的金额。

5.2 回调验证、幂等与补偿

wx.requestPayment成功只能作为“用户已完成支付”的提示,不能作为订单状态的最终依据。微信支付成功通知是异步的,可能延迟,也可能重复推送。服务端收到通知后,必须校验回调签名、校验商户号、校验订单金额,再更新订单。

一个比较稳的方案是:支付回调只负责置为“已支付”,不做太多业务动作。同时加一个定时任务,每隔一段时间扫描“用户已支付但订单状态还是待支付”的订单,拿着订单号向微信支付查询,如果确实已支付则补更状态。前端轮询订单状态可以使用“支付成功后每秒查一次,连续查 5 次”,如果还是待支付,就提醒用户“支付结果确认中,请稍后刷新”,不要直接宣布失败,更不要让用户二次支付。

5.3 退款、优惠券与商家结算

外卖平台的退款比电商还难,因为涉及商家、平台、配送费三方分摊。用户申请整单退款时,如果商家已出餐,通常会拒绝;如果商家未接单,平台自动退款。退款接口也需要做幂等,微信支付退款后会有退款回调,需要用out_refund_no去重。

优惠券的处理也要提前约定:用掉的满减券在退款后是否退回?一般约定一次性优惠券不退,避免用户“退款后继续用券”的漏洞。商家结算是按周期计算“用户实付金额 - 平台抽佣 - 配送费”,遇到退款订单要先冻结商家收益,等退款结果确认后再做对应扣减。不提前想清楚这层,后台财务模块很容易在第一个月就崩。

6. 导航栏、定位、图片和 iOS 请求失败,这些细节决定体验

6.1 自定义顶部导航栏的高度适配

外卖商城的首页通常要把搜索框放进顶部导航栏,所以需要自定义导航栏。微信小程序的胶囊按钮和状态栏高度在不同机型差别很大。如果写死padding-top: 20px,iPhone 刘海屏会顶上去,老机型又太空。

建议调用wx.getWindowInfo()取statusBarHeight,再调用wx.getMenuButtonBoundingClientRect()取胶囊位置,动态计算导航栏高度:

const statusBarHeight = wx.getWindowInfo().statusBarHeight const menuButton = wx.getMenuButtonBoundingClientRect() const navBarHeight = (menuButton.top - statusBarHeight) * 2 + menuButton.height

把计算出的高度放到全局数据里,页面用内联样式设置padding-top,这样能兼容绝大多数设备。虽然也有现成组件,但外卖首页的导航栏通常还要嵌入搜索框和定位按钮,自己封装反而更好用。

6.2 定位权限和配送范围别让后端背锅

外卖商品的配送范围判断,不只是前端的事。用户点“我要定位”后,wx.getLocation会弹授权框。如果首次拒绝,下一次再调用不会自动弹,需要在页面上做引导:先检查wx.getSetting,若授权已关闭,就提供一个“去设置”按钮,调用wx.openSetting打开设置页。

拿到经纬度后,前端可以展示距离,但能否配送应该由后端判断。前端拿到地址坐标可能在商家配送范围边缘,导致下单后被拒。我们是在后端把每个门店的配送半径配好,根据经纬度计算距离。如果超出范围,下单页直接提示“当前地址超出该门店配送范围”,并推荐附近的同类商家,而不是等用户填完地址再报错。

6.3 图片处理:CDN、压缩和 canvas

小程序主包限制很严格,外卖商城图片极多。所有菜品图、商家 logo、广告图都要走 CDN,不能塞进本地包。开发时可以用相对路径,上线前必须全部切到 HTTPS 的 CDN 域名,并在小程序后台配置downloadFile合法域名。

列表页的图片要按尺寸压缩,比如商家列表缩略图宽度 350px,菜品缩略图 200px;详情页再用原图。不要前端直接加载原图,这会消耗用户流量,也很容易在 iOS 大图上导致页面卡顿。分享海报用 canvas 绘制时,图片必须先在本地downloadFile得到临时文件再画,不能直接画网络图片,否则某些机型的 canvas 会空白。

6.4 iOS 网络请求失败率偏高的排查经验

有段时间 weixin129 收到反馈,说 iPhone 上某些页面网络请求失败率明显比安卓高。排查下来,主要原因是接口域名证书链不完整,或者部分接口还走 HTTP,被系统的传输安全策略拦截。微信小程序环境同样有安全要求,线上环境必须是备案过的 HTTPS 域名。

另一个常见原因:小程序后台的 request 合法域名配置错误,或者域名证书缺少中级证书。验证方法是直接用 iPhone 浏览器访问接口域名,看有没有证书报错;再到微信开发者工具“详情-域名信息”里检查。另外,WebSocket 连接也需要配置 socket 合法域名,如果频繁断连,大概率是域名配置或证书问题,而不是代码逻辑问题。

7. 上架审核、年审和运维,别等项目火了才想起

7.1 资质材料和体验账号要提前准备

外卖商城小程序审核时,平台会要求提供对应资质。餐饮外卖需要《食品经营许可证》或类似证件,资质主体要和小程序认证主体一致。类目选错很容易被驳回,建议在准备阶段先查最新的微信小程序类目要求,比如“餐饮-外卖服务”“生活服务-同城配送”等。

第一版提交审核前,最好做一个“体验账号说明文档”,把商家后台账号和测试门店地址提供给审核人员。审核人员不会记住每个门店的菜名,所以要给可复现步骤:打开小程序、定位门店、下单、支付、取消。如果没有说明文档,对方可能卡在找不到商家或无法验证功能上。

7.2 审核驳回的典型原因

外卖平台容易碰到这些驳回点:用户隐私政策缺失、收集位置和手机号时未声明用途、分享卡片涉嫌诱导分享、营销弹窗误导、虚拟商品支付。尤其是“支付”环节,小程序平台对虚拟支付管控很严格,但外卖属于真实生活服务,正常微信支付即可。不要搞“先充值再消费”的预充值模式,审核风险较高。

另一个隐蔽问题:首页或详情页不能夸大宣传,比如“零利润”“全网最低价”这类文案,审核和后续投诉都很麻烦。小程序的宣传文案要基于真实活动,不能为了曝光写绝对化用语。

7.3 年审、灰度发布和监控

微信小程序认证每年需要年审,年审到期时微信官方会提醒。如果平台主体资质没有变化,按时提交即可;但很多人会忽略,导致搜索和支付能力受影响。这里提醒不要拖到过期再处理。

版本发布时,不要一上来就全量发布。可以用微信后台的“分阶段发布”,先放 10% 的用户,观察错误率和用户反馈,再逐步放量。后端要有按渠道统计错误日志的能力,至少监控下单接口、支付回调、订单状态查询的成功率和耗时。有一次我们发现 iOS 用户支付成功率高但订单创建失败率高,最后定位到是某个页面参数在 iOS 上被转成了NaN,如果没有监控,这个问题会被用户骂很久才发现。

最后放一个我最想说的提醒:不要在线上环境为了省事打开开发版或者使用未审核的版本,所有业务验证都要走正式版和体验版。外卖商城是强交易产品,一个不起眼的缓存错误都可能造成用户下了单却收不到餐。做 weixin129 的过程中,体验版在我们手里过了几百遍,真正筛选出来的基本都是这类很细但很关键的稳定性问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 16:54:05

研究生AI论文写作软件测评:十款工具组合方案全解析

研究生这两年&#xff0c;最不缺的就是“写论文”这件事。从选题到综述&#xff0c;从初稿到返修&#xff0c;每一环都在跟时间和心理承受力较劲。前两年大家还在用翻译软件和Word查找替换&#xff0c;现在已经人手好几个AI工具了。我前后花了小半年&#xff0c;把市面上主流的…

作者头像 李华
网站建设 2026/9/29 16:51:49

识人三步法:定标准、采信号、做验证,挖透一个人

做识人断事这些年&#xff0c;我老王被问最多的一句话是&#xff1a;怎么才能真正挖透一个人&#xff1f;要么是HR朋友说候选人面试时表现完美&#xff0c;入职三个月原形毕露&#xff1b;要么是创业者说合伙人谈的时候掏心掏肺&#xff0c;分钱的时候翻脸不认人。说到底&#…

作者头像 李华
网站建设 2026/9/29 16:50:50

Java编译报错“invalid source release: 16”根源与彻底修复指南

你有没有过这种经历&#xff1a;在 start.spring.io&#xff08;Spring Initializr&#xff09;上选好 Spring Boot 版本、点几下鼠标下载项目压缩包&#xff0c;IDEA 里一打开&#xff0c;还没写任何业务代码&#xff0c;编译就直接抛红&#xff1a;java: 无效的源发行版: 16。…

作者头像 李华
网站建设 2026/9/29 16:50:28

Spring Boot教务管理系统开发全解析:从表设计到答辩要点

每年到毕业季&#xff0c;Java方向的选题榜上&#xff0c;“教务管理系统”几乎雷打不动地出现在前三名。很多学生看到这个题目&#xff0c;第一反应是“不就是一堆增删改查嘛”&#xff0c;但真正上手之后才发现&#xff1a;角色权限怎么控制、选课冲突怎么判断、成绩修改要不…

作者头像 李华
网站建设 2026/9/29 16:50:28

供应商管理系统实战:SpringBoot2+Vue3+MySQL8.0全栈开发

1. 供应商管理系统到底在做一件什么事很多朋友看到"供应商管理系统"这几个字&#xff0c;第一反应是"这不就是一套围绕供应商数据的增删改查吗"。一开始我也是这么想的&#xff0c;但真正把需求理清之后才发现&#xff0c;供应商管理远不止登记一个公司名称…

作者头像 李华
网站建设 2026/9/29 16:50:17

starnet桌面AI Agent框架:MCP协议与OpenRouter模型调度实战

1. 从“starnet”这个名字说起&#xff1a;它到底想解决什么问题第一次看到“starnet”这个项目标题&#xff0c;加上旁边一串热搜词——AI agents、desktop harness、OpenRouter、MCP——我脑子里第一反应是&#xff1a;这大概率是一个把“桌面端 AI 智能体”和“模型调用网关…

作者头像 李华