Stripeの商品データをAlgoliaにインデックスする方法

Algolia Advent Calendar 2021 19日目の記事です。

Stripeには、ベータ版ですが「Search API」があります。これを使うことで、商品データの検索などを行うことができます。

ですが、複数のデータを使った検索やレコメンデーションなどを求め始めると、Algoliaを使った方が効率的なケースが多いでしょう。

ということで、今回は簡単にですが、Stripeに登録または更新した商品データをAlgoliaにインデックスする方法を紹介します。

データの流れ

商品更新からインデックスまでは、以下のようなフローで行います。

  • Stripeで商品データを更新する
  • Stripe WebhookがAPIを呼び出す
  • Expressで実装したAPIが、Algoliaにインデックスする

Stripe Webhook用 APIを用意する

まずはStripeのWebhookイベントを受け付けるAPIを用意します。

いろいろ方法はありますが、今回は手軽にStripe CLIを利用します。

サンプルアプリをセットアップ

Stripe CLIでサンプルアプリをローカルにDLします。accept-a-paymentなど、Webhookの実装があるものを選ぶようにしましょう。

% stripe samples create accept-a-payment
 ⣾ Downloading accept-a-payment ✔ Finished downloading
 ✔ Selected integration: prebuilt-checkout-page 
 ✔ Selected client: react-cra 
 ✔ Selected server: node 
 ⣾ Copying files over… accept-a-payment ✔ Files copied
 ⣷ Configuring your code… accept-a-payment ✔ Project configured

サーバー側のプロジェクトをセットアップ、起動させましょう。

% cd accept-a-payment/server
% npm install
% npm start
$ node server.js
 body-parser deprecated undefined extended: provide extended option server.js:20:17
 Node server listening on port 4242!

Stripe CLIで、ローカルのAPIにWebhookイベントをforwardする

Stripe CLIのlistenコマンドで、Stripe WebhookのイベントをローカルAPIで受けれるようにしましょう。

% stripe  listen --forward-to localhost:4242/webhook
 ⡿ Getting ready… > Ready! You are using Stripe API Version [2020-08-27]. Your webhook signing secret is whsec_xxxx

Webhookの実装を変更する

最後に商品データの作成と更新を受け付けるように、Webhookの実装を変更します。


app.post('/webhook', async (req, res) => {
  let event;

  // Check if webhook signing is configured.
  if (process.env.STRIPE_WEBHOOK_SECRET) {
    // Retrieve the event by verifying the signature using the raw body and secret.
    let signature = req.headers['stripe-signature'];

    try {
      event = stripe.webhooks.constructEvent(
        req.rawBody,
        signature,
        process.env.STRIPE_WEBHOOK_SECRET
      );
    } catch (err) {
      console.log(`⚠️  Webhook signature verification failed.`);
      return res.sendStatus(400);
    }
  } else {
    // Webhook signing is recommended, but if the secret is not configured in `.env`,
    // retrieve the event data directly from the request body.
    event = req.body;
  }

  if (!/product/.test(event.type)) {
    res.sendStatus(200);
    return
  }
  if (['product.updated','product.created'].includes(event.type)) {
    const product = event.data.object
    const param = {
      objectID: product.id,
      description: product.description,
      name: product.name,
    }
    if (product.metadata) {
      Object.entries(product.metadata).forEach(([key, value]) => {
        param[key] = value
      })
    }
    // Algolia Index処理
  }
  res.sendStatus(200);
});

APIにAlgoliaのクライアントライブラリを追加

Algoliaへのインデックス処理を実行するライブラリを追加します。

% npm i -S algoliasearch

Algoliaに商品データをインデックスする

先ほど変更したコードをさらに編集します。// Algolia Index処理部分を以下のように変更しましょう。

  const algoliaApplicationId = 'XXXX'
  const algoliaAdminApiKey = 'XXXX'
  const client = algoliasearch(algoliaApplicationId, algoliaAdminApiKey);
  const index = client.initIndex("IndexName");
  await index.saveObject(param)

この状態で、npm startを再実行し、サーバーを再起動させましょう。

Stripeの商品を追加または更新する

あとはStripe DashboardまたはCLI、APIでデータを更新して実際に動かしてみましょう。Stripe CLIの場合は、以下のような形になります。

% stripe products create --name test --description "example product from cli" 
 {
   "id": "prod_Knw0BhTC5MOcdI",
   "object": "product",
   "active": true,
   "attributes": [
 ],
   "created": 1639900154,
   "description": "example product from cli",
   "images": [
 ],
   "livemode": false,
   "metadata": {
   },
   "name": "test",
   "package_dimensions": null,
   "shippable": null,
   "statement_descriptor": null,
   "tax_code": null,
   "type": "service",
   "unit_label": null,
   "updated": 1639900154,
   "url": null
 }

AlogliaのIndexを確認する

Algolia側のDashboardで、データの追加や更新が確認できていればOKです。

Tips1: データ更新時のキーについて

algoliasearchライブラリのsaveObjectでは、objectIDが更新時のキーになります。

自動生成させてもよいのですが、Stripeの商品IDがユニークなので、そのままobjectIDに指定して作成も更新も同じ処理でできるようにしています。

Tips2: 価格について

Stripeは、商品データに価格情報を持ちません。商品のIDを利用して、stripe.prices.listを実行し、価格データを取得する必要があります。

AlgoliaにIndexする場合、ケースによってはprice側でIndexをそれぞれ作成し、productが持つデータをそこにマージする形が良いケースもあるかもしれません。

この辺りは、もう少しまとめたものを来年にでも公開できればと思います。

Comment