
大家好,我是小悟。
做微信支付服务商,帮商户完成入驻,也就是特约商户进件,是绕不开的一环。营业执照、法人身份证、结算银行卡、联系人信息、经营类目,五大类加起来四十多个字段,其中身份证号、银行卡号、手机号属于敏感个人信息,按规定必须用微信支付平台公钥做 RSA 加密才能上送,明文严禁出现在任何传输链路里。


听起来是个体力活,对吧?可真正折磨人的从来不是"能不能做",而是"做不做得稳"。商户随手一拍的营业执照往往单张就有四五兆,材料散在群里、散在本地盘;字段填错要翻半天聊天记录找原始依据;运营换班后新人接不住进度;同一个商户被重复问一遍身份证号。我一度以为,这种乱只能靠人盯、靠耐心去扛。直到我换了个思路:与其盯流程,不如给流程一个不会失忆的底座,把和微信、和 OCR 的对接交给代码——而写代码这件事,我全程只和 WorkBuddy 对话。

在写第一行代码之前,我先把痛点理清楚。问题从来不在"能不能调接口",而在"能不能把一堆散乱的材料和状态稳稳地管起来"。
真实痛点 | 系统给出的解法 |
|---|---|
材料散落各处、易丢、易重复采集 | 每个商户对应一个 Agent Bucket Space,材料一次上传、永久可追溯,换人不丢上下文 |
身份证、银行卡明文流转,合规风险高 | 敏感字段经 RSA-OAEP 加密后才落库,系统自始至终只持密文,运营和日志里看不到任何明文 |
进度靠人盯,换班接不住 | 进件被建成一条状态机(草稿→已提交→驳回→通过),每一步都持久化,断点可恢复 |
微信驳回后,要在聊天里翻原始材料重传 | 驳回自愈:把微信返回的审核项直接映射到材料类型,精准告诉运营"该补传哪一张" |
签约与审计无据可查 | 审核通过后生成只读确认函外链,每一动作都写入事件时间线,事后可回溯 |
这些解法不是凭空设计的,而是每一次被微信驳回、每一次在群里找不到材料时,我把它讲给 WorkBuddy 听,再由它帮我想清楚"该把这块能力放哪一层"逐步长出来的。
系统的骨架很朴素:一个原生 JS + Jinja2 的前端向导,一个 FastAPI 编排层,下面挂三个"会干活"的能力——Agent Bucket 存储底座、腾讯云 OCR 预审、微信支付 v3 进件适配器。它们之间不直接互传文件,而是把产物写进 Bucket,再从 Bucket 读取下游需要的状态。

逐个说一下它们各自做什么:
templates/index.html):按主体类型动态显隐字段,实时做必填与日期合法性校验,支持拖拽上传、图片压缩回显。表单是创建时手填、编辑时回填,提交前后端双重校验。main.py / workflow.py):系统的"大脑"。它把 Space 创建、材料上传、OCR、微信审核、驳回自愈、签约串成一条状态机;每一次状态流转都写进事件时间线。关键设计是适配器可替换——AgentBucketAdapter 和 WeChatApplymentAdapter 都同时提供 mock 和 real 两套实现,页面填了密钥就走 real、留空走 mock,改完配置重启即可切换,开发期完全不用真连微信就能把全流程跑通。storage/agent_bucket.py):以 Space 为一级容器、Task 为二级单元。它把 COS 的 V5 签名、分区、生命周期屏蔽掉,上层只看到 put / get / delete / share_link 几个动作。多租户靠 Space 前缀硬隔离,一个商户的材料绝不会串到另一个商户。ocr/precheck.py):上传营业执照或身份证,OCR 抽出统一社会信用代码、商户全称、身份证号等结构化文本,直接回填表单,运营只需核对。真实调用失败会自动降级为模拟预审,不会让上传卡死。crypto_sign.py / rsa_oaep.py):零依赖实现 RSA 签名与 RSA-OAEP 加密。前者给微信 v3 请求签名(手写 WECHATPAY2-SHA256-RSA2048),后者把敏感字段加密成密文再落 Bucket。两者都带自检,不依赖 cryptography 也能跑。wechat/applyment.py):手写微信签名、图片转 media_id 上传、按官方文档完整拼装申请单、查询审核状态并把驳回项映射回材料类型。image_compress.py):上传前把图片压到 ≤2MB(微信图片上传限制),最长边缩到两千像素以内,逐级降质,保留肉眼可见信息。workflow.py + main.py):审核通过后生成 confirmation_token,据此产出 COS 预签名外链,渲染成一张只读确认函页面,给商户或协作方查看。
把一次完整的进件拆开看,引导逻辑是一条清晰的路径。商户建 Space 后,材料上传即触发 OCR 预审回填,图片压缩后做 RSA 加密落库,再交微信 v3 提交;任何一步卡住都会回到对应节点给出精准提示,而不是笼统报错;微信驳回时,自愈模块定位需重传的材料,运营在原任务上补传即可,不必从头再来。

最让我满意的一处设计是:密文在提交之前就已经落进 Bucket,提交这一动作因此变成幂等的"读密文再上送"。哪怕提交环节网络抖动失败,重试时直接读取密文即可,不用让商户再提供一遍敏感信息——商户完全感知不到中间失败。

这是我想重点说的部分。系统不是一次写对的,而是和 WorkBuddy 反复对话、把每一次报错当成线索逐步拆出来的。整个过程是一个循环:我有一个想法或遇到一个报错,描述给它,它生成或修改代码,我运行,报错就再丢回对话,对齐官方文档定位根因,修复,再运行。

开局并没有先把整张架构图画完。第一句话大概是:"帮我搭一个微信支付特约商户进件的 Web 工具,先把 mock 全流程跑通。"WorkBuddy 给了我一个 FastAPI + Jinja2 的 MVP 骨架,我很快就能在 127.0.0.1:7860 上走完"建 Space→传材料→提交→驳回→自愈→签约"的完整链路。等骨架立住,再一轮轮往里填真实能力:先是把 OCR、Agent Bucket、微信三组配置改成页面录入、填了走 real 留空走 mock;然后是手写 COS V5 签名接入真实存储;接着是手写微信 v3 签名、图片转 media_id、零依赖 RSA——每一步都是"我先说要什么,它给初版,我跑,报错再聊"。


几个印象最深的排障:
Wechatpay-Serial 头被悄悄改了大小写。 加上请求头之后仍报错"缺少微信支付平台证书序列号"。我把报错丢回对话,WorkBuddy 让我去看底层实现才发现:底层用 email.Message 存请求头,会把 Wechatpay-Serial 规范成全小写,而微信严格区分大小写。修法是改用 unredirected_hdrs(纯字典)注入,保住原始大小写;序列号则从平台证书 DER 结构里递归抽取第一个 INTEGER。
RSA-OAEP 的哈希算法用错了。 这是最隐蔽的一个。微信要求用 SHA-1 做掩码生成哈希,而一开始按惯例用了 SHA-256,结果微信回"平台私钥解密失败"。为确认不是我多心,临时装了 pycryptodome,用它的 PKCS1_OAEP(hashAlgo=SHA1) 当作微信官方算法的权威实现,对七个样本(姓名、身份证、手机、邮箱、执照号、住址、银行卡)做交叉验证——本系统的加密结果和官方实现逐字节 MATCH,才敢确信 SHA-1 才是正解。验证完立刻卸载,没让测试依赖污染项目。
长期执照的"开始时间"也必须填。 以为选了"长期"就可以省略有效期起期,结果微信回"证书有效期开始时间格式错误"。和 WorkBuddy 对齐官方文档后明白:period_begin 对所有执照必填,只有 period_end 才能填"长期"。然后在后端加了护栏——缺失 period_begin 直接抛清晰的中文错误,不再把空值丢给微信让它回含糊提示。
这三处,每一个背后都对应一条分散在文档角落的硬约束。WorkBuddy 的价值,是让我能把"这次又报了什么"直接丢进对话,快速定位到对应字段和文档条款,而不是从头翻规范。
(body, status) 与 (status, body) 解构反了,导致取到的 body 其实是 int 的状态码,触发一串晦涩的类型错误。自此我更倾向用命名元组或 dataclass 来明确多返回值。用下来,我对 WorkBuddy 这类 AI 编程环境的判断更明确了:它的价值不在于替你写出完美代码,而在于把"反复试错"压缩成"即时对话复盘"。进件接口里那些散落、隐晦、报错含糊的约束,恰好是需要人和 AI 一起啃的硬骨头——它帮你快速比对文档、构造请求体、做交叉验证,最终的判断权和责任仍在写代码的人手里。
接口约束的权威性永远来自官方文档,系统只是把文档翻译成了人话。这一点,在每一处修复时都坚持用真实提交或权威实现去验证,而不是凭推断下结论。毕竟支付进件没有"差不多",只有"过"和"不过"。
如果你也正在和某个报错含糊、文档分散的接口搏斗,不妨试着不急着翻源码,而是先把报错原样讲给 WorkBuddy 听,往往聊着聊着,路就清楚了。
谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。
您的一键三连,是我更新的最大动力,谢谢
山水有相逢,来日皆可期,谢谢阅读,我们再会
我手中的金箍棒,上能通天,下能探海
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。