订场「批准之后才付款」工作流文档

订场「批准之后才付款」· 工作流文档

分工只登记在仓库顶层的 BOUNDARIES.md,本页不另写。可跑的检查在附录

← 回 订场办活动页 /reserve PRD v0.2 · 同级:订场「批准之后才付款」PRD v0.1 · /reserve 订场页改版 · 提高转化的计划

主 PRD订场办活动页 /rese…附录 边界 · 仓库顶层 BOUNDARIES.md 定义什么 · 不定义什… 子 PRD订场「批准之后才付…附录 计划/reserve 订场页改版 · 提高… 计划 · 你在这订场「批准之后才付…附录

读图:这份工作流文档(图中高亮那一块)是「批准之后才付款」子 PRD 的配套细节页;往上能跳回子 PRD 或订场页主 PRD,旁边是主 PRD 原有的改版计划页。

状态机一览

一条订场请求从提交到订上,只有五个状态;其中两个是终态(到了就不再变)。状态字只在这份文档和附录里出现,正文里的意思照左边那一列的人话读。

状态(人话 · 代号)谁把它推到这一步从这里还能去哪这一步发的信
等审核
pending_review
客人提交订场请求(这一步不用先登录)店长批准 → 「等付款」;店长拒绝 → 「已拒绝」站内提交成功提示;另有一封全站通用的「收到你的请求」自动回信(六张表共用,已经在跑,这份文档不改它)
等付款
approved_awaiting_payment
店长点 Approve客人付款成功 → 「已确认」;店长点 Decline → 「已拒绝」(这条岔路会把已经开出的付款页作废);不会自动变化,没有「等太久自动作废」这条路建账号或登录才能付款的邀请信(两个版本,见下)
已确认
confirmed_paid
Stripe 回调告诉系统钱到了店长点 Cancel → 「已取消并退款」付款确认信 + 日历邀请
已拒绝
declined
店长在「等审核」或「等付款」任一阶段点 Decline(终态)拒绝信(没有收钱)
已取消并退款
cancelled_refunded
店长在「已确认」阶段点 Cancel(终态)取消退款信

一个安全网:如果店长对一条已经是「已确认」的请求误点了 Decline,系统不会假装「拒绝了但钱还收着」——会自动按「已取消并退款」处理,全额退款,跟主动点 Cancel 走的是同一条路、发同一封信。

逐步:谁做什么、出错怎么办

第 1 步客人提交订场请求

客人在 /reserve 页选店、说日期和人数,填姓名/邮箱/电话,直接提交——不需要先登录、也不需要已经有账号。请求落库,状态是「等审核」。

出错怎么办:必填项没填、邮箱格式不对——表单当场拦下,不提交。提交接口本身失败(网络、服务器)——客人看到失败提示,可以重试;不会出现「提交了但没落库」的中间态,因为落库是唯一的硬要求,其余通知都是尽力而为。

第 2 步店长审核

店长在后台看这条请求,决定 Approve、Decline,或者暂时不理它(停在「等审核」,店长还没看的时候也是这个状态,跟「已经看过但没决定」没有区别)。批准时系统按人数和单价算好金额,把金额存在这条请求上;同一个时间段如果已经有别的请求批准过或付过款,这条批不了,店长会看到「这个时段已经批过别的单」的提示。

出错怎么办:算金额、写状态失败(数据库出问题)——店长会看到失败提示,请求还停在原来的状态,可以重试,不会出现「状态变了但金额没存上」的半成品。批准之后发邀请信失败(邮件服务出问题)——状态照样推进到「等付款」,失败只记日志、报警到内部健康频道(Slack 的 #job-health,专收定时任务和后台故障),不拦店长的操作;这种情况下客人收不到信,店长在后台看不到自动提示,需要店长自己复核有没有发出去(这是现有机制,本卡不改)。

第 3 步系统发邀请信

批准那一刻,系统看这条请求当时是不是已经挂在某个账号名下(不只是「提交时是否登录」——提交之后、批准之前,客人也可能自己注册登录、被自动认领上):已经挂着账号 → 发「登录付款」版本;还没有 → 发「建账号付款」版本。两个版本都不带能直接点开付款的链接,只带一个站内地址。

出错怎么办:信没发出去(邮件服务出问题)的处理跟上一步是同一套:状态照常推进到「等付款」,失败记日志并报警,不拦店长的操作,店长需要自己复核有没有发出去。

第 4 步客人建账号或登录

收信人点邮件里的链接,打开账号页的注册或登录视图。用提交订场请求时那个邮箱注册(或者已有账号就直接登录)。登录成功那一刻,系统自动去核对「有没有邮箱跟我一样、还没挂账号的请求」,有就挂上——这一步每次登录账号页都会跑一遍,不是只在收到邀请信之后才跑一次。

出错怎么办:用了另一个邮箱注册——认领比对的是邮箱完全一样(大小写不敏感),邮箱不一样就认领不到,账号页历史区照样是空的;客人看不到这条请求、也就看不到付款按钮,需要店长人工把这一行的归属改过来(直接改数据库那一列,这一轮不建专门的界面)。认领接口本身失败(网络问题)——不拦账号页其余内容的加载,客人刷新一次账号页,登录状态还在,认领会重新跑一遍,不会出现「永久挂不上」。

第 5 步客人点 Pay now

账号页这条请求旁边出现「Pay now $X」按钮,金额是批准那一刻算好、存在这条请求上的数字。点击时系统先核对两件事:这条请求是不是点击的人自己的、状态是不是还在「等付款」——两条都通过,才带客人去 Stripe 的付款页;有一条不通过,直接告诉客人付不了(不新建付款页)。

出错怎么办:请求不是这个人的(比如链接被转发给了别人,或者两个人共用一个请求 id 瞎猜)——拒绝,不建付款页。状态已经不是「等付款」(已经付过、已经被拒、已经被取消)——拒绝,并告诉客人现在是什么状态。之前点过一次、这次又点——如果上一次开的付款页还开着,直接把客人带到同一个页面,不重复建;如果上一次的付款页已经过期或者已经付完,就新建一个(或者告诉客人已经付过了)。

第 6 步客人在 Stripe 付款

标准 Stripe 付款页,客人输入卡信息、确认支付。这一步完全在 Stripe 那边完成,系统只是把客人带过去、再等 Stripe 的回调。

出错怎么办:客人中途放弃——回跳到 /reserve 页,看到「已取消」的提示,请求状态还留在「等付款」,客人可以回账号页再点一次 Pay now。卡被拒——Stripe 自己处理,客人可以换卡重试,系统这边状态不变。

第 7 步系统确认付款

Stripe 付款成功后回调系统,系统按这次付款对应的请求 id 把状态推进到「已确认」,生成日历邀请文件,发确认信。

出错怎么办:Stripe 因为网络问题把同一次付款的回调发了不止一次——系统落库那一步天生只让第一次成功(数据库层面保证同一条请求不会被确认两次),后到的回调发现状态已经是「已确认」就直接跳过,不会重复发确认信、也不会出现「两条已确认的记录」。确认信或日历邀请发送失败——已确认的状态不受影响(落库优先于通知),失败只记日志、报警,不拦已经成功的付款。

第 8 步店长事后拒绝或取消

店长在「等审核」或「等付款」阶段点 Decline——不收钱,如果客人已经点开过付款页,那个付款页会被作废。店长在「已确认」阶段点 Cancel——全额退款,同样把可能还开着的付款页作废(正常情况下已确认的请求不会再有开着的付款页,这里是保险)。

出错怎么办:作废付款页这一步本身失败(Stripe 那边出问题)——店长这边的拒绝/取消照常生效并落库,只是那个旧付款页可能还能被打开;这种情况会被记下来、报警,需要人工二次确认那个页面别再被付掉(现有机制,本卡不改)。

每一封信的正文

都是英文,直接可抄;不承诺回复要多久,不提退税。前两封是这份 PRD 新加的,换掉了原来那条谁拿到都能付的裸链接;后三封现在已经在发、这份 PRD 不改。

新增 · 替换原来的邀请信批准时发 · 收信人当时还没有账号
Subject: Your Yummy Future event request: one step to confirm Hi {name}, Good news: {store} is open for {date}, {timeslot}. To lock it in, set up a free account and pay the reservation fee for your {guestCount} guests: ${amount}. Sign up with the same email address you gave us, {email}. That's how we'll find this request and put it on your account automatically. Set up your account: https://yummy-future.com/account.html#signup Once you're in, look for this booking on your account page. The "Pay now" button is right there. — Garrett, Yummy Future 中文:{store} 在 {date} {timeslot} 这一档给您留出来了。请用刚才填表那个邮箱({email})注册账号,登录后在账号页就能看到这条请求和付款按钮。
新增 · 替换原来的邀请信批准时发 · 收信人当时已经有账号
Subject: Your Yummy Future event request: one step to confirm Hi {name}, Good news: {store} is open for {date}, {timeslot}. To lock it in, sign in and pay the reservation fee for your {guestCount} guests: ${amount}. Sign in: https://yummy-future.com/account.html#signin Look for this booking on your account page. The "Pay now" button is right there. — Garrett, Yummy Future 中文:{store} 在 {date} {timeslot} 这一档给您留出来了。登录账号后,在账号页就能看到这条请求和付款按钮。
不改 · 现在已经在发店长点 Decline 时发
Subject: About your Yummy Future event request Hi {name}, Sorry — {store} can't hold {date}, {timeslot} after all. No charge was made. Want to try another date or store? Just reply and we'll find something that works. — Garrett, Yummy Future
不改 · 现在已经在发店长点 Cancel(全额退款)时发
Subject: Your Yummy Future event on {event_date} has been cancelled — refund issued Hi {name}, We're sorry — we have to cancel your confirmed event at {store} on {date}, {timeslot}. Your ${amount} has been refunded in full; it usually takes 5–10 business days to show on your statement. If you'd like to try another date, just reply to this email. — Garrett, Yummy Future 中文:非常抱歉,{date} {timeslot} 在 {store} 的预订需要取消,已全额退款,5–10 个工作日到账。
不改 · 现在已经在发付款成功、收到 Stripe 的回调(webhook,收款方付完钱主动来通知我们的那个请求)时发,带日历邀请附件
Subject: Confirmed — your Yummy Future event on {event_date} Hi {name}, You're all set. {store}, {event_date}, {timeslot} — the calendar invite is attached (one click to add it to your phone or laptop). We'll set up the space and the drinks; just show up. Questions before then? Reply to this email. — Garrett, Yummy Future 中文:您的预订已确认——{event_date} {timeslot},{store}。附件是日历文件,点一下就能加进日历。

边界情况问答

收信人一直没去注册或登录怎么办?
请求就一直停在「等付款」,不设自动过期,也不会自动发提醒信——这是全站 2026-09-13 拍板撤掉的时限承诺(出处:拍板台账那一条,落地在 netlify/functions/_lib/lead-reply/_busy.mjs 和锁 __tests__/lead-reply-busy-note.test.mjs),这份 PRD 延用同一条规矩。店长发现这条请求一直没动静,可以随时手动点 Cancel(这个状态其实等同于 Decline,没收过钱,走的是拒绝那条路,不是退款那条路——账号页会显示已拒绝)。
收信人用另一个邮箱注册怎么办?
认领只认「邮箱完全一样」(大小写不敏感),换一个地址就挂不上。邀请信里已经明说「请用提交时那个邮箱」;真挂不上时,店长在数据库里手动把这一行的归属改成对的账号——这一轮不建专门的界面来做这件事。
同一个邮箱提交了好几条请求怎么办?
登录后全部会被认领,账号页会列出好几行,每一行状态不同、各自有自己的「Pay now」按钮(只有状态是「等付款」的那些才会出现按钮)。
提交的时候客人本来就是登录状态怎么办?
这种情况请求一落库就已经挂在账号名下,不需要认领这一步。批准那一刻系统会发现这条请求已经有账号,直接用「登录付款」那个版本的邀请信,不会让客人误以为要重新注册。
客人正准备付款,店长这时候把请求取消了怎么办?
点 Pay now 的那一刻系统才去核对状态,不是靠邀请信或按钮本身来判断——核对发现状态已经不是「等付款」就直接拒绝,不会把客人带到一个还能付钱、但店长已经不认账的付款页。
客人重复点 Pay now,或者付款页已经过期了怎么办?
系统每次都先看这条请求上记的付款会话是否还开着:开着就把客人带到同一个页面,不重复创建;过期了或者已经付完了,就新建一个并覆盖记录。客人不会因为多点了一次而付两次钱,也不会因为链接放久了就点不动。
怎么保证付钱的人就是填表的那个人?
游客提交请求时不需要证明邮箱是自己的;但整个流程走到「能点 Pay now」之前,必须先用那个邮箱注册或登录一个账号——注册本身要求邮箱可达(收得到验证信才算数)。等于是「先证明这封信是你收到的,才轮到你付钱」,付款前就已经把邮箱核实过了。