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内にあるなら、データベースには触れない。これにより、リスクとメンテナンスコストを削減できます。
  • エンコーディングに注意する。複数の言語をサポートする場合、スパムフィルターを回避するために、件名のエンコーディングがコンテンツと一致していることを確認してください。
  • ハードコードされた値を監査する。グローバル展開する際は、通知関数に言語設定がハードコードされていないか確認してください。

ステートレスな設計にすることで、システムのメンテナンスが容易になり、壊れにくくなります。

Source: https://dev.to/susumun/multilingual-emails-from-a-stripe-webhook-inferring-language-from-currency-i99