はじめに
Dubは、短いリンクを作成、共有、追跡するのに役立つ強力なリンク管理プラットフォームです。Dodo PaymentsをDubと統合することで、顧客が購入を完了したときに自動的に販売転換イベントを追跡でき、マーケティングキャンペーンや紹介プログラムのROIを測定できます。
顧客が以下のいずれかを行ったときに、Dubに「販売」イベントが記録されます。
- 一回限りの支払いを完了する
- 有料プランに加入する
- 定期購読の支払いを行う
この統合には、リンクで転換追跡が有効になっているDubアカウントが必要です。
仕組み
Dubは、ユーザーがあなたのDub短縮リンクをクリックしたときに、クッキーに保存されたユニークなクリックID (dub_id) を通じて訪問者を追跡します。販売をリンクに帰属させるには、次の手順を実行する必要があります。
- チェックアウトセッションを作成する際に、
dub_idクッキーからDubのクリックIDを取得する
- 顧客の外部IDとともに、支払いメタデータにクリックIDを保存する
- 支払いが成功したときに、DubのTrack APIを使用して販売データを送信する
これにより、Dubは成功した販売を元のリンククリックに一致させ、完全な転換帰属を提供します。
前提条件
この統合を設定する前に、次のことを確認してください。
- ワークスペースを持つDubアカウント
- リンクの転換追跡が有効になっている
- DubのAPIキー(設定 → APIキーの下にあるDubダッシュボードで入手可能)
始め方
Dubでの転換追跡を有効にする
Dubダッシュボードで、販売を追跡したいリンクの転換追跡を有効にします。これにより、顧客が購入を完了したときにDubが販売イベントを記録できるようになります。 DubのAPIキーを取得する
Dubダッシュボード → 設定 → APIキーに移動し、conversions.writeスコープで新しいAPIキーを作成します。APIキーを安全に保管し、クライアント側のコードに公開しないでください。
チェックアウトでクリックIDを取得する
チェックアウトセッションを作成する際に、クッキーからDubのクリックIDを取得し、支払いメタデータに追加します。
Webhookを介して販売データを送信する
支払いが成功したときに、DubのTrack APIに販売データを送信するWebhookを設定します。
完了!
販売転換イベントは、リンクへの完全な帰属とともに、Dubの分析ダッシュボードに表示されるようになります。
実装ガイド
ステップ1: チェックアウトメタデータにクリックIDと顧客IDを追加する
チェックアウトセッションを作成する際に、クッキーからDubのクリックIDを取得し、顧客の外部IDとともに支払いメタデータに含めます。
import { cookies } from 'next/headers';
import DodoPayments from 'dodopayments';
const client = new DodoPayments();
export async function createCheckout(productId: string, customerId: string) {
// Capture Dub click ID from cookie
const dubClickId = cookies().get('dub_id')?.value;
const payment = await client.payments.create({
billing: {
city: 'New York',
country: 'US',
state: 'NY',
street: '123 Main St',
zipcode: '10001',
},
customer: {
email: '[email protected]',
name: 'John Doe',
},
product_id: productId,
metadata: {
dub_click_id: dubClickId, // Store Dub click ID
dub_external_id: customerId, // Store your customer's unique ID
},
});
return payment;
}
ステップ2: Dubに販売データを送信する
支払いが成功したときに、DubのTrack APIに販売データを送信するWebhookエンドポイントを設定します。
Webhookセクションを開く
Dodo Paymentsダッシュボードで、Webhooks → + エンドポイントを追加に移動し、統合のドロップダウンを展開します。 変換を設定する
DubのTrack Sale API用に支払いデータをフォーマットするために変換コードを編集します。
テストと作成
サンプルペイロードでテストし、作成をクリックして統合を有効にします。
変換コードの例
基本的な販売追跡
支払いが成功したときに販売を追跡します:
function handler(webhook) {
if (webhook.eventType === "payment.succeeded") {
const payment = webhook.payload.data;
// Only send to Dub if click ID exists in metadata
if (payment.metadata && payment.metadata.dub_click_id) {
webhook.payload = {
clickId: payment.metadata.dub_click_id,
externalId: payment.metadata.dub_external_id || payment.customer.customer_id,
amount: payment.total_amount, // Ensure the amount is in cents
currency: payment.currency || "USD",
paymentProcessor: "dodo",
invoiceId: payment.payment_id,
metadata: {
customer_email: payment.customer.email,
customer_name: payment.customer.name,
product_id: payment.product_cart ? payment.product_cart.map(product => product.product_id).join(', ') : undefined,
},
};
} else {
// Cancel dispatch if no click ID (organic traffic)
webhook.cancel = true;
}
}
return webhook;
}
サブスクリプション販売の追跡
初回のサブスクリプションと定期的な支払いの両方を追跡します:
function handler(webhook) {
const data = webhook.payload.data;
// Track initial subscription activation
if (webhook.eventType === "subscription.active") {
if (data.metadata && data.metadata.dub_click_id) {
webhook.payload = {
clickId: data.metadata.dub_click_id,
externalId: data.metadata.dub_external_id || data.customer.customer_id,
amount: data.recurring_pre_tax_amount, // Amount in cents
currency: data.currency || "USD",
paymentProcessor: "dodo",
invoiceId: data.subscription_id,
eventName: "Subscription Started",
metadata: {
subscription_id: data.subscription_id,
product_id: data.product_id,
billing_interval: data.payment_frequency_interval,
customer_email: data.customer.email,
},
};
} else {
// Cancel dispatch if no click ID (organic traffic)
webhook.cancel = true;
}
}
// Track recurring subscription payments
if (webhook.eventType === "subscription.renewed") {
if (data.metadata && data.metadata.dub_click_id) {
webhook.payload = {
clickId: data.metadata.dub_click_id,
externalId: data.metadata.dub_external_id || data.customer.customer_id,
amount: data.recurring_pre_tax_amount,
currency: data.currency || "USD",
paymentProcessor: "dodo",
invoiceId: `${data.subscription_id}_${Date.now()}`,
eventName: "Subscription Renewed",
metadata: {
subscription_id: data.subscription_id,
product_id: data.product_id,
customer_email: data.customer.email,
},
};
} else {
// Cancel dispatch if no click ID (organic traffic)
webhook.cancel = true;
}
}
return webhook;
}
税金除外での販売追跡
正確な収益追跡のために、税抜き金額のみをDubに送信します:
function handler(webhook) {
if (webhook.eventType === "payment.succeeded") {
const payment = webhook.payload.data;
if (payment.metadata && payment.metadata.dub_click_id) {
// Calculate pre-tax amount (total minus tax)
const preTaxAmount = payment.total_amount - (payment.tax || 0);
webhook.payload = {
clickId: payment.metadata.dub_click_id,
externalId: payment.metadata.dub_external_id || payment.customer.customer_id,
amount: preTaxAmount, // Pre-tax amount in cents
currency: payment.currency || "USD",
paymentProcessor: "dodo",
invoiceId: payment.payment_id,
metadata: {
total_amount: payment.total_amount,
tax_amount: payment.tax || 0,
customer_email: payment.customer.email,
},
};
} else {
// Cancel dispatch if no click ID (organic traffic)
webhook.cancel = true;
}
}
return webhook;
}
カスタムイベント名での販売追跡
異なるタイプの販売を分類するためにカスタムイベント名を使用します:
function handler(webhook) {
if (webhook.eventType === "payment.succeeded") {
const payment = webhook.payload.data;
if (payment.metadata && payment.metadata.dub_click_id) {
// Determine event name based on payment type
let eventName = "Purchase";
if (payment.subscription_id) {
eventName = "Subscription Purchase";
} else if (payment.metadata && payment.metadata.is_upgrade) {
eventName = "Plan Upgrade";
}
webhook.payload = {
clickId: payment.metadata.dub_click_id,
externalId: payment.metadata.dub_external_id || payment.customer.customer_id,
amount: payment.total_amount,
currency: payment.currency || "USD",
paymentProcessor: "dodo",
invoiceId: payment.payment_id,
eventName: eventName,
metadata: {
product_id: payment.product_cart ? payment.product_cart.map(product => product.product_id).join(', ') : undefined,
customer_email: payment.customer.email,
},
};
} else {
// Cancel dispatch if no click ID (organic traffic)
webhook.cancel = true;
}
}
return webhook;
}
代替:クライアントサイドの実装
Webhookを使用する代わりに、サーバーから販売を追跡したい場合は、成功した支払いの後にDubのTrack APIを直接呼び出すことができます:
'use server';
import { Dub } from 'dub';
const dub = new Dub();
export async function trackSale(
paymentId: string,
clickId: string,
customerId: string,
amount: number,
currency: string
) {
await dub.track.sale({
clickId: clickId,
externalId: customerId,
amount: amount,
currency: currency,
paymentProcessor: 'dodo',
invoiceId: paymentId,
});
}
ベストプラクティス
クリックIDを早期に取得する: ユーザーが離脱して後で戻ってきた場合でも、正確な帰属を確保するために、チェックアウトフローの早い段階でDubのクリックIDを保存してください。
- メタデータにクリックIDを常に含める: クリックIDがないと、Dubは収益をあなたのリンクに帰属させることができません
- 外部IDを一貫して使用する: 正確な顧客レベルの分析のために、システムで使用している同じ顧客IDを渡してください
- オーガニックトラフィックを適切に処理する: クリックIDがない場合は、不要なAPI呼び出しを避けるために
webhook.cancel = trueを設定してください
- サンプル支払いでテストする: 本番環境に移行する前に、統合が正しく機能することを確認してください
- Dubダッシュボードを監視する: 販売が正しく表示され、適切に帰属されていることを確認してください
重要な注意事項
- 金額形式: Dubは金額をセント単位で期待します(例:$10.00 = 1000)
- 通貨: ISO 4217通貨コードを使用してください(USD、EUR、GBPなど)
- 無料トライアル: $0の支払いは販売として追跡されません
- 返金: 正確な収益報告のために、必要に応じて返金を別途追跡することを検討してください
トラブルシューティング
- DubのAPIキーが正しく、
conversions.writeスコープを持っていることを確認してください
dub_click_idが支払いメタデータにキャプチャされ、保存されていることを確認してください
- Webhookの変換がペイロードを正しくフォーマットしていることを確認してください
- Webhookが
payment.succeededイベントでトリガーされていることを確認してください
- Dubリンクの転換追跡が有効になっていることを確認してください
- ユーザーがチェックアウト前にあなたのDub短縮リンクをクリックしていることを確認してください
dub_idクッキーがあなたのドメインに正しく設定されていることを確認してください
- チェックアウト作成と支払い完了の間でクリックIDが一致していることを確認してください
- チェックアウトセッションを作成する前にクリックIDを取得していることを確認してください
- JSON構造がDubのTrack Sale APIフォーマットに一致していることを確認してください
- 必要なすべてのフィールド(
clickId、externalId、amount)が存在することを確認してください
- 金額がセント単位(整数、少数ではなく)であることを確認してください
- APIエンドポイントURLが正しいことを確認してください:
https://api.dub.co/track/sale
- サンプルWebhookペイロードで変換をテストしてください
payment.succeededイベントでのみ追跡していることを確認してください。payment.processingではありません
- 各販売に対してユニークな
invoiceId値を使用してください
- サブスクリプションの場合、更新時に重複を防ぐためにタイムスタンプや請求期間を追加してください
追加リソース