AsaDesign

Next.jsワークショップやってみた③外部連携編

参考サイト:Stripeを利用して、商品・料金情報を登録しよう – Zenn

決済サービスStripeの設定

  • アカウント作成:メールアドレス、名前で登録できます。
  • 店舗のアカウント作成:店舗の名前を入力します。
  • 商品登録:商品名、説明、画像、価格を登録します。
  • クロスセル(関連商品):コーヒー豆にコーヒードリッパーを関連づけます。

APIでデータ取得

APIキー

キーの種類用途
公開キーフロントエンドアプリで、カードのtoken化などに利用する。
シークレットキーStripeの公開しているAPI全てにアクセスができるAPIで、万が一漏洩した場合には顧客情報や注文データなどを取得されるリスクが生じる。
制限付きのキーアクセスできるリソースや操作を個別に設定することができる。
Zennさんより引用

今回は必要最低限の権利を付与した制限付きキーを作ります。

リソースタイプ権限
Products読み取り
Prices読み取り
Checkout Sessions書き込み
それ以外なし
Zennさんより引用

次に、Next.jsにAPIキーを設定します。

STRIPE_API_KEY=rk_test_xxxxx
NEXT_PUBLIC_STRIPE_PUBLISHABLE_API_KEY=pk_test_xxxxxx
  • STRIPE_API_KEY:制限付きキー
  • NEXT_PUBLIC_STRIPE_PUBLISHABLE_API_KEY:公開キー

フロントエンドで使うものには「NEXT_PUBLIC_」を付けますが、第三者に見られたくない値には付けないように注意が必要だそうです。

Stripe SDKをインストールする

Stripe SDK | Stripe のドキュメント

npm install stripe

package.jsondependenciesにstripeが追加されます。

  "dependencies": {
  ...
   "stripe": "^16.12.0"
  },

APIの実装

pages/api/products.tsに追記します。

Stripe SDKでは、new Stripe()の戻り値に顧客や商品・料金などのリソースへアクセスするプロパティが含まれています。

商品の一覧を取得するには、stripe.products.list()を利用します。
https://docs.stripe.com/api/products/list

Zenn
  import Stripe from "stripe";
  ...
  const stripe = new Stripe(process.env.STRIPE_API_KEY  ?? "", {
    maxNetworkRetries: 3 // ネットワークエラーでStripe API呼び出しが失敗した時のリトライ回数を指定
  })

【エラー小話】型 ‘string | undefined’ の引数を型 ‘string’ のパラメーターに割り当てることはできません。

【Typescript】型 ‘string | undefined’ の引数を ‘string’ のパラメーターに割り当てることができません。を解決したい – Qiita

→ 「?? “”」を追記。「envを読み込むタイミングで型の絞り込み(narrowing)をします」と解説されていましたが、まだ難しくて分かっていません。

async/awaitでデータ取得

before

export default function handler(req: NextApiRequest, res: NextApiResponse) {

after

export default async function handler(req: NextApiRequest, res: NextApiResponse) {

before


  res.status(200).json(
    [
      {
        name: "サバ缶",
        price: 150,
      },
      {
        name: "ツナ缶",
        price: 300,
      }
    ]
  )

after

  const products = await stripe.products.list()
  res.status(200).json(products)

curlコマンドで確認する場合

以下のコマンドを実行します。

% curl http://localhost:3000/api/products
{"object":"list","data":[{"id":"prod_QsNcmlcynzeNFB","object":"product","active":true,"attributes":[],"created":1726723479,"default_price":"price_1Q0cr5Ap7bPBQwOfQcVxoBmr","description":"産地直送のとうもろこしです。"...

必要なデータだけ表示する

pages/api/products.ts

before:とりあえず取得したデータを全てJSONで表示しています。

  const products = await stripe.products.list()
  res.status(200).json(products)

after:データ取得後、JSONを整形しています。また、料金情報を取得するためawait stripe.prices.list()で別途APIリクエストをしています。

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
...

const response = await Promise.all(products.data.map(async (product, i) => {
    const prices = await stripe.prices.list({
      product: product.id,
    })
    return {
      id: product.id,
      description: product.description,
      name: product.name,
      images: product.images,
      unit_label: product.unit_label,
      prices: prices.data.map(price => {
        return {
          id: price.id,
          currency: price.currency,
          transform_quantitiy: price.transform_quantity,
          unit_amount: price.unit_amount,
        }
      })
    }
  }))

  res.status(200).json(response)
}