Stripe Webhookによる多言語メール対応
SaaSをグローバルに展開するには、隠れた罠があります。私たちは、StripeのWebhookの中にその一つを見つけました。
私たちのシステムは、英語を話すユーザーに対して、購入確認、更新、および失敗の通知を日本語で送信していました。このバグは目立たなかったため、数ヶ月間も放置されていました。
私たちは、通貨から言語を推測することでこれを解決しました。
3つの設計オプションを検討しました:
- オプションA:データベースに言語を保存する。これには、既存ユーザーのためのマイグレーションとバックフィルが必要です。
- オプションB:Stripe APIから取得する。これではAPIコールが増え、多くの顧客は優先するロケールを設定していません。
- オプションC:Webhookのペイロード内の通貨を使用する。これはコストがかからず、データベースの変更も不要で、既存のユーザーにも即座に適用できます。
私たちはオプションCを選択しました。通貨は購入の瞬間に確定するシグナルです。ユーザーがUSDで支払えば英語が、JPYで支払えば日本語が届きます。
ロジックは単純です:
function lang_from_currency(string $currency): string {
$en_currencies = ['usd'];
return in_array(strtolower($currency), $en_currencies, true) ? 'en' : 'ja';
}
これは、Stripeの主要な4つのイベントすべてに機能します:
- checkout.session.completed
- invoice.payment_succeeded
- invoice.payment_failed
- customer.subscription.updated
また、PHPに関する技術的な罠も見つけました。
mb_language('Japanese') を使用すると、件名が ISO-2022-JP でエンコードされます。この設定で英語の件名を送信すると、GmailやOutlookはそれを異常なエンコーディングと判断します。これにより、スパムスコアが上がってしまいます。
修正方法は、言語に基づいてエンコーディングを切り替えることです:
mb_language($lang === 'en' ? 'uni' : 'Japanese');
'uni' を使用すると UTF-8 Base64 が使用されます。これにより、メールがスパムフォルダに振り分けられるのを防ぐことができます。
この修正から得られた3つの教訓:
- イベントペイロードを活用する。データがすでにWebhook内にあるなら、データベースには触れない。これにより、リスクとメンテナンスコストを削減できます。
- エンコーディングに注意する。複数の言語をサポートする場合、スパムフィルターを回避するために、件名のエンコーディングがコンテンツと一致していることを確認してください。
- ハードコードされた値を監査する。グローバル展開する際は、通知関数に言語設定がハードコードされていないか確認してください。
ステートレスな設計にすることで、システムのメンテナンスが容易になり、壊れにくくなります。
