跳至正文
Victor Ly

Balobrador 订购与支付

马德里附近一家芝士蛋糕店 balobrador.es 背后的订购与银行卡支付系统:商品目录、购物车、取货时间安排与 Stripe Checkout。它由我独自构建,自 2025 年 10 月起一直在处理真实付款,至今仍由我运营。

访问 balobrador.es
  • Stripe
  • Astro
  • React
  • TypeScript
  • Netlify Functions
  • Node.js
  • Zustand
  • Tailwind

它做什么

Balobrador 销售三种尺寸的芝士蛋糕以及曲奇,顾客到它位于 Rivas-Vaciamadrid 的门店自取。整个订单都在网站上完成:商品目录、关掉标签页也不会丢失的购物车、只开放厨房来得及出品之日的取货日历、由 Stripe 托管的银行卡结账,以及付款完成后发给顾客和店铺的确认邮件。页面是静态 Astro,只在需要响应交互的地方使用 React;服务器端是几个 Netlify Functions。没有数据库:代码仓库里的产品文件就是唯一可信来源,每一次调价、新口味或停业安排都作为一次提交发布,推送即部署。

价格由服务器决定

结账环节不信任浏览器发来的购物车。构建步骤把产品文件转换成一份按产品和尺寸索引的价格目录,结账函数据此重建每一个 Stripe 行项目:价格、名称和类别都来自服务器,数量被限定为允许范围内的整数,最低订单金额按目录价格校验,落在停业期内的取货日期会在任何支付会话创建之前就被拒绝。价格含 10% 增值税,并能精确换算成 Stripe 实际收取的整数欧分。webhook 会验证 Stripe 的签名,并且只依据它验证过的会话生成订单邮件。

把取货规则写成代码

厨房从周五开始把周末订单作为一批统一制作,所以周六、周日和周一的取货共用同一个截止时间:马德里时间周四 18:30。周二至周五取货需提前两天,截止时间同样是 18:30。我把规则写成需要提前的天数,而不是星期几的名称,因此跨周时同样成立。一次停业就是数据文件里的一条带日期的记录:它会把日历上的对应日期置灰,在每个页面和确认邮件里加上通知,让结账函数拒绝这些日期,并在结构化数据中发布特别营业时间。日期范围格式有误时,构建直接失败。

日常运营

自上线以来我一直在运营它,其间经历了一次涨价、几轮季节限定口味和一次为期两周的夏季停业,有两条教训如今已经成为流程的一部分。购物车连同价格快照一起保存在浏览器中,所以涨价时也会同时提升 schema 版本号,清空已保存的购物车,而不是显示一个价格、收取另一个价格。另外,曾有三次部署悄无声息地失败,Netlify 仍在提供上一次的构建,原因是我本地安装的依赖版本已经领先于锁文件,而我用到了锁定版本中并不存在的一个类型;如今凡是类型层面的改动,都从一次全新安装开始。构建检查必须以零错误、零警告、零提示结束。