WEBその他

【Stripe】ExpressにStripeCheckoutの決済機能を組み込む

WEB
この記事は約13分で読めます。

implの藤谷です。
stripe checkoutのUIを使用した決済機能をexpressで実装する方法を解説します。

解説するユースケース

今回の記事で取り扱うケースは下記になります。

payment(一括支払)

カード決済 & コンビニ決済

subscription(継続支払)

カード決済のみ
※ サブスク設定でコンビニ払いを選択することは仕様上出来ない

stripe側での事前設定

  1. 鍵の取得
    stripe dashboardから新規にビジネスを作成し、Publishable keyとSecret keyをソースコードのenvに設定します
  2. Productの作成
    テスト用のプロダクト(商品)を作成します。
    testProduct1はpayment、testProduct2はsubscriptionに設定しています

以上でstripe側の設定は完了です。

stripeClient作成

expressアプリケーションにnpm経由でstripeをインストールします。

npm i stripe

その後、プロジェクト内共通で使用できるstripeClientオブジェクトを作成します。
new Stripeでインスタンスを生成し、引数にSecretKeyとバージョン情報を設定します。

import Stripe from "stripe";
import dotenv from "dotenv";
dotenv.config();

const stripeSecret = process.env.SECRET_KEY;

export const stripeClient = new Stripe(stripeSecret, {
  apiVersion: "2022-11-15",
});

stripe payment決済実装

先に、paymentを利用した決済機能を解説します。

payment用html作成

今回は、paymentとsubscriptionでhtmlファイルを分けているので、まずはpayment用から作成します。

フォームには商品を識別するpricaIdと、購入する商品の個数を選ぶquantityを設置します。
actionで/api/payment/create-checkout-sessionのpayment用メソッドのエンドポイントに飛ばすようにしています。

<!DOCTYPE html>
<html lang="ja">
  <body>
    <main>
      <div>
        <h3>stripe checkout payment</h3>
      </div>
      <form
        action="http://localhost:3000/api/payment/create-checkout-session"
        method="post"
      >
        <div>
          <label>priceId</label>
          <input type="text" name="priceId" />
        </div>
        <div>
          <label>quantity</label>
          <input type="number" name="quantity" />
        </div>
        <button type="submit">pay now</button>
      </form>
    </main>
  </body>
</html>

router

実際のメソッドはProductControllerで記述し、下記のファイルにルート情報をまとめています。

import express from "express";
const router = express.Router();
import { ProductController } from "../controller/ProductController";

const productContext = new ProductController();

router.post(
  "/payment/create-checkout-session",
  productContext.buyCheckoutPayment
);
module.exports = router;

buyCheckoutPaymentメソッド

buyCheckoutPaymentメソッドに、payment決済のロジックを実装します。
最初に実装したstripeClientを使用して一連の決済フローに関する設定を行います。
決済フローの設定をsessionに格納し、成功時にsession.urlを呼び出すことでstripe checkoutから提供されるUIをレンダリングさせています。

  • payment_method_types
    支払方法の設定。今回はカード決済とコンビニ決済を指定
  • line_itemsd
    決済の対象になる商品情報の設定。checkout.htmlからpriceIdとquantityを取得
  • mode
    決済プランの設定。paymentを指定。
  • success_url
    決済フロー成功時のリダイレクト先を設定。stripe公式のsuccess.htmlファイルを引用して使用する。
  • cancel_url
    決済フロー失敗時のリダイレクト先を設定。stripe公式のcancel.htmlファイルを引用して使用する。
import { Request, Response } from "express";
import { stripeClient } from "../stripeClient";
import dotenv from "dotenv";
dotenv.config();

export class ProductController {
 async buyCheckoutPayment(req: Request, res: Response): Promise<void> {
    const { priceId, quantity } = req.body;
    const base_url = process.env.BASE_URL;

    try {
      const session = await stripeClient.checkout.sessions.create({
        payment_method_types: ["card", "konbini"],
        line_items: [
          {
            price: priceId,
            quantity: quantity,
          },
        ],
        mode: "payment",
        success_url: `${base_url}/success.html`,
        cancel_url: `${base_url}/cancel.html`,
      });

      res.redirect(303, session.url);
    } catch (error: any) {
      res.status(401).json({
        message: error.message,
      });
    }
  }
}

paymentのcheckout.htmlにデータを入れて送信します。
情報が正しければ、stripe checkoutのpayment用UIが表示されます。

  • カード決済の場合
    カード決済の場合は、諸々のカード情報を入れて支払ボタンをクリックすると、成功時はsuccess_urlで設定したページに遷移します。
  • コンビニ決済の場合
    コンビニ決済の場合は、情報を入力して支払ボタンをクリックすると、支払詳細画面に遷移してコンビニの選択と支払詳細の確認や印刷ができます。
    ※ 決済コード等の情報は伏せています。

ここまでが、paymentでの決済フロー解説になります。

stripe subscription決済実装

subscription側の実装は、先ほどpaymentを実装したProductContollerに追加する形で実装します。

subscription用html作成

subscriptionページに遷移するhtmlファイルを作成します。
フォームで扱う値はpaymentのhtmlと同じで、form action で指定するエンドポイントをサブスクをうに変更しています。

<!DOCTYPE html>
<html lang="ja">
  <body>
    <main>
      <div>
        <h3>stripe checkout subscription</h3>
      </div>
      <form
        action="http://localhost:3000/api/subscription/create-checkout-session"
        method="post"
      >
        <div>
          <label>priceId</label>
          <input type="text" name="priceId" />
        </div>
        <div>
          <label>quantity</label>
          <input type="number" name="quantity" />
        </div>
        <button type="submit">pay now</button>
      </form>
    </main>
  </body>
</html>

router

buyCheckoutSubscriptionメソッド用のルートを追加

import express from "express";
const router = express.Router();
import { ProductController } from "../controller/ProductController";

const productContext = new ProductController();
router.post(
  "/payment/create-checkout-session",
  productContext.buyCheckoutPayment
);

router.post(
  "/subscription/create-checkout-session",
  productContext.buyCheckoutSubscription
);

module.exports = router;

buyCheckoutSubscriptionメソッド

buyCheckoutSubscriptionメソッドにsubscription用のロジックを実装します。
基本的な実装はpaymentと同じですが、modeがsubscriptionに変更されていることに注目してください。modeに取る値の違いで、支払方法を切り替えています。
また、stripe dashboardで商品にpaymentの設定をしたにも関わらず、subscriptionのmodeを設定した場合はエラーが発生します。(逆も同じです)

import { Request, Response } from "express";
import { stripeClient } from "../stripeClient";
import dotenv from "dotenv";
dotenv.config();

export class ProductController {
  async buyCheckoutPayment(req: Request, res: Response): Promise<void> {
    const { priceId, quantity } = req.body;
    const base_url = process.env.BASE_URL;

    try {
      const session = await stripeClient.checkout.sessions.create({
        payment_method_types: ["card", "konbini"],
        line_items: [
          {
            price: priceId,
            quantity: quantity,
          },
        ],
        mode: "payment",
        success_url: `${base_url}/success.html`,
        cancel_url: `${base_url}/cancel.html`,
      });

      res.redirect(303, session.url);
    } catch (error: any) {
      res.status(401).json({
        message: error.message,
      });
    }

  async buyCheckoutSubscription(req: Request, res: Response): Promise<void> {
    const { priceId, quantity } = req.body;
    const base_url = process.env.BASE_URL;

    try {
      const session = await stripeClient.checkout.sessions.create({
        payment_method_types: ["card"],
        line_items: [
          {
            price: priceId,
            quantity: quantity,
          },
        ],
        mode: "subscription",
        success_url: `${base_url}/success.html`,
        cancel_url: `${base_url}/cancel.html`,
      });
      res.redirect(303, session.url);
    } catch (error: any) {
      res.status(401).json({
        message: error.message,
      });
    }
  }
}

subscriptionのcheckout.htmlにデータを入れて送信します。
情報が正しければ、stripe checkoutのsubscription用UIが表示されます。
先述したように、 subscriptionの場合はカード決済のみです。

paymentの時とはstripe checkoutのUIが違うことがわかります。
「1ヶ月につき」の表記があり、サブスク用の説明書きがされています。
サブスクの課金形態は1週間、一ヶ月等幅広く用意されており、dashboardの商品設定から指定できます。

情報を入力して支払いボタンをクリックするとsuccess_urlの設定上、paymentと同じくsuccess.htmlに遷移します。

ここまでが、subscriptionでの決済フロー解説になります。

終わりに

stripeからはcheckoutの他にも、独自の決済ページを利用するstripe element
カードやコンビニ払いの他に銀行振り込みでの支払いなど様々な決済機能が提供されており、用途によってカスタマイズが可能です。
公式のカスタマーサポートに問い合わせると丁寧に答えてくれるのも幸せです。

今回は以上になります。
ここまで読んでいただき、ありがとうございました。