Next.jsとStripeでECサイトを作る – 3

概要

Next.jsとStripeでECサイトを作ります。
今回の内容はStripeから商品情報を取得するAPIの作成です。

背景

以下の記事の続きです。

やりかた

Stripe SDKの準備

Stripe SDKを使う上で、まずはAPIキーをNext.jsの環境変数に設定します。

ecディレクトリ直下に.env.localファイルを作成します。

  • ec/
    • .next/
    • node_modules/
    • public/
    • src/
    • .env.local

.env.localの中身は次のようにします。

STRIPE_API_KEY=<制限付きAPIキー>
NEXT_PUBLIC_STRIPE_PUBLISHABLE_API_KEY=<公開可能APIキー>

公開可能APIキーはStripeダッシュボードの[開発者]-[APIキー]-[標準キー]に[公開可能キー]として表示されています。

Stripe SDKをインストールします。

ecディレクトリ直下で次のコマンドを実行します。

yarn add stripe

商品情報一覧を取得するAPIを作る

AppRouterでのAPIはアクセスするURLにあたるディレクトリにroute.tsを作成するそうです。

今回は/api/productにGETした時にStripeから商品情報の一覧を取得するAPIを作成します。

app配下にapiディレクトリ、apiディレクトリ配下にproductディレクトリ、productディレクトリ配下にroute.tsを作成します。

  • app/
    • api/
      • product/
        • route.ts

route.tsの中身は次のようにします。

import { NextResponse } from 'next/server'
import Stripe from 'stripe'

export const GET = async () => {
  const apiKey = process.env.STRIPE_API_KEY;
  if (!apiKey) {
    throw new Error("Failed to load the Stripe API key from .env");
  }

  const stripe = new Stripe(apiKey, { apiVersion: '2023-10-16' });
  const products = await stripe.products.list({ limit: 3, expand: ['data.default_price'] });

  return NextResponse.json({ status: 200, ...products });
}

リファレンスを見ている感じだとアロー関数ではなくfunctionを使うようですが、明確な理由は見つからなかったのでアロー関数でいきます。オシャレな気がしますし。

const products = await stripe.products.list({ limit: 3, expand: ['data.default_price'] });

ここで渡している expand: [‘data.default_price’] は製品情報に価格を含めるための引数です。Stripeでは製品情報と価格は別のオブジェクトとして持っているため、標準的な方法をとるのであれば製品用APIと価格用APIをそれぞれ実行しないといけません。
expandオプションはStripeのオブジェクトを展開してくれるようで、製品のdefault_priceを展開することで1度の呼び出しで価格も取ってくるようにしています。

expandできるかどうはリファレンスに書かれています。

https://docs.stripe.com/api/products/object

npm run devをして、API(localhost:3000/api/product)にアクセスするとStripeから拾ってきた商品情報をjsonで返してくれます。

この画面はfirefoxのいい感じに表示してくれる機能。値さえとれていれば何でもいい。

商品詳細情報を取得するAPIを作る

商品情報一覧はとれるようになったので、指定した商品の詳細情報(といっても上記の情報を1製品に絞っただけ)を取得できるようにします。

商品の詳細情報は /api/product/<Product ID>にGETした時に、Stripeから指定された商品情報を取得するようにします。

このような場合は動的ルーティングを使います。
Next.jsの動的ルーティングは、以下のように [ ] で囲まれたパスにアクセスが来た時に、[ ] の中身が route.ts から扱えるというものです。
下のとおり[id]ディレクトリとroute.tsファイルを作ってください。

  • app/
    • api/
      • product/
        • route.ts
        • [id]/
          • route.ts

[id]/route.tsの中身は次のとおりです。

import { NextRequest, NextResponse } from 'next/server'
import Stripe from 'stripe'

export const GET = async (req: NextRequest, { params }: { params: { id: string } }) => {
  const apiKey = process.env.STRIPE_API_KEY;
  if (!apiKey) {
    return NextResponse.json({ status: 403, msg: "Failed to load Stripe API key" });
  }

  const stripe = new Stripe(apiKey, { apiVersion: '2023-10-16' });
  const { id } = params;

  try {
    const product = await stripe.products.retrieve(id);
    return NextResponse.json({ status: 200, ...product });
  } catch (error) {
    const { type, message } = error;
    const status = type === 'StripeInvalidRequestError' ? 404 : 500;
    return NextResponse.json({ status, msg: message });
  }
}

req: NextRequest は使ってないからいらないと思うじゃないですか。
消すとidが受け取れなくなり、↓のエラーが出ます。

Cannot destructure property ‘id’ of ‘params’ as it is undefined.

またnpm run devをして、API(localhost:3000/api/product/<Product ID>)にアクセスします。
Product IDはStripeを見にいってもいいですし、商品情報一覧のAPIから戻ってきたjson内に書かれているものから適当に選んでもいいです。

製品情報が1つだけ返ってくれば成功です。

おまけ

参考

App RouterでのAPIのリファレンスです。
https://ja.next-community-docs.dev/docs/app-router/building-your-application/routing/route-handlers

コメント