博客
方法

按发票归因,而不是按点击

按点击的追踪知道成交发生了。按发票的追踪知道多少钱、何时续费、何时退款。对订阅制,只有第二种是对的。

Jun 29, 2026 · 13 分钟读完 · RelayWonder
clickcookiecheckoutStripeinvoice.paidledgerevery renewal and every refund becomes its own row

发票级归因的意思是:伙伴的佣金按 Stripe 里真正付了钱的那张发票计算,而不是按感谢页上打出去的一个像素。对一次性购买,两种算法得到同一个数;对订阅制,两种算法每个月都不一样。像素只打一次,然后就安静了;发票却每个月到来,金额会变,会失败,会重试,有时还会退款。只要你的计划付的是订阅佣金,把数算对的唯一办法就是去读发票。

这篇文章讲 RelayWonder 怎么在发票这一层做归因,为什么存结果的账本是只追加的,以及几种别扭情况怎么处理:升级、降级、扣款失败、退款、拒付,还有一个客户点了两位伙伴的链接。我们把算术全部写出来,方便你拿自己的计划对照。

感谢页上的像素到底知道什么

经典的联盟脚本做三件事:从落地页 URL 读一个点击 id,把它写进 Cookie,在确认页上发一个转化事件,金额是页面模板碰巧暴露出来的那个数。通常是首次扣款,有时是扣税前、用券前的套餐价,如果结账的是免费试用,那就是零。

因为页面只加载一次,像素不可能知道这些事:

  • 试用在十四天后有没有转成付费订阅。
  • 客户在第二、第三、第十二个月有没有续费。
  • 第四个月有没有从 $29 升到 $99。
  • 第六个月卡有没有扣失败,催款邮件之后订阅有没有被取消。
  • 第二个月有没有退款,第三个月有没有来一笔拒付。

建立在这种 Stripe 联盟追踪之上的计划,最后只会落到两种坏结果之一。要么只在首次扣款上付一次性佣金,悄悄亏待了那些带来长期客户的伙伴;要么用首次扣款乘以一个假设的生命周期来估订阅佣金,对流失的客户多付,对升级的客户少付。两种都是在近似一个 Stripe 本来就精确持有的数字。

发票级归因从头到尾怎么跑

整个流程四步,只有第一步跑在浏览器里。

  1. 追踪脚本从落地页 URL 读出伙伴标识,写进品牌自己域名下的第一方 Cookie。默认窗口 60 天。如果结账时用了伙伴的专属优惠码,码本身就带归因,不需要 Cookie。
  2. 访客开始结账时,品牌把这个标识传给 Stripe。用 Stripe Checkout 的话是 session 上的 client_reference_id 字段;自建结账流程的话是客户或订阅上的一个 metadata 键 [1][2]。不管哪种,标识现在住在 Stripe 对象上,不在浏览器里。
  3. RelayWonder 监听品牌的 Stripe webhook。每一个带伙伴标识的客户的 invoice.paid 事件变成一条佣金行:发票 id、实付金额、币种、日期、伙伴、佣金率、佣金额 [3]。
  4. 每一个关联到这些发票的 charge.refunded 和 charge.dispute.created 事件变成同一位伙伴名下的一条负数行,带同一个发票 id 以便追溯 [4][5]。

这条链上没有任何一环依赖页面加载。客户从邮件链接续费、从 Stripe 托管的客户门户续费、或者睡觉时自动续费,发票照样付了,webhook 照样触发,行照样落下来。发票级归因的全部道理用一句话说就是:事实的来源是让钱动起来的那个事件,不是宣布它的那个页面。

clickcookiecheckoutStripeinvoice.paidledgerevery renewal and every refund becomes its own row
每张已付发票变成一条佣金行;每笔退款或拒付变成同一发票 id 下的一条负数行。

一个十二个月的完整算例

设一位伙伴拿 20% 的订阅佣金,没有时间上限。他推荐的客户 1 月 3 日订了 $49/月的套餐,5 月 5 日升到 $129/月,6 月因为账单投诉退了一个月的钱,10 月底取消。像素只看到一笔 $49 的转化。发票讲的是另一个故事。

一位被推荐客户的十二个月。合计:扣除退款后开票 $915.84,佣金 $183.17。感谢页像素只会记下 $49 和 $9.80。
月份发票事件发票金额佣金行
1 月invoice.paid(新订阅)$49.00+$9.80
2 月invoice.paid(续费)$49.00+$9.80
3 月invoice.paid(续费)$49.00+$9.80
4 月invoice.paid(续费)$49.00+$9.80
5 月invoice.paid(升级,按比例)$129.00 + $74.84 差额+$40.77
6 月invoice.paid,随后 charge.refunded$129.00,退款 $129.00+$25.80 然后 −$25.80
7 月invoice.paid(续费)$129.00+$25.80
8 月invoice.payment_failed,重试成功$129.00+$25.80(只在付成功的事件上)
9 月invoice.paid(续费)$129.00+$25.80
10 月invoice.paid(最后一期)$129.00+$25.80
11 月customer.subscription.deleted$0无行

几处值得注意。5 月的差额不是估的:Stripe 会为旧套餐未用完的部分和新套餐剩余的部分各开一行发票项,我们按实际付的金额计佣,这里是 $74.84,也就是 129 减 49 再乘以周期剩余的 29/31 [6]。6 月的退款不会把 6 月那条佣金行改成零,而是追加一条 −$25.80。8 月的扣款失败不产生任何行,因为 invoice.payment_failed 不是钱在动;重试成功时才产生那条行。11 月的取消也什么都不产生。

加总起来,这一位客户给伙伴带来了 $183.17,是像素会付的数字的 18.7 倍。如果一个品牌有四十个这样的客户,像素记账和发票记账之间的差距,就是"伙伴会谈论的计划"和"伙伴会忘掉的计划"之间的差距。

为什么账本只追加

退款来的时候有一个诱人的捷径:找到原来那条佣金行,把金额改成零。我们不这么做,也认为任何计划都不该这么做。只追加账本的意思是:行只插入,从不更新或删除。余额是行的加总,扣回是负数行。这样做的好处是实际的,不是哲学的。

  • 伙伴三月的对账单在四月、十二月和报税时是同一个数。如果你六月去改三月的行,四月发出去的对账单就错了,而且没人知道。
  • 打款批次能对账。4 月 1 日导出的批次覆盖的是日期在 3 月 1 日之前、过了 30 天保留期的行。其中某一行后来退款了,扣回会作为一条负数出现在 5 月的批次里,品牌的会计看到的正是他预期的样子。
  • 争议能审计。伙伴问余额为什么少了,答案是一条带时间戳、发票 id 和 Stripe 事件 id 的行,不是一句含糊的"调整"。
  • 风控有痕迹。某条推荐被标记、保留期被延长,那是行上的一个状态,不是把行删掉。

这个模型的代价是余额要算出来而不是存起来。对几万行的计划,这仍然是一个很轻的查询。我们当初认为这不是问题,现在也没有改变看法。

保留期、起付额,以及一条行什么时候可付

发票一付,佣金行就存在了,但要过了保留期才可付。默认保留期 30 天,选这个数是因为它比常见的退款窗口长,也比大多数发卡行早期拒付的活跃期长。品牌可以设得更长。过了保留期的行按伙伴、按币种加总,总额达到起付额(默认 $50)时,伙伴进入下一个月度批次。

负数行的处理方式很重要。一条 −$25.80 的退款行立即生效,不等 30 天。所以一位伙伴如果只有一条已过保留期的 +$25.80,最新一条又是 −$25.80,可付余额就是零。如果负数行在正数行已经付出去之后才来,伙伴的余额会变成负数,后面的正数行先把坑填平,然后才有新的可付金额。我们从不让伙伴把钱退回来,账本用未来的收入抵扣。

边缘情况和我们的处理

两位伙伴,一个客户

访客第一周点了伙伴 A 的链接,第三周点了伙伴 B 的,然后订阅。默认规则是窗口内最后一次点击,所以 Cookie 被覆盖,归 B。如果访客结账时用的是 A 的专属优惠码,码赢,因为结账时手动输入的码比几周前写下的 Cookie 信号更强。品牌在客户时间线上能看到两个事件,可以手动改判;改判本身也是一条行。

免费试用

试用会创建一个订阅,在多数 Stripe 配置下还会创建一张 $0 发票。我们把这张发票记成一条零金额的行,让推荐可见;试用结束后第一张真实发票产生第一条非零佣金。60 天 Cookie 窗口管的是从点击到创建订阅这一步,不管试用多长;30 天试用在第 31 天转化照样归因,因为订阅是第一天创建的。

年付和多币种

年付套餐一年一张发票,所以佣金行很大,保留期更重要。我们不把年付佣金拆成十二条月度行;一张发票,一条行。币种取自发票,伙伴每个币种一个余额,所以一位伙伴同时给一个欧元计费和一个美元计费的品牌推荐客户,会看到两个余额,打款批次里也是两条。

扣款失败和催款

invoice.payment_failed 不产生行。Stripe 的智能重试可能几天后成功并触发 invoice.paid,那时才创建行,日期是实际付款那天。订阅最终因欠费被取消的话,之后什么都不发生。伙伴从未因为没到账的钱被记过功。

赢回来的拒付

charge.dispute.created 产生一条负数行。品牌后来赢了拒付,带 won 状态的 charge.dispute.closed 产生一条正数行把金额补回来。还是两条行,不是编辑。伙伴看到下跌和恢复,两边都带日期。

一个下午配好

对已经在用 Stripe Billing 的品牌,接入工作量很小,大部分就是传一个字段。

  1. 把追踪片段加到营销站。它只写 Cookie,不做别的。
  2. 创建结账时在服务端读 Cookie,作为 client_reference_id(Checkout)或 metadata(自建流程)传过去。用 Stripe Payment Links 的话,标识可以作为 URL 参数附上 [7]。
  3. 在 Stripe 后台添加 RelayWonder 的 webhook 端点,订阅 invoice.paid、charge.refunded、charge.dispute.created、charge.dispute.closed 和 customer.subscription.deleted。Stripe 给每个事件签名,我们先验签再读 [3]。
  4. 设置佣金条款:比例、订阅佣金还是只首期、可选的时长上限、保留期、起付额。
  5. 在 Stripe 测试模式跑一个测试订阅,看着行出现。

我们不要能创建扣款或读银行信息的受限密钥。webhook 已经带了归因需要的一切。品牌如果更愿意我们用 API 读发票而不是收 webhook,一个只读、只有 invoice 和 charge 范围的受限密钥就够。

别的工具怎么做

发票归因不是我们独有的,任何订阅制联盟工具不这么做我们反而会怀疑。Rewardful 和 FirstPromoter 都从 Stripe 事件做归因,都支持订阅佣金;FirstPromoter 还接了 Paddle 和 Chargebee,Rewardful 支持 Paddle [8][9]。工具之间的差别在于:存下来的结果能不能改,退款是自动负数行还是手动调整,保留期和扣回怎么互动,伙伴跨品牌是不是一个账号。这些才是 demo 时该问的问题,订阅 webhook 本身只是入场券。按 2026-09-18 查看,两家定价页都描述为按归因收入分档、不抽佣金,和我们的模型一致,所以比较的是账本行为而不是价格。

我们还不支持 Shopify。到时候订单 webhook 会扮演这里发票的角色,退款同样是负数行;账本模型不变,只是来源不同。写在这里是为了没人把这篇文章读成对一个还没上线的东西的承诺。

检查你自己的计划

如果你已经在跑一个订阅制联盟计划,三个快速检查能告诉你你做的是发票级归因还是点击级归因。

  • 挑一位升过级的被推荐客户。升级当月伙伴的佣金变了吗?没变的话,你付的是首次扣款。
  • 挑上个季度的一笔退款。能在伙伴对账单里找到带这个发票 id 的负数行吗?如果对账单只是被重新算了一遍,你的历史是不稳定的。
  • 导出上个月的打款批次。每一行都能追到一串发票 id 吗?伙伴问起来,你能拿给他看吗?

把这些做对,靠的不是软件功能,而是一次性地决定:发票是事实的单位。这就是发票级归因在实践中的含义,账本里的其他一切都从这个决定推出来。

FAQ

联盟计划里的发票级归因是什么意思?

每一笔佣金都按计费系统里一张已付发票计算,而不是按网页上的一个转化事件。每次续费、升级和退款都产生自己的一条记录,所以订阅佣金反映的是客户随时间实际付的钱。

续费归因需要客户一直保留 Cookie 吗?

不需要。伙伴标识在第一次结账时就附在 Stripe 的客户或订阅上。之后这个订阅的每张发票都从 Stripe 对象归因,Cookie 不再相关。

客户退款后佣金怎么处理?

在伙伴名下追加一条负数行,金额等于退款部分对应的佣金,引用同一张发票。原来那条行从不被编辑。如果伙伴已经被付过款,负余额用未来收入抵扣。

升级时按比例开的发票怎么处理?

Stripe 会开一张含差额行的发票,佣金按这张发票实际付的金额计算。我们例子里月中从 $49 升到 $129 产生了 $74.84 的差额,佣金是它的 20%,约 $14.97,加在正常续费之上。

重试大概率会成功,为什么不在 invoice.payment_failed 时就付佣金?

因为钱没有动。重试成功会触发 invoice.paid,行在那天创建。永远不成功的话,行从未创建,也就没有什么要扣回。

支持 Paddle、Chargebee 或 Shopify 吗?

目前归因建立在 Stripe webhook 上。Shopify 在清单上但还不可用;上线后订单 webhook 会喂进同一个只追加账本。我们宁可直说,也不列一堆没做出来的集成。

Sources

  1. Stripe 文档:Checkout Session 的 client_reference_id · 把伙伴标识带到 session 和由此产生的客户上的字段。
  2. Stripe 文档:Metadata · 自建结账流程用在客户和订阅上的键值存储。
  3. Stripe 文档:Webhooks · 事件投递、签名验证和 invoice.paid 事件族。
  4. Stripe 文档:退款 · charge.refunded 事件与部分退款。
  5. Stripe 文档:拒付 · charge.dispute.created 与 charge.dispute.closed 的生命周期。
  6. Stripe 文档:按比例计费 · 周期中途换套餐如何产生差额发票行。
  7. Stripe 文档:Payment Links · 在托管链接上以 URL 参数传 client_reference_id。
  8. Rewardful 定价 · 按归因收入分档,支持 Stripe 和 Paddle;2026-09-18 查看。
  9. FirstPromoter 定价 · 按联盟带来的收入分档,支持 Stripe、Paddle、Chargebee;2026-09-18 查看。
话题发票级归因Stripe 联盟追踪订阅佣金退款扣回只追加账本订阅制联盟计划