使用 Dub 将 Stripe 自定义客户创建流程与点击归因关联:dubCustomerExternalId 与 dubClickId 元数据实战指南
2026/9/12 1:51:58 网站建设 项目流程

使用 Dub 将 Stripe 自定义客户创建流程与点击归因关联:dubCustomerExternalId 与 dubClickId 元数据实战指南

【免费下载链接】dubThe modern link attribution platform. Loved by world-class marketing teams like Framer, Perplexity, Superhuman, Twilio, Buffer and more.项目地址: https://gitcode.com/GitHub_Trending/du/dub

导读

本文面向使用 Stripe 但不走 Checkout Session 标准创建流程、而是自行调用stripe.customers.create/stripe.customers.update管理客户的开发者,讲解如何在 Stripe 客户对象上写入 Dub 归因元数据(dubCustomerExternalIddubClickId),让 Dub 在客户产生购买后自动把订单金额、币种等信息回填到用户最初点击的链接(Click Event)上,从而实现完整的"点击 → 线索 → 销售"归因闭环。读完本文,你将掌握 Dub Stripe 集成的客户侧接入方式、元数据字段的确切语义,以及 Dub 服务端 Webhook 处理这些元数据时的完整解析与关联逻辑。

一、为什么要在 Stripe 客户上写入 Dub 元数据

Dub 的 Stripe 集成核心机制是:通过 Webhook 监听 Stripe 侧的事件,再把事件中的金额、币种等销售信息关联回 Dub 侧已经记录的点击事件。而关联的"锚点"就是 Stripe 客户对象上的metadata字段。

在标准流程(stripe-checkout.md)中,你只需在checkout.sessions.create时把用户 ID 写入metadata.dubCustomerExternalId,Dub 就能在checkout.session.completed事件到达时完成归因。但如果你不使用 Checkout Session,而是直接在服务端创建 Stripe 客户(例如自有支付页面、自定义订阅管理、B2B 记账等场景),就必须在客户创建/更新流程中主动传递归因信息——这正是本文所依托文档 stripe-customers.md 的适用场景。

二、核心概念:两个元数据字段

在 Stripe 客户对象的metadata字段中,需要写入两个 Dub 约定的键:

元数据键含义来源用途
dubCustomerExternalId你数据库中该用户的唯一用户 ID你的业务数据库(如user.id让 Dub 在本地Customer记录与 Stripe 客户之间建立一一对应关系
dubClickId用户在落地页产生点击事件时下发的dub_idHTTP 请求头dub_id(由 Dub 的点击跟踪脚本或服务端 SDK 下发)让 Dub 把新客户关联回最初带来该访客的链接与点击事件

需要特别说明的是:只有dubCustomerExternalId是必须的dubClickId的作用是在客户首次出现时找到对应的点击事件,从而把客户"挂"到某个链接/合作伙伴上。从 sync-customer.ts/api/stripe/integration/webhook/utils/sync-customer.ts#L82-L96) 的源码可以看到,如果 metadata 中没有dubClickId,Webhook 会返回"Click ID not found in Stripe customer metadata, skipping..."并跳过新客户创建;而对于已存在的 Dub 客户,即使没有dubClickId也能正常更新。

三、获取点击 ID(dub_id)

dub_id是 Dub 在每次链接点击事件中生成的唯一标识,通过请求头dub_id随用户请求一起下发到你的服务端。在 Node.js 服务中读取方式如下:

const dub_id = req.headers.get("dub_id");

它通常配合你现有的会话体系(Cookie、JWT 等)一起使用:用户在落地页被 Dub 跟踪后,后续到达你的支付/注册接口时,请求头中就会携带该用户来源对应的dub_id

四、在 Stripe 客户创建流程中写入元数据

当你的后端调用stripe.customers.create创建客户时,将用户 ID 作为dubCustomerExternalId、请求头中的dub_id作为dubClickId一起写入metadata

import { stripe } from "@/lib/stripe"; const user = { id: "user_123", email: "user@example.com", teamId: "team_xxxxxxxxx", }; const dub_id = req.headers.get("dub_id"); await stripe.customers.create({ email: user.email, name: user.name, metadata: { dubCustomerExternalId: user.id, dubClickId: dub_id, }, });
  • emailname可保持你原有逻辑不变;
  • dubCustomerExternalId建议使用数据库中的主键字符串(如user_123),保证全局唯一、稳定不变;
  • dubClickId直接透传请求头dub_id即可,缺失时 Dub 端会跳过新客户创建(详见下文源码解析)。

五、在 Stripe 客户更新流程中补充元数据

如果你的应用是先创建 Stripe 客户、后补录归因信息(例如用户在注册后一段时间才通过推广链接完成转化),可以在stripe.customers.update中补齐同样的两个字段:

import { stripe } from "@/lib/stripe"; const user = { id: "user_123", email: "user@example.com", teamId: "team_xxxxxxxxx", }; const dub_id = req.headers.get("dub_id"); await stripe.customers.update(user.id, { metadata: { dubCustomerExternalId: user.id, dubClickId: dub_id, }, });

由于 Stripe 的metadata是整体合并写入的,更新时只需携带归因相关键即可,不会覆盖你已有的其他业务元数据。

六、源码级原理:Dub 如何消费这些元数据

6.1 Webhook 入口与事件类型

Dub 的 Stripe 集成 Webhook 位于 apps/web/app/(ee)/api/stripe/integration/webhook/api/stripe/integration/webhook),其中sync-customer.ts专门处理customer.createdcustomer.updated两类事件。这两个事件正是触发"客户关联"的时机:客户创建/更新时,Dub 会读取客户对象上的metadata

6.2 外部 ID 的解析优先级

在 get-dub-customer-external-id-from-metadata.ts/api/stripe/integration/webhook/utils/get-dub-customer-external-id-from-metadata.ts#L8-L21) 中,Dub 从 metadata 解析外部 ID 的顺序是:

  1. dubCustomerExternalId(本指南推荐的主键字段)
  2. dubCustomerId(旧版/兼容字段)
  3. user_id(Lemon Squeezy 客户迁移到 Stripe 后的兼容字段)

解析结果为空字符串或null时返回undefined,随后sync-customer.ts会返回"External ID not found in Stripe customer metadata, skipping..."直接跳过。

6.3 客户查找、更新与创建的完整流程

sync-customer.ts的实际处理链路如下:

  1. 从事件对象中取出stripeCustomer.metadata,解析dubCustomerExternalIddubClickId
  2. OR条件在 Dub 数据库中查找客户:(projectId + externalId)stripeCustomerId
  3. 若已存在:直接更新该客户的stripeCustomerIdprojectConnectId(Stripe Account ID)、externalId,并在 Stripe 提供非空值时才覆盖name/email
  4. 若不存在:必须存在dubClickId才会继续——通过 Tinybird 查询点击事件(getClickEvent),校验该链接归属同一 workspace,然后调用getOrCreateCustomer创建 Dub 客户(cus_前缀 ID),并继承点击事件中的linkIdprogramIdpartnerIdclickIdclickedAtcountry等字段;
  5. 新客户创建成功后,记录一条名为New customer的 Lead 事件,同时递增链接的leads计数、workspace 的usage,并异步触发工作流(leadRecorded)、合作伙伴 Postback(lead.created)、workspace Webhook(lead.created)与 Google Ads 转化上传。

并发场景下若两个 Webhook 同时创建同一客户,getOrCreateCustomerfindMode: "first"逻辑会保证只创建一个,另一个走更新分支。

6.4 客户创建/更新后的销售归因

当客户在后续产生购买时,Dub 通过另外两个处理器完成销售归因:

  • checkout.session.completed(checkout-session-completed.ts/api/stripe/integration/webhook/checkout-session-completed.ts)):读取 Checkout Session 的 metadata 或已关联客户,找到该客户的 Lead 事件后记录 Sale;
  • invoice.paid(invoice-paid.ts/api/stripe/integration/webhook/invoice-paid.ts)):先用stripeCustomerId反查 Dub 客户,查不到时再回连 Stripe 获取客户 metadata 中的dubCustomerExternalId,通过(projectConnectId, externalId)复合唯一键更新客户并补记stripeCustomerId,最后记录"订阅续费/发票支付"类型的 Sale 事件。

两个处理器都会在 Redis 中以trackSale:stripe:invoiceId:{invoiceId}为键做 7 天幂等去重,避免同一张发票被重复计入销售额。这也解释了为什么文档强调"当客户产生购买时,Dub 会自动将购买详情(发票金额、币种等)关联到原始点击事件"——正是这套 Webhook 链路在背后完成关联。

七、集成后的数据流全景

整个归因链路可以概括为:

  1. 用户点击 Dub 短链 → Dub 生成dub_id点击事件并记录到 Tinybird;
  2. 用户在你的站点完成注册/支付,请求头携带dub_id
  3. 你的服务端创建/更新 Stripe 客户,把dubCustomerExternalId(用户 ID)与dubClickIddub_id)写入metadata
  4. Stripe 触发customer.created/customer.updatedWebhook,Dub 在本地创建或更新Customer,并记录New customerLead 事件;
  5. 客户后续支付触发checkout.session.completed/invoice.paid,Dub 记录 Sale 事件(金额、币种、发票号),递增链接sales/saleAmount/conversions,必要时为合作伙伴生成佣金并发送 Postback。

八、最佳实践与注意事项

  • 用户 ID 必须稳定唯一dubCustomerExternalId一旦确定就不要变更,它是 Dub 客户记录与 Stripe 客户长期绑定的主键;若 ID 变化,会导致历史归因断裂。
  • 保持幂等:Webhook 对同一发票做了 Redis 去重,你侧重复调用customers.update不会产生副作用,可以放心重试。
  • 非 USD 币种自动换算:销售金额会统一换算为 USD 记录(convert-currency.ts),不需要你自行处理币种转换。
  • 免费试用与折扣控制:工作区可在 Stripe 集成设置中配置freeTrials(是否将订阅免费试用记录为 Lead)与discountCodeRestrictions(仅首次交易可使用折扣码),配置模式见 schema.ts。
  • 订阅场景的元数据优先级:若同时使用了 Checkout Session 流程,Dub 会优先读取 Checkout Session 的metadata,其次才是已连接客户对象上的metadata,两种方式互不冲突。

通过本文的接入方式,即使你的支付流程完全自定义、不经过 Stripe Checkout,也能让 Dub 精确地把每一笔销售回溯到最初的点击来源,为链接转化率分析、合作伙伴佣金结算和 Google Ads 转化回传提供准确的数据底座。

【免费下载链接】dubThe modern link attribution platform. Loved by world-class marketing teams like Framer, Perplexity, Superhuman, Twilio, Buffer and more.项目地址: https://gitcode.com/GitHub_Trending/du/dub

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询