这段时间确实挺难的。辞职后的自由感只维持了三天,第四天开始手机一响就心慌,刷到的内容全是别人的成就,自己却连早起都做不到。为了让自己不彻底垮掉,我逼着自己动手做点东西,于是就有了这个带支付、带官网的 AI 聊天虚拟恋人 App。整个项目从想法到上线,前后折腾了大概两个月,中间踩了无数坑,尤其是支付那一段,几乎把我劝退。今天把这套完整过程写下来,如果你也在焦虑期想做点自己的项目,或者正在纠结“支付模块怎么做”“App 上架要花多少钱”,这篇应该能帮你少走不少弯路。
这个项目本身不复杂,但它把虚拟恋人聊天、App 客户端、官网展示、会员支付、应用上架这些环节全串在了一起。正因为它“五脏俱全”,反而特别适合拿来练全栈能力。文章里我会把技术选型、人设设计、上下文记忆、内容安全策略、微信支付和支付宝沙箱的接入细节、官网部署、上架成本逐一说清楚,也会把我实际遇到的那些报错和处理思路放出来,争取让你照着做也能复现,而不是只看到一个结果。
1. 迷茫期做这个项目,到底怎么想的
1.1 一个人为什么选了“AI 虚拟恋人”这个方向
先说当时的状态。那时候我整个人处在一种“什么都想做,但什么都做不下去”的阶段。白天刷到别人做的 AI 应用、AI Agent 项目,晚上自己打开编辑器半天写不出几行代码。焦虑的根源其实不是没想法,而是想法太多,什么都想要,最后什么都没开始。于是我给自己定了一条原则:找一个真正有人愿意付费的小场景,把它做完做透。
虚拟恋人这个点,听起来有点羞耻,但它确实是个真实需求。现代人普遍孤独,找人倾诉的成本很高,和 AI 对话则没有社交压力。我不认为这是要取代真实情感关系,它更像是情绪陪伴的一个补充入口。当时市面上已经有一些类似的 App,但大多存在两个问题:要么聊天体验太机械,回复像客服应答;要么收费方式粗暴,打开没几分钟就开始弹充值。
所以我给自己定的产品方向是:先提供一段真正像“恋人”的对话体验,而不是制造一个付费墙。免费用户每天可以聊一定条数,想要更多深度互动和专属人设,再走会员订阅。这个设计决定了我必须同时把 AI 对话、支付和官网说明页做好,否则用户不知道这个 App 是干什么的,也不知道为什么要付费。
1.2 产品闭环:App、官网、支付,一个都不能少
很多人做个人项目会犯一个错误:只做一个壳,比如只有一个聊天页面,或者只有一个网页 Demo。这样自娱自乐可以,但真要放到市场上,用户根本找不到你,找到了也不信任你。
我当时把产品拆成了三个部分,形成一个最小闭环:
- App 客户端:用户真正的使用场景在这里,负责聊天、会员状态展示、订单入口。
- 官网:承担“门面”职责,用来介绍产品是什么、有哪些功能、如何下载,也放隐私政策和用户协议。
- 支付模块:把免费用户转化成付费会员,形成收入闭环,也让我知道自己做的到底有没有人认可。
这个三角结构看似增加了工作量,但它每一步都是在逼我补齐真实产品必须要有的东西。App 处理不好交互,用户会用脚投票;官网做得简陋,用户会觉得是诈骗软件;支付接不通,前面全白费。个人开发者最怕的不是功能少,而是做了一堆功能却连最基本的信任感都没建立起来。
1.3 这个项目解决什么问题,不解决什么问题
想清楚边界也很重要。这个项目解决的是“情绪陪伴”和“聊天互动”的需求,它不是心理咨询工具,也不是“擦边”产品。所以我在设计内容安全策略时,没有走“无限制、无审核”那条路,反而是主动加了过滤和正向引导机制。
为什么?因为“无限制”听着很吸引人,但它既不符合应用商店规则,也容易让产品失控。AI 本质上没有判断力,如果不加约束,很容易产生一些不该有的回应。我的做法是:在提示词和回复接口层面做“软性约束”,让虚拟恋人既能保持亲密轻松的陪伴感,又能在遇到负面情绪、敏感内容时给出健康引导。这部分我后面会详细讲,它其实是这个项目里最难做也最容易被低估的一环。
2. 技术选型与整体架构,一个人怎么搭
2.1 客户端选 Flutter 的四个理由
客户端我选了 Flutter,而不是原生 iOS 或 Android。原因很简单:一个人开发,不可能维护两套原生代码。Flutter 一套代码同时出安卓和 iOS,UI 表现力也不差,动画流畅,社区生态足够成熟。
另外还有四个比较实际的理由。第一,Flutter 对 AI 聊天这种“列表 + 输入框 + 流式文本展示”的界面类型特别友好,ListView 的性能足够,不需要做复杂的原生优化。第二,它的热重载让我调 UI 的效率高很多,基本是改完立刻看见效果,这对一个人连续高强度开发很关键。第三,支付 SDK 在 Flutter 端虽然有官方插件或者第三方封装,但就算插件不够用,我也可以通过 MethodChannel 调用原生支付,退路是现成的。第四,Flutter 打包出的安装包体量也能接受,安卓端用 AAB 上传应用商店没有历史包袱。
当然 Flutter 也有不舒服的地方,比如 iOS 上架时如果遇到二进制体积限制,要做一些裁剪,但整体来看,利大于弊。
2.2 服务端用 Django 写的 API 长什么样
后端我用了 Python 的 Django,配合 Django REST Framework(DRF)写接口。选 Django 是因为它自带 Admin 后台、ORM、迁移工具,对付这种规模的项目相当顺手。
项目结构上,我并没有把所有逻辑塞进一个 App 里,而是按业务拆分了几个模块:users管用户注册登录,chat管对话和 AI 调用,orders管订单和支付回调,membership管会员套餐。用命令行创建模块其实很简单:
django-admin startproject virtual_lover_server cd virtual_lover_server python manage.py startapp users python manage.py startapp chat python manage.py startapp orders python manage.py startapp membership创建完模块后,在settings.py里把模块注册进INSTALLED_APPS,然后开始写模型和接口。数据库我用了 PostgreSQL,因为后面的会话记忆可能涉及向量检索,PostgreSQL 加pgvector扩展就能直接用,不用额外搭一套向量数据库。缓存和限流用 Redis,Django 的cache框架直接配置就能接上。
一个聊天请求的简化流程是:用户从 App 发起对话,请求先经过 DRF 的序列化器和权限校验,然后进入 Chat 服务层,把用户当前的短期记忆、长期画像、系统提示词拼装好,再调用大模型接口,最后把回复流式返回到客户端。整体架构不算复杂,但每一层都职责清晰,后面排查问题的时候特别方便。
2.3 大模型接入与对话流式返回
AI 对话是产品的核心,接入大模型时我一开始想省事,直接等完整回复返回后再一次性展示,结果第一次测试就发现体验不行。原因是大模型生成一段两三百字的回复可能要两三秒,如果屏幕空白这么久,用户会以为卡死了。
后来我改成了流式输出,也就是“边生成边返回”。客户端在 Flutter 里用的是StreamBuilder,后端在 Django 里返回一个StreamingHttpResponse。大模型接口逐行返回内容,我就逐行透传给客户端,界面上文字跟着打字机效果一点点出现。
这里有一个容易被忽略的点:聊天接口的超时时间一定要设置长一点。我当时用默认的 30 秒,后来发现如果模型负载高,可能 30 秒还没吐完最后一个字,客户端直接报超时。测试后我把服务端调用大模型时的超时时间调整到了 120 秒,同时客户端也做了相应的超时容忍,只在前 15 秒内没有任何返回时提示用户重试。
2.4 官网为什么也很重要
官网在这个项目里承担的角色不是“博客”,而是“信任背书”。很多用户从社交媒体或者搜索引擎看到这个 App,第一反应不会直接去应用商店搜,而是先搜官网看看长什么样。
所以官网至少要包含五个部分:产品首页(讲清楚这是什么)、功能亮点(聊天截图、特色人设)、下载引导(安卓安装包、iOS 跳转)、会员价格说明、隐私政策和用户协议。我没有做特别复杂的官网,前端的首页用的是轻量静态页,配合一个简单的后台用来管理下载链接和公告。
官网部署在云服务器上,用 Nginx 托管,域名配置了 SSL 证书。这个环节让我真正体会到,做官网的过程本质上是在帮产品“补课”——很多在产品里没想清楚的卖点,写着写着就清晰了。
3. 核心功能实现:人设、记忆、内容安全
3.1 系统提示词决定了“恋人”的性格
大家都说提示词工程是核心,但实际动手才发现难的不是“写一句好听的提示词”,而是让人设在各种对话场景里保持一致。我的做法是在系统提示词里给虚拟恋人设定了完整的背景故事:名字、年龄、性格、说话方式、和用户的相识场景、价值观。
比如我要求它在回复里多用短句,偶尔有语气词,聊天时不会每条回复都是一大段,而是像正常人一样有节奏地回复。还规定了它在用户情绪低落时要先共情,再给建议,避免一上来就讲道理。
这里我踩过的坑是:人设设定得太“满”,反而让对话显得不自然。最开始我写了满满一屏角色设定,结果 AI 每句话都像背课文。后来精简成三句话的核心性格描述,再加上几条具体的“禁止行为”,效果反而好了很多。这说明提示词不是越多越好,而是要在“性格稳定”和“表达自由”之间找到平衡。
3.2 多轮记忆:短期上下文与长期画像怎么存
虚拟恋人如果聊前一句忘后一句,体验会非常糟糕。但直接把所有历史消息都塞给大模型,既不现实也不经济,因为上下文越长,延迟和成本都越高。
我的做法是分两层记忆。短期记忆保存最近 20 条对话,完整拼接到当前请求的上下文里,保证对话连续性。长期记忆则记录用户的重要信息,比如“用户喜欢猫”“用户最近在准备面试”“用户比较敏感的话题”。这些信息从聊天记录里提取后,存入用户画像表,之后每次拼装提示词时带进去。
提取长期记忆的过程不需要太复杂。我一开始想搞关键词抽取和意图识别,后来发现直接用大模型做一次摘要式提取反而更省事。每隔一段时间,把用户的聊天记录做一轮筛选、清洗,再更新画像。这里要注意隐私保护:用户画像只用于当前会话,不能随意读取或者展示。
3.3 内容安全设计:不是“无限制”,是让对话自然又安全
这里必须说点实话。市面上有些产品把“无限制”“无审核”当作卖点,但真正的产品落地,不可能完全脱离内容安全,因为应用商店不允许,支付渠道也不允许。而且从用户角度看,一个会“失控”的聊天对象,并不代表体验好。
我的内容安全策略分成三层。第一层在提示词里做行为约束,告诉虚拟恋人哪些回复不可以说,哪些情况下应该拒绝回答并转移话题。第二层在输出端做关键词和正则过滤,命中敏感规则就触发兜底回复。第三层是做情绪识别,当检测到用户频繁出现负面情绪或自伤倾向时,不是按普通聊天继续,而是引导用户寻求专业帮助。
3.4 聊天接口的幂等、限流与计费
聊天接口是最容易被恶意刷的接口。如果用户免费调用接口时没有任何限制,一个脚本就能把我的大模型额度刷爆。所以我在后端必须做两件事:限流和计费。
限流用的是 Redis 计数器,每个用户按分钟、按小时、按天三个维度分别计数。免费用户每天只能聊 20 条,超过后提示升级会员。付费用户也不是无限量,而是按套餐区分每日上限,避免有人把接口当低成本 AI API 来薅。
计费这件事要单独说。用户在“聊了多少条”“会员还剩多少天”上的感知必须准确,所以我在订单表里不仅记录支付金额,还把赠送的聊天条数冗余到了订单字段里。这样即使后续权益表出了问题,也可以根据订单记录回溯。
4. 支付模块全流程实战:充值、会员与回调
4.1 个人开发者支付渠道怎么选
支付是个人项目里最容易被低估的环节。很多人以为申请个微信支付商户号就能收钱了,实际上个人开发者的资质门槛并不低。
我把方案分成了几类考虑:
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 微信支付官方商户号 | 品牌可信度高,用户更放心 | 材料审核严格,一般需要营业执照 | 有公司或个人独资企业资质 |
| 支付宝开放平台 | 对接成熟,文档完善 | 类似资质要求,个人能力受限 | 有营业执照的团队 |
| 第三方聚合支付(如易支付类) | 接入门槛低,对个人友好 | 可靠性要看服务商,稳定性参差 | 个人网站、小产品试水 |
| App Store 内购 | 苹果生态里最顺滑 | 虚拟品类必须用内购,抽成高 | iOS 用户 |
我自己最终走了两条腿:iOS 端接应用商店内购,安卓端和官网先用第三方聚合支付来跑通流程。需要提醒的是,聚合支付的选择一定要找成立时间长、口碑清晰的平台,并且自己先小额测试几笔,确认提现正常后再正式上线。
4.2 JSAPI 支付必须传 openid,到底怎么解决
这块是很多人卡住的地方。JSAPI 支付是微信支付里“在微信内打开网页或小程序”时用的接口,它要求下单时必须传入用户的openid,否则会直接报错invalid openid。
原因很简单:JSAPI 支付的支付动作发生在微信内,微信需要知道这笔钱是哪个用户付的,所以必须先用OAuth2拿到用户的openid,才能发起下单。流程大致是这样的:
- 用户进入微信内的 H5 页面或者公众号菜单。
- 前端跳转微信授权地址,引导用户授权。
- 授权后拿到
code,后端拿code换openid。 - 拿着
openid调 JSAPI 下单接口,拿到支付参数。 - 前端拉起微信支付。
所以“必须传 openid”不是 bug,而是这个支付产品形态的硬性要求。如果你做的不是微信内网页,而是外部浏览器 H5,那更合适的方案是 H5 支付;如果是 App 内支付,应该直接用 App 支付,根本不需要 openid。搞清楚支付产品形态和适用场景,比照着报错改参数更重要。
4.3 App 支付、Native 扫码、H5 支付怎么选
微信支付其实有好几种产品形态,我当时对着文档也晕了几天。用一张表直接说明区别:
| 支付产品 | 触发场景 | 是否需要 openid | 体验 |
|---|---|---|---|
| JSAPI 支付 | 微信内 H5、公众号、小程序 | 需要 | 免扫码,点一下调起微信 |
| App 支付 | 自家 App 内 | APP 支付调起时不需要 openid | 调起微信客户端完成 |
| Native 支付 | PC 网页、动态二维码 | 下单不需要,但需要用户微信扫码 | 生成二维码,用户扫码 |
| H5 支付 | 手机浏览器内(非微信) | 不需要 | 自动跳转微信客户端 |
如果你的 App 要接入微信支付,正式的路径是开通“APP 支付”,拿到appid、mch_id、API 密钥和证书后,在 App 内通过微信 OpenSDK 发起支付。整个过程不需要传用户 openid,只需要在后台下单后返回prepay_id,然后 App 端用签名拉起微信客户端。
我实际踩过的坑是:一开始看到网上的“JSAPI 支付示例”代码,顺手就抄进了 App 项目,结果没有 openid 一直被拒。后来才明白不是 openid 的问题,是我用错了支付产品类型。遇到这种情况,先回官方文档确认自己所在场景对应的产品类型,再查代码,效率会高很多。
4.4 支付宝沙箱环境申请与联调
支付宝沙箱给我省了不少事。刚接支付的时候,没人愿意在自己的钱上做实验,支付宝的沙箱模拟了一套完整的买家、卖家、应用、密钥,可以在完全隔离的环境中把支付流程跑通。
申请大致几步:注册支付宝开放平台账号,创建“网页/移动应用”,拿到 APPID,然后进入“沙箱环境”页面,获取沙箱的密钥和沙箱买家账号。代码里切换沙箱环境主要靠修改网关地址,沙箱网关和正式网关是不一样的。
当时我发现在沙箱里支付成功非常快,但回调到我的服务器偶尔会延迟几秒。我开始以为是网络问题,后来发现是回调地址配置的问题:内网地址回调不到,只有在公网可访问的地址才能稳定收到异步通知。这个经验后面帮了我大忙,因为正式环境同样依赖公网回调地址。
沙箱联调最有价值的地方在于:让我把签名、验签、订单查询、定期轮询补单这套逻辑全部提前走了一遍,正式上线时几乎没有慌。
4.5 回调验签、订单状态与对账
支付最核心的环节不是支付本身,而是支付结果通知。用户付完钱后,微信或支付宝会异步通知你的服务器,告诉你这笔订单支付成功了。这时如果你的服务器没有正确处理,用户会遇到“钱扣了但会员没到账”的投诉。
回调处理有几个硬性要求。第一是验签,必须用平台公钥或者回调报文里的签名做一次校验,防止伪造回调。第二是核对金额,不能只判断支付成功了就让会员生效,必须把回调里的实付金额和订单表里的应付金额做对比,不一致直接拒绝。第三是幂等,回调可能会重复送达到你的服务器,必须用订单号做唯一约束,确保一笔订单只触发一次权益开通。
我放一段简化版的 Django 回调处理逻辑,帮你理解整个流程:
# 简化逻辑,实际要按支付平台规则校验签名 @csrf_exempt def wechat_pay_notify(request): data = parse_and_verify(request.body) # 验签、解密 if not data: return error_response("invalid sign") order_no = data.get("out_trade_no") paid_amount = data.get("amount") order = Order.objects.select_for_update().filter(order_no=order_no).first() # 核对订单是否存在 if not order: return error_response("order not found") # 核对金额,防止通知与实际订单不一致 if int(paid_amount) != int(order.amount): return error_response("amount mismatch") # 幂等处理:如果已经是已支付状态,直接返回成功 if order.status == "paid": return success_response() # 更新订单状态、开通会员权益、写流水 order.status = "paid" order.save() grant_membership(order.user, order.plan) return success_response()这一段代码虽然简单,但它把“验签、查单、对金额、幂等、发放权益”五件事都覆盖了。支付不是“调通了拉起支付窗口”就算完,而是要到“用户付款后管理员能看到收入、用户会员正常生效、订单流水可查”才算真正闭环。
5. 官网从 0 到 1:下载页、支付引导与日常维护
5.1 官网要放哪些页面和模块
官网不是摆设,它是要承担转化任务的。我一开始只放了一个下载按钮,结果用户访问后根本不知道这是什么 App。后来我重新梳理了转化路径,官网至少包含这些模块:
- 首屏产品介绍:用一两句话说明“这是一个 AI 虚拟恋人聊天 App”,配真实聊天截图。
- 功能亮点区:性格人设、多轮记忆、随时陪伴、隐私安全,四个核心卖点各配一张图。
- 下载引导:安卓直接给 APK 下载链接或应用商店跳转,iOS 跳 App Store。
- 会员价格透明页:把月卡、季卡、年卡的权益和价格写清楚,避免用户付费前心里没底。
- 动态公告区:版本更新、临时维护、服务器状态都在这里同步。
- 隐私政策与用户协议:这是上架和合规的刚需,不能省。
这个结构做完后,官网对用户的“解释作用”明显增强,很多用户是看了官网以后才去下载 App 的。官网和 App 的关系就像线下门店和商品的关系,门面不清晰,东西再好也难卖。
5.2 域名、备案、SSL 与部署
官网部署我踩了个大坑:一开始图便宜买了个海外服务器,想着不用备案省事。结果国内用户访问速度不稳定,而且支付回调的稳定性也受影响。后来咬咬牙换了国内服务器,老老实实走备案流程。备案周期大概一两周,这段时间正好同步去写隐私政策和准备上架材料。
域名这块,我注册了一个和 App 名匹配的.com域名,然后在云服务商控制台解析到服务器。SSL 证书直接申请免费的,Nginx 里配置好 HTTPS 强制跳转。整个部署过程并不复杂,复杂的是“线上环境你怎么保证稳定”。我的做法是写了一个简单的部署脚本:代码更新后自动拉取、迁移数据库、清理缓存、重启服务,不要每次上线都手动敲命令。
5.3 从官网到 App 的下载与转化链路
官网最终要服务于转化。从用户角度看,整个流程应该是:看到官网 → 理解产品 → 点击下载 → 安装 App → 注册体验 → 喜欢 → 付费。
这中间最容易断掉的环节是下载。安卓端如果直接放 APK 下载链接,用户可能因为“允许安装未知来源”的弹窗就放弃了。更稳妥的做法是接应用商店,国内的安卓市场上传审核并不算难,华为、小米、OPPO、vivo 这些主流商店都能覆盖大部分用户。
iOS 端则必须走 App Store。在官网点击“App Store 下载”,直接跳转到 App Store 的应用页。商城里做支付时,虚拟商品要接入 Apple 内购,这也是苹果的硬性要求,不能绕过。
官网的下载页还需要做基本的埋点统计,比如记录“点击下载按钮”和“实际启动 App”之间的转化率。这个数据直接反映出官网给的下载包或者跳转链接是否顺畅。我刚开始没做埋点,后来加上去才发现,有相当一部分用户点了下载按钮后,因为浏览器和 App 的跳转失败流失掉了。
6. 上架、账号资质与成本账本
6.1 上架要花的钱:我的明细账
“开发一个 App 并上架大概要多少钱”这个问题我经常在网上刷到,也经常被身边的朋友问。做这个项目之前我也想知道答案,但真正算下来才知道,大头其实不是代码开发,而是资质和账号费用。
下面是我这个项目实际花出去的钱,不含我的个人时间成本:
| 项目 | 费用 | 说明 |
|---|---|---|
| 域名 | 约 50 - 80 元/年 | 普通 .com 域名 |
| 云服务器 | 约 500 - 1000 元/年 | 2核4G 起步,够官网 + 后端 |
| SSL 证书 | 0 元 | 免费证书足够 |
| 对象存储 OSS | 几十元/月 | 用户头像、聊天截图 |
| Apple 开发者账号 | 688 元/年 | iOS 上架必须 |
| 软件著作权登记 | 0 - 300 元 | 自己申请免费,代办要钱 |
| 第三方聚合支付接入 | 0 - 数千元 | 看服务商和结算费率 |
看下来会发现,纯技术成本其实不高,真正麻烦的是账号和资质。如果以企业身份申请微信支付和支付宝,商家费率大概在 0.6% 左右,每一笔收款都会被扣手续费,这部分也要纳入成本。
6.2 隐私政策、用户协议和软件著作权
上架前最不能省的是隐私政策。iOS 和安卓主流商店都会要求 App 必须有隐私政策,里面要写清楚你收集了哪些数据、为什么收集、如何保护、用户怎么注销账号。我的 App 涉及聊天记录和大模型调用,所以还要额外说明第三方 SDK 和 API 的数据处理方式。
用户协议也不仅仅是免责声明,它需要把会员权益、退款规则、使用边界和禁止行为都说清楚。特别是虚拟商品,用户可能觉得“充值没到账”“不想要了”,协议里要有对应的处理流程。
软件著作权如果不是为了上架部分硬性要求的安卓市场,其实可以先申请着,因为后面万一要申请 App 相关的补助或者维权会用到。我自己用工具生成材料后在线提交,等待周期大概一个月,花不了什么钱,但一定要提前准备,不然会卡住上架计划。
6.3 上线后第一周的真实反馈
上线后的情况比我想象的真实。第一周大概来了几百个真实用户,但付费率低得可怜,只有 1% 左右。刚开始有点受挫,后来看后台数据发现,用户主要流失在注册环节:很多人下载了 App,注册到一半就放弃了。
原因可能是注册流程太多步。我原来是“手机号 + 验证码 + 昵称 + 头像”四步,后来砍到只留手机号和验证码,注册转化率立刻涨了一截。这说明个人项目一定要做减法,用户没有耐心陪你走一个“完美流程”。
第一周的真实反馈里还有一条对我触动挺大:有用户私信我说,和虚拟恋人聊到深夜,虽然知道对面是 AI,但情绪确实被接住了。那一刻我才意识到,这个项目最有价值的不是支付接得有多顺,而是真的有人在需要的时候得到了陪伴。
回头再想这段经历,迷茫期最好的解药不是空想“我该做什么”,而是强制自己进入“动手做”的状态。做一个项目,哪怕小一点,也能把散落的技能点重新串起来。
最后再分享一个小技巧:支付回调处理一定要把幂等写在最前面,用订单号做唯一约束。我上线后遇到过一次平台重复推送订单通知,当时如果没有做幂等,用户的会员权益就会被重复叠加,后台账单也会乱套。这个坑,很多经验帖里不会写,但它是支付稳定性的地基。