全程 AI 写的小程序,刚接上会员虚拟支付,上线第二天就有人真金白银买单了|双签名、定价、iOS 的坑我全踩了一遍
厨菜记是我一个人做的家庭菜谱小程序。10 月 12 号我把它的会员体系正式上线了——两档付费会员、六个 SKU,支付走微信小程序虚拟支付。同一天顺手把 AI 荐菜 / AI 识别 / AI 生图也推了上去。
一个陌生人,真金白银给我开了会员。从 9 月 18 号上架到今天,满打满算 25 天,一个月都不到。上一篇写流量主的时候我还在算"第一个月收入三十多块",那会儿收费这条路我连资质都没有;现在会员真来了一单,金额很小,但性质跟广告那几块钱完全不是一回事——广告是平台按曝光赏我的,会员是有人认可这个产品、主动掏的。
写这篇不是报喜,是想把「一个小程序到底怎么把虚拟支付和会员跑通」这件事从头到尾记一遍:为什么非走虚拟支付不可、六档价格是怎么算出来的、下单到发放那两把签名钥匙卡在哪、iOS 又有哪些后台门槛。数字和坑都是一条条核过的,代码大部分是 AI 写的,但越到钱这一步我越不敢全交给它——原理我得自己看懂才敢上线。口径不一致的地方我直接写出来。
一单。就一单。搁大厂那是一笔都进不了报表的数,但对我来说,它是第一笔"用户主动掏钱"而不是"平台按曝光分给我"的钱。这两笔钱的来路不一样——广告那几块钱是我把人留在页面上、平台赏我的;这一单是有人觉得这个小程序值这个价,自己点开的。
卖会员这种虚拟商品,2026 年起微信有条硬规矩:
得单独开通"虚拟支付"这个能力、签协议、建道具。普通微信支付我熟,unifiedorder 那套签名早烂了;换成虚拟支付是另一条链路,接口叫 wx.requestVirtualPayment,钱也不走我的商户号,走的是微信的虚拟支付账户,按苹果/安卓分账。这一步 AI 帮不上忙——它是后台配置和一整套审核流程,我只能自己一遍遍点。
六档定价不是拍脑袋,是对着竞品「独家御膳房」一档一档压出来的,也是被 iOS 那条"最低 1 元"的地板卡着定的:
畅厨·月首月 ¥2.9 是转化钩子,阶梯顶只做到年付,三年卡和永久卡我明确不做——理由很实在:服务器我按年续、AI 生图有真实赠额成本、产品随时可能停,"永久"这两个字进了用户账单还属违规口径。竞品能卖 ¥98.9 的永久卡,那是它的风险预算,不是我的。
定价之前我还被 iOS 的费率上了一课。安卓走微信支付,iOS 走 Apple 支付,苹果要抽成,粗略按 88% 到手算:
所以 ¥1.9 那档根本不是利润档,是"先让人迈进门"的价格。这笔除法 AI 不会主动替我做——它不知道苹果抽成,也不知道我为什么要贴钱做钩子。
这是我最想聊的一段。整个下单到发放的链路,AI 是这么给我串下来的:
链路看着顺,AI 也写得顺。真正让我停下来一行行啃的,是那个 sign 里的两把签名:
两个签名、两把不同的钥匙,缺一不可。我逼自己搞懂的是这三件事——它们每一个 AI 都能给我写"对",但写错的时候它不会提醒我:
第一,两把钥匙都不能下发到前端。 AppKey 是虚拟支付的现网密钥,session_key 是用户登录时微信给的会话密钥,两个都只能在服务端算签名。这不是我谨慎,是原理决定的:一旦下发,任何人都能伪造任意价格的订单。这种安全边界,AI 默认会写对,但如果你不知道"为什么必须放服务端",将来重构时你很可能亲手把它挪到前端去——它不会拦你。
第二,signData 这个字符串,前端一次都不许多 JSON.stringify。 服务端算签名用的是某一顺序拼出来的 signData 原文,前端必须原封不动透传同一段字符串去调 requestVirtualPayment。哪怕字段一个没变、只是序列化顺序不同,微信那边重算签名对不上,直接 -15006 拒付。这个坑极其隐蔽——本地测试偶尔过、偶尔不过,你根本想不到是 JSON 键顺序在捣鬼。所以我后端把 signData 生成好,原样返回,前端连碰都不能碰。
第三,session_key 我之前根本没存。 这个是整版对既有代码改动最大的一处。登录链路里我早就调了 jscode2session、也拿到了 session_key,但只用了 openid 和 unionid,session_key 用完就丢了。可虚拟支付算 signature 必须要它。AI 能把签名函数写得漂漂亮亮,但它不会主动回头去翻我登录那个类里"拿过却没用"的字段,也不会告诉我"你得把它存进 Redis、还得跟 token 同 TTL"。这个是我自己顺着 signature 需要的入参,倒查到 session_key,再倒查回登录服务的。用户侧的表现是:session 过期了下单会报 -15007,前端得静默重新登录刷一遍再重试一次。
发放那头的原理同样不能含糊。微信的发货通知会被丢,所以我不靠回调直接放行,而是收到通知后先调 query_order 主动查单复核一遍,确认微信侧真的付了钱才发放;发放本身用状态位 0→1 的 CAS 做幂等,同一个人同一单被推两次也只发一次。跨档位续费的折算(低档买高档按剩余天数折、同档顺延)这套规则 AI 一次没写对过,是我拿计算器一档一档试出来的。
一句话:AI 负责把这条链跑通,我负责判断它在钱这件事上跑通的是不是我敢上线的那条。
Android 上跑通了不代表 iPhone 能付。iOS 走的是苹果 IAP,得满足一堆前置条件,全是后台配置和系统门槛,跟代码没关系,AI 无从知晓:
支付接口本身是 wx.requestVirtualPayment 一个,按设备自动路由:Android 走微信支付、iOS 走 Apple 支付,道具双端互通不用各配一套。这些条件少一条,iPhone 用户点了就是拉不起收银台。我把 IAP 开关实测确认到"已开通"、OfferID 和 AppID 都跟代码对上,才敢发版。这种活没有算法可写,只有一个个后台页面去核对。
先说清楚这两档分别给什么,因为价格段也好、签名段也好,最后都得落回"用户付了钱到底换到什么":
轻享 = 只去掉展示型广告;畅厨 包含轻享,再拆掉容量天花板。两档互斥且包含,一个人同一时刻只有一个生效档位(member.tier 单值)。而且两个"不限"只作用于你本人创建的厨房,在别人的厨房里添加权限仍由那边厨师长授予,与本服务无关。
会员不是凭空长出来的闸门,它全部挂在既有链路上:免广告收口在 ad-unit 一个渲染闸门(判 member.tier 短路),无限容量收口在 3 处已达上限校验点——不改数据、不动存量。有效性 = expire_at > now() 读时判定,不需要任何定时回收。
(免的那“插屏”一共四处:今天吃什么 / AI 荐菜 / AI 生图 / AI 识别——AI 功能人人能玩,但付了钱不用看这四下插屏。)
来源:稀土掘金 原文