LoginSignup
5
3

kintoneプラグインにて、kintoneアプリにStripeの決済フォームを表示する方法 with kintone-ui-component

Posted at

この記事は、kintoneアドベントカレンダー3日目の記事です。

kintoneでは、JavaScriptを利用してアプリの表示や動作を拡張・カスタマイズすることができる、拡張機能機能が用意されています。

拡張機能は、kintoneのスタンダードコースから利用できますが、動作確認や拡張機能の開発などの目的などであれば、開発者ライセンスを利用して無料で試すこともできます。

kintoneアプリに、決済フォームを直接組み込む

kintoneはJavaScriptでアプリの機能や表示をカスタマイズできます。そしてStripeでは、JavaScriptで決済フォームを表示するSDK(@stripe/stripe-js)を提供しています。

ということは、実装方法さえわかれば、プラグインを利用して、kintoneアプリ内にStripeが用意する決済フォームを埋め込むことも可能です!

スクリーンショット 2023-11-22 13.23.45.png

今回の記事では、kintoneプラグインで、Stripeの決済フォームを表示する方法を紹介します。

kintoneプラグインで、Stripeの決済フォームを表示・動作させる4STEP

プラグインにStripeを組み込むには、次の4ステップが必要です。

まず、「決済フォームを表示させる場所」をアプリ内に用意しましょう。ここではkintone UI Componentライブラリを利用して、ダイヤログを表示させます。

続いてStripe APIをよびだし、決済処理に必要なリソース(Payment Intent)を生成します。

3つ目のステップで、Stripe ElementsをStripeのJavaScript SDKで表示させます。ここではフォームの表示のみですので、実際に決済処理などはまだ行われません。

最後のステップで、生成したPayment IntentとStripe Elementsの情報を利用し、決済を実施します。

それでは、一つずつ見ていきましょう。

Step1: 決済フォームを表示するダイヤログを作成する

まずは決済フォームを表示させる場所の用意です。直接埋め込んでも良いのですが、今回はダイヤログに表示させましょう。

src/js/desktop.js
import { Button, Dialog } from "kintone-ui-component";
const PLUGIN_ID = kintone.$PLUGIN_ID;

kintone.events.on("app.record.index.show", function () {
  const spaceElement = kintone.app.getHeaderSpaceElement();
  if (spaceElement === null) {
    throw new Error("The header element is unavailable on this page");
  }

  const buttonElement = new Button({
    text: "決済フォームを表示",
    type: "submit",
  });
  buttonElement.addEventListener("click", async function () {
    const paymentElementPlaceholder = document.createElement("div");
    paymentElementPlaceholder.id = "payment-element";
    const dialog = new Dialog({
      title: "Payment form",
      content: paymentElementPlaceholder,
    });
    dialog.open();
  });
  spaceElement.appendChild(buttonElement);

});

kintone UI ComponentのButtonで「ダイヤログ表示ボタン」を、Dialogで「ダイヤログそのもの」を実装しました。

コードをデプロイすると、プラグインを登録したアプリ(今回は見積書)の一覧画面に、[決済フォームを表示]ボタンが表示されます。

スクリーンショット 2023-11-22 13.58.32.png

Button要素にaddEventListenerでクリックイベントを設定しています。その中でダイヤログ要素の作成と表示を行っているため、追加されたボタンをクリックすることで、ダイヤログが開きます。

スクリーンショット 2023-11-22 13.58.37.png

Step2: kintone API Proxyを利用して、Stripe APIを呼び出す

決済フォームを表示する場所が用意できましたので、続いて決済処理に必要な情報をStripeで生成しましょう。

Stripeでは、決済を行うためのリソースとして「Payment Intent API」が用意されています。このAPIを利用して、クレジットカード / Apple Pay / Google Payなどのさまざまな支払い手段をサポートした決済フローを実装します。

StripeのシークレットAPIキーを安全に保存する

このAPIを利用するために、Stripeが発行するシークレットAPIキーを利用する必要があります。安全にシークレットAPIキーをkintone内で取り扱うための、「APIプロキシー機能」の設定方法などについては、以下の記事をお読みください。

今回は次のようなAPIキー入力フォームと、保存処理を追加しています。

plugin/html/config.html
<section class="settings">
  <h2 class="settings-heading">Stripe API連携</h2>
  <p class="kintoneplugin-desc">Stripeと連携するためのAPIキーを設定します</p>
  <form class="js-submit-settings" id="js-submit-settings">
    <p class="kintoneplugin-row">
      <label for="message">
        Secret API KEY:
        <input type="text" id="stripe-secret-api-key" class="js-text-message kintoneplugin-input-text">
      </label>
    </p>
    <p class="kintoneplugin-row">
        <button type="button" class="js-cancel-button kintoneplugin-button-dialog-cancel">Cancel</button>
        <button class="kintoneplugin-button-dialog-ok" id="plugin-submit">Save</button>
    </p>
  </form>
</section>
src/js/config.js
const PLUGIN_ID = kintone.$PLUGIN_ID;

const form = document.querySelector(".js-submit-settings");
const cancelButton = document.querySelector(".js-cancel-button");
const messageInput =
  document.querySelector<HTMLInputElement>(".js-text-message");
if (!(form && cancelButton && messageInput)) {
  throw new Error("Required elements do not exist.");
}

form.addEventListener("submit", (e) => {
  e.preventDefault();
  const secretAPIKeyField = document.getElementById("stripe-secret-api-key");
  if (!secretAPIKeyField) return;
  const secretAPIKeyFieldValue = secretAPIKeyField.value;
  if (!secretAPIKeyFieldValue) return;
  const stripeAPIUrl = "https://api.stripe.com/v1";
  const requestHeader = {
    Authorization: `Bearer ${secretAPIKeyFieldValue}`,
  };
  kintone.plugin.app.setProxyConfig(stripeAPIUrl, "GET", requestHeader, {});
  kintone.plugin.app.setProxyConfig(
    stripeAPIUrl,
    "POST",
    {
      ...requestHeader,
      "Content-Type": "application/x-www-form-urlencoded",
    },
    {}
  );
  alert("Saved");
});
cancelButton.addEventListener("click", () => {
  window.location.href = "../../" + kintone.app.getId() + "/plugin/";
});

保存したAPIキーを利用したAPIリクエストを作成する

APIキーを利用できるようになりましたので、支払いを処理するためのデータ(Payment Intent)を作成しましょう。

決済フォームを表示ボタンを作成していますので、このボタンをクリックした際に、StripeのAPIを呼び出してPayment Intentを作成します。

src/js/desktop.js
  const buttonElement = new Button({
    text: "決済フォームを表示",
    type: "submit",
  });
  buttonElement.addEventListener("click", async function () {
+    const paymentIntentRequest = new URLSearchParams("");
+    paymentIntentRequest.set("amount", "1000");
+    paymentIntentRequest.set("currency", "jpy");
+    const paymentIntent = await new Promise((resolve, reject) => {
+      kintone.plugin.app.proxy(
+        kintone.$PLUGIN_ID,
+        "https://api.stripe.com/v1/payment_intents",
+        "POST",
+        {},
+        paymentIntentRequest.toString(),
+        (response) => {
+          const result = JSON.parse(response);
+          resolve(result);
+        },
+        (e) => reject(e)
+      );
+   });
    const paymentElementPlaceholder = document.createElement("div");
    paymentElementPlaceholder.id = "payment-element";
    const dialog = new Dialog({
      title: "Payment form",
      content: paymentElementPlaceholder,
    });
    dialog.open();
  });

StripeのAPIをkintoneのproxyで呼び出す場合、リクエストの本文(body)はURLSearchParamsを利用して設定します。下にリクエスト内容を設定している部分のコードだけピックアップしました。

    const paymentIntentRequest = new URLSearchParams("");
    paymentIntentRequest.set("amount", "1000");
    paymentIntentRequest.set("currency", "jpy");

このコードでは、「価格(amount)が1000」「通貨(currency)がjpy」のPayment Intentを作成しています。

どのようなデータが生成されるかを確認したい場合、Dialog要素のcontentを次のように変更してみましょう。

    const dialog = new Dialog({
      title: "Payment form",
      content: paymentElementPlaceholder,
      content: JSON.stringify(result, null, 2),
    });

kintoneアプリでボタンをクリックすると、次のようなJSONを表示するダイアログが確認できます。

スクリーンショット 2023-11-27 11.45.51.png

Step3: Stripe Elementsで、決済フォームを表示する

ここからは、StripeのJavaScript SDKを利用して決済フォームを埋め込みましょう。

決済フォームを表示するには、StripeのJavaScript SDKを利用します。npmコマンドでライブラリをインストールしましょう。

% npm i @stripe/stripe-js

loadStripe関数を利用しますので、インポートします。

src/js/desktop.js
import { Button, Dialog } from "kintone-ui-component";
+import { loadStripe } from "@stripe/stripe-js";

その後、Payment Intentを作成した処理の後に、インポートしたloadStripeを呼び出しましょう。

src/js/desktop.js
    const paymentIntent = await new Promise((resolve, reject) => {
      kintone.plugin.app.proxy(
        kintone.$PLUGIN_ID,
        "https://api.stripe.com/v1/payment_intents",
        "POST",
        {},
        paymentIntentRequest.toString(),
        (response) => {
          const result = JSON.parse(response);
          resolve(result);
        },
        (e) => reject(e)
      );
   });
+    const stripe = await loadStripe(
+      "pk_test_から始まる、Stripeの公開可能キーを設定する"
+    );
+    if (!stripe) throw new Error("Failed to load Stripe client");
    const paymentElementPlaceholder = document.createElement("div");

JS SDKのセットアップをloadStripeで行いますので、セットアップに失敗した場合(戻り値がnullの場合)用の処理も入れておきましょう。

Stripeの公開可能キーは、プラグインの設定情報に保存しよう
今回は便宜上プラグインコードに直接、Stripeの公開可能キーを記述しました。
しかしkintoneでは、プラグインごとに設定情報を保存する機能が用意されています。
実際の案件やプラグイン開発では、kintone.plugin.app.setConfigを利用しましょう。

https://cybozu.dev/ja/kintone/docs/js-api/plugins/set-config/

セットアップに成功した後は、決済フォームを表示する処理を追加します。

src/js/desktop.js
    if (!stripe) throw new Error("Failed to load Stripe client");

    const paymentElementPlaceholder = document.createElement("div");
    paymentElementPlaceholder.id = "payment-element";
    const dialog = new Dialog({
      title: "Payment form",
      content: paymentElementPlaceholder,
    });
    dialog.open();
+    const elements = stripe.elements({
+      clientSecret: paymentIntent.client_secret,
+      appearance: {
+        theme: "stripe",
+      },
+    });
+    const paymentElement = elements.create("payment", {
+      layout: {
+        type: "accordion",
+        defaultCollapsed: false,
+        radios: false,
+        spacedAccordionItems: true,
+      },
+    });
+    paymentElement.mount("#payment-element");

Stripeでは、「すでにあるHTML要素に、決済フォームをマウントする」形で決済フォームを埋め込みます。そのため、mount()関数はdialog.open()処理を実行してから呼びだすようにしましょう。

変更をデプロイすると、ボタンをクリックした際のダイアログに、決済フォームが表示されます。
スクリーンショット 2023-11-27 12.00.32.png

Step4: 決済を完了させよう

最後に、決済フォームに入力されたカード情報などを利用して、決済を完了させる処理を作りましょう。

まずは、注文操作を行うためのボタンをダイアログに追加します。

src/js/desktop.js
+    const paymentFormButton = new Button({
+      text: "注文する",
+      type: "submit",
+    });
    const paymentElementPlaceholder = document.createElement("div");
    paymentElementPlaceholder.id = "payment-element";
    const dialog = new Dialog({
      title: "Payment form",
      content: paymentElementPlaceholder,
+      footer: paymentFormButton,
    });
    dialog.open();

追加したボタンのクリックイベントを設定しましょう。

src/js/desktop.js
    dialog.open();
    paymentElement.mount("#payment-element");

+    paymentFormButton.addEventListener("click", async () => {
+      const confirmResult = await stripe.confirmPayment({
+        elements,
+        redirect: "if_required",
+      });
+      console.log(confirmResult);
+      alert(`決済を完了しました。決済ID: ${confirmResult?.paymentIntent?.id}`);
+      dialog.close();
+    });

決済フォームの動作を確認するには、ドキュメントに公開されている「テスト用カード番号」を利用します。

テスト用のカード情報をフォームに入力し、注文ボタンをクリックしてみましょう。
処理に成功した場合、決済成功のメッセージが表示され、ダイアログが閉じられます。

スクリーンショット 2023-11-27 12.05.08.png

これでkintoneアプリ内に、決済フォームを埋め込む作業が完了です。

終わりに

kintoneはJavaScriptを利用してアプリをカスタマイズできます。そのため、StripeのJavaScript系SDKと、APIを利用することで、決済や請求業務に関するカスタマイズや自動化も、kintone上で行うことが可能です。

特にkintoneでは、ゲストユーザー向けのスペースを用意することもできます。そのため、ゲストユーザーがアプリ内で注文や決済を行えるようなシステムを作りたい場合、今回のような決済フォームを埋め込む実装が役に立ちます。

そのほかにも、請求書処理や、支払いリンクの作成など、様々な連携サンプルについても紹介しています。
こちらも併せてごらんください。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3