ノーコードで商品の注文からオンライン決済まで実装した話

昨今は様々な自動化ツールの登場で、ノーコードでも業務アプリを簡単に実装できる時代になりました。

今回は自動化ツール「Make」と弊社SonicGardenのサービス「じぶんシリーズ」を活用し、ノーコードで商品の注文から決済まで完了できる仕組みをご紹介します。

本記事は「Make」による各サービスの連携部分がメインとなりますので、その他のサービスについてはざっくりレベルでの説明となります。

## 実装するサービスの概要
本記事では以下のようなセミナー申し込みアプリを想定し、セミナーの申し込みから決済までノーコードで実装します。

- それぞれのセミナーにはセミナー名と開催日時、参加費が設定されている
- ユーザーは申し込み画面からセミナーに申し込みができる
    - ユーザーは1つのセミナーに複数人数分の申し込みが可能
    - ユーザーは複数セミナーへの同時申し込みも可能
- ユーザーの申し込み後、stripeの決済URLがユーザーに案内される
- ユーザーはそのURLから決済画面にアクセスし、セミナー料の支払いができる
- システム管理者はユーザーが支払いを完了したかどうかを確認できる

## 利用サービス

連携させるサービスは以下の通りです。

### kintone
- サイボウズ社が提供するクラウドベースの業務アプリ作成プラットフォーム
- 売上情報、顧客情報、請求書など、様々なデータ管理アプリをノーコードで作成可能

https://kintone.cybozu.co.jp/

kintoneはレコード登録や更新といったイベント時にwebhookで外部サービスと連携することができます。
今回はその機能を利用してstripeと連携させていきます。

### じぶんシリーズ
- kintoneを拡張してより便利に活用できる連携サービス
    - じぶんページ、じぶんフォーム、じぶんレコードの3つの製品が存在
    - 今回はその中で「**じぶんフォーム**」「**じぶんレコード**」を使用する
- じぶんフォーム
    - kintoneと連携したWebフォームを作成できるサービス
- じぶんレコード
    - kintoneのレコードを閲覧できるページを作成できるサービス

https://www.jibun-apps.jp/

「じぶんフォーム」「じぶんレコード」を活用することで、kintoneにアクセス権限がないユーザーでもkintoneへのデータの登録、登録された情報の閲覧が可能となります。
この特徴を活かし、セミナーへの申し込みフォーム、申し込み情報の閲覧ページをノーコードで作成します。

### stripe

オンライン決済を簡単に導入できるプラットフォームです。
今回はCheckoutのSession作成をノーコードで実装していきます。

https://stripe.com/jp

### Make
- 様々なモジュールを組み合わせて複雑な自動化シナリオを作成可能なツール
- 特定のイベントをトリガーとして、一連のアクションを自動で実行可能

https://www.make.com

サービスの画面は以下のようになっており、モジュールを組み合わせることで様々なタスクを自動化することができます。

![スクリーンショット 2024-12-31 12.01.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/8ccc7f83-db2b-8fea-3e98-cc0caf3bf013.png)

本記事ではMakeを使って「決済画面URLの作成」や、「ユーザーの決済完了の検知」をノーコードで実装していきます。
後ほど詳しくご説明します。

## 事前準備

事前準備としてstripeでの商品の作成と、kintoneでアプリの作成を行います。

### stripeで商品を作成する
以下のように、各セミナーをstripeの商品として登録します。

![スクリーンショット 2024-12-25 21.02.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/f18c8de6-b852-94e5-6705-3a6367ebc62e.png)

stripeに登録すると、**価格ID**が作成されるため、それをコピーしておきます。

### kintoneで必要なアプリを作成する

#### セミナーアプリの作成
kintoneでセミナーアプリを作成し、必要な情報を登録します。

![スクリーンショット 2024-12-25 21.09.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/cc6b37b3-9361-affd-e6d4-40bde96ac013.png)

今回は「セミナー名」「日時」「価格」「stripe_price_id」のフィールドを用意します。
stripeからコピーした「価格ID」は「stripe_price_id」に入力します。


#### 申込者アプリの作成

セミナー申込者を登録するアプリを作成します。以下のようなイメージです。

![スクリーンショット 2024-12-25 21.28.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/be533af9-bb9b-f8dc-4a3e-3e1d0ddb6cc5.png)

「氏名」「メールアドレス」「決済URL」「希望セミナー」「決済状況」のフィールドを作成しています。
「決済URL」にはstripeの決済画面のURLが入力されますが、最初は空の状態です。後々Makeによってstripeの決済URLを発行し、kintoneに登録する仕組みを実装します。
「希望セミナー」は先に登録したセミナーアプリからルックアップで選択します。
「単価」と「stripe_price_id」もセミナーアプリから参照します。希望セミナーごとに任意の「申込数」を入力できるようにしています。
「決済状況」は申込者が決済画面URLから決済後、自動で決済済みになるイメージです。

以上で事前準備は完了です。

## じぶんフォームでセミナーの申し込み画面を作成する

まずユーザーがセミナーに申し込む画面を、じぶんフォームを利用しノーコードで作成します。
事前準備で作成したkintoneの申込者アプリと連携することで、以下の画面のようにkintone側で設定したフィールドが表示されます。

![ダウンロード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/3edd0277-790d-a07c-fb4f-b6ffdd41517a.png)

各フィールドはドラッグ&ドロップで並べ替えができ、必須項目や非表示にする項目の設定もできます。
設定が完了すると、以下のような申し込み画面が作成できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/1f66c5a0-e672-7650-a372-9c470eb093a1.png)

kintoneのテーブルにも対応しており、テーブルの行追加・削除も可能です。
同じようなことができるWebフォームって意外とないんですよね。

これでユーザーに案内できる申し込みフォームを作成できました。

## じぶんレコードで申込者用画面を作成する

次にじぶんレコードで申し込み者用画面を作成します。
じぶんフォームと同じように、ドラッグ＆ドロップで簡単に作成できます（作成画面は省略します）。
作成した画面が以下のようになります。

![スクリーンショット 2024-12-29 14.02.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/23609947-83f9-07e4-22dd-a76c200b4852.png)

じぶんレコードはkintoneとwebhookで連携させることで、上記画面のURLをユーザーに自動でメール送信することができます。
例えば、ユーザーがセミナーに申し込んだタイミングで申し込み内容の確認画面のURLをメールで案内するといった使い方ができます。

連携は簡単で、以下の設定画面でWebhook URLを発行し、kintoneのwebhook設定画面で発行したURLを登録するだけです。

![ダウンロード (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/d171dc9a-1177-f0ce-c6cd-3db73b5398c3.png)

ついでに設定画面でメールの内容も変えておきます。

![スクリーンショット 2024-12-30 13.11.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/9639ede5-f750-38fa-0860-ec5477694de1.png)

上記設定をすることで、以下のようにセミナー申し込み受付時にユーザーへメールを送信することが可能です。

![スクリーンショット 2024-12-31 11.56.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/6b07f895-8ee5-5567-f54a-bb8f8abef5bb.png)

以上でユーザーが申し込み内容を確認する画面の実装は完了です

## Makeでセミナー申込者の決済画面URLを作成する

セミナー申し込み画面、申し込み内容確認画面の実装が完了したので、次にセミナーの代金を支払うための決済画面のURLを作成していきます。

念のため整理しておくと、申し込みのフローは次のようになっています。

1. ユーザーはじぶんフォームからセミナーへの申し込みを行う
1. じぶんフォームから送信された申し込み内容がkintoneへ登録される
1. **Makeによってstripeの決済URLを発行し、申し込みレコードに上書き登録する**
1. じぶんレコードのURL（申し込み内容確認画面）がユーザーに送信される

これまでで1, 2, 4は実装できているため、このステップでは**手順3**の部分の実装を行います。

Makeによって決済画面のURLを作成し、そのURLを申込者情報に追記することで、ユーザーは申し込み内容確認画面から決済画面のURLを確認できるという手筈です。

### 申し込みを検知できるようにする

まずMakeでkintoneに登録された申し込みレコードを検知させるため、Webhookの受信ポイントを作成します。
モジュールのリストから、`Custom webhook`を選択します。

![スクリーンショット 2024-12-30 13.59.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/cdf3b197-40ec-99bb-bba4-1760413df496.png)

`Add`ボタンをクリックすると、webhookの作成ウィンドウが開くため、webhookの名前と必要があればIP制限を設定します。

![スクリーンショット 2024-12-30 14.00.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/85e4134d-cf18-2d58-4b32-101c79c4baea.png)

ちなみにcybozu.comのアウトバウンドIPアドレスは`103.79.14.0/24`なので、実際に運用する際は以下のように設定することを推奨します。

![スクリーンショット 2024-12-30 14.10.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/821057e3-7f4a-a806-14f5-7195b9efb1ee.png)

https://jp.cybozu.help/general/ja/id/020259.html#admin_outbound_ipaddress_1020

設定すると以下のようにWebhook URLが発行されます。

![スクリーンショット 2024-12-30 14.01.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/7edbefa9-e87f-5420-39ac-530b88666b74.png)

それでは、kintoneと通信できるかテストしてみます。
Webhookモジュールが以下の画像の状態でkintone側にレコードを登録してみてください。
成功するとモジュールが受け取ったデータを解析し、後のモジュールでデータを扱うことができるようになります。

![スクリーンショット 2024-12-30 14.16.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/126e9a73-a078-91ad-6ae3-bf61068257a6.png)

じぶんフォームから以下のようなデータを送信してみましょう。

![スクリーンショット 2024-12-30 14.06.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/80889df6-946e-4219-7209-b269d9b29156.png)

受信に成功すると以下の状態になります。

![スクリーンショット 2024-12-30 14.17.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/5523726e-a61a-a2dd-c259-5f01ca2d38a2.png)

以下のようにkintoneのresponseが解析された状態になります。
これらのデータから必要なデータを抽出し、stripeのリクエスト作成に使用していきます。

![スクリーンショット 2024-12-30 14.17.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/9d49b636-e136-9669-8ae4-f33d6ab4b5c6.png)

### stripeの決済URLを発行する

決済URLの発行は以下の2つの手順に分かれます。

1. stripeへのリクエストコンテントの作成
1. stripeへのリクエストの作成

それぞれ説明していきます。

#### stripeへのリクエストコンテントの作成

リクエストコンテントを作成するにはまず、kintoneのレスポンスから必要なデータを抽出する必要があります。
そのためにまず、**Iteratorモジュール**を使用します。

![スクリーンショット 2024-12-30 14.16.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/60632fe8-eb7b-d506-e128-2237be6472aa.png)

**Iteratorモジュール**は配列を渡すことで、次に接続するモジュールで繰り返し処理を行うことができます。
今回のケースでは希望セミナーのリストを渡します。

![スクリーンショット 2024-12-30 16.44.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/6fbc520c-9b58-d9b6-8c7c-2577ea66e01f.png)

これにより次のモジュールで、希望セミナーごとに繰り返し処理を行い、stripeへのリクエストコンテントを作成することができます。

次のモジュールには**Text aggregator**を使用します。
このモジュールは複数の文字列データを1つの文字列にまとめることができます。

![スクリーンショット 2024-12-30 14.18.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/4af34db1-df36-b23d-1e7a-b82e59780407.png)

以下のように、**Source Module**には直前の**Iterator**を指定します。
**Text**は以下のように設定します。

![スクリーンショット 2024-12-30 16.57.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/b18f557b-d0a2-8911-86f0-376b467d37e4.png)

stripeのリクエストのうち、line_itemsの部分を作っている感じです（ここは微妙にコードを書いていると言えなくもない・・・）。

https://docs.stripe.com/api/checkout/sessions/create#create_checkout_session-line_items

以上でstripeのリクエストコンテントの作成は完了です。


#### stripeへのリクエストの作成

次にstripeへのリクエストモジュールを作成していきます。

Makeにはstripe用のモジュールが用意されているのですが、stripe CheckoutのSession用モジュールは用意されていないので、HTTPリクエストモジュールを使用して自作していきます。

![スクリーンショット 2024-12-30 17.11.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/342b7ce8-e4d4-4b14-4825-3e83ddaefab9.png)

HTTPモジュールの設定内容は以下のようになります。

![スクリーンショット 2024-12-31 12.33.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/6c8eac1d-af3d-6570-56a6-6aa90b004c0b.png)

設定内容は以下のとおりです。

- Credentials
    - StripeのAPI tokenを設定する（後述）
- URL
    - https://api.stripe.com/v1/checkout/sessions
- Method
    - Post
- Body type
    - Raw
- Content type
    - Custom
- Value
    - application/x-www-form-urlencoded
    - stripe v1 APIでは フォームのエンコード方式`application/x-www-form-urlencoded`を使用する必要があります。
        - https://docs.stripe.com/api-v2-overview?locale=ja-JP#key-differences-between-the-v1-and-v2-namespace
- Request content
    - mode: payment
    - success_url: テスト環境なので適当なURLを設定
    - `7. text`の部分は、**Text aggregator**のアウトプットを指定する
    - metadata
        - kintoneの申込者アプリのIDと、申し込みレコードのIDを渡す
            - ユーザーが支払いを完了したことを検知する仕組みで利用

**Credentials**はStripeのシークレットキーをコピーして設定します。今回はテスト環境なのでシークレットキーを使用します。

![スクリーンショット 2024-12-30 15.26.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/e1e45849-55f4-f5f9-9615-11f09195e5d6.png)

**Credentials**の設定内容は次の通りです。

![スクリーンショット 2024-12-30 15.34.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/1154a0d6-3c28-5bef-44f5-793f5a15f30d.png)

**Key**の部分は`Bearer sk_test_xxxxx`のように`Bearer `を先頭に追加する必要があるので、ご注意ください。

以上でstripeへのリクエストモジュールの設定は完了です。
実際にkintoneにレコードを登録して、決済URLが発行されるかテストしてみましょう。
以下のようにstripeからレスポンスが帰ってくれば成功です。

![スクリーンショット 2024-12-30 15.51.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/5d516a4f-db1a-919a-be4b-8331dd6eefa0.png)

レスポンス内に決済URLが含まれているのでそのURLアクセスすれば、以下のようにstripeの決済ページにアクセスできるはずです！

![スクリーンショット 2024-12-30 15.54.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/28f1a650-8863-d03f-a7ea-dc4e4b28ac31.png)

以上でstripeの決済URLの発行は完了です。

### stripe決済URLを申込者レコードに登録する

最後に、発行した決済URLをkintoneの申込者レコードに登録します。
まず、stripeのJSONレスポンスを解析して、kintoneへのリクエストに使える形にします。
**Parse JSONモジュール**を使用します。

![スクリーンショット 2024-12-30 17.43.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/958fd6bc-b5d7-ace2-802e-688e894d9486.png)

**Parse JSONモジュール**で、解析させるJSONの構造を入力していきます。今回必要なのはURLだけなので、以下のように`Name: url, Type: Text`で作成します（idも追加していますが、今回は不要です）。

![スクリーンショット 2024-12-30 15.57.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/b8b02611-26ea-7784-4af2-a637f2fbdf7c.png)

解析させるJSONは直前のモジュールの`Data`に入っているので、**JSON string**には`Data`を指定します。

![スクリーンショット 2024-12-30 17.49.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/8606a4b1-32a0-1af2-b478-3a265644f749.png)

以上で決済URLの抽出はできたので、kintoneに登録します。
kintoneへの登録にはKintoneの**Make an API Callモジュール**を使用します。

![スクリーンショット 2024-12-30 22.56.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/14be827d-e932-9dfb-0560-4b34ef098c9e.png)

モジュールの設定内容は以下の画像のようになります（**Connection**の設定は今回は省略します）。

![https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_325543_5e24ded7-604f-ae62-0964-4dcdac8546b0 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/da39dc37-2aef-13c6-bf59-cade8d343fd9.png)

**Body**の内容は公式のAPIリファレンスを参考に作成します。必要な情報は今までのモジュールで揃っているので、必要なものを必要なところに当てはめるだけで作成できます。簡単ですね。

https://cybozu.dev/ja/kintone/docs/rest-api/records/update-record/#request

作成できたら実際に動かしてみましょう。
正しく設定できていれば、以下のようにkintoneモジュールがステータスコード200を返してくれるはずです。

![スクリーンショット 2024-12-30 16.03.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/a0d2a1d7-3f03-2efc-6228-60e956a60144.png)

また、kintoneの決済URLフィールドにもURLが登録されていることが確認できるはずです。

![スクリーンショット 2024-12-30 16.04.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/6f3520fd-0251-5671-69fb-42cfb01aae86.png)

以上で、stripeの決済URLの発行は完了です。


## 管理者がユーザーの支払いを確認できるようにする

これまでで「ユーザーがセミナーに申し込む画面」「申し込み内容を確認する画面」「セミナーの代金を支払う画面」の3つをノーコードで実装することができました。

最後に、ユーザーがセミナーの代金を支払ったことを管理者が確認できる仕組みを実装していきます。
ここでもMakeを活用していきます。

### stripeの支払い完了イベントをMakeで検知できるようにする

新しくMakeのシナリオを作成し、Stripeの**Watch Eventsモジュール**を追加します。

![スクリーンショット 2024-12-31 13.34.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/d081ed79-a098-f07c-ddf3-c6427c886150.png)

「Create a webhook」をクリックし、以下のように設定します。

![スクリーンショット 2024-12-31 13.48.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/36b68788-4ec6-944f-c9e4-897bc8fbc18d.png)

**Connection**は、stripe側でAPI KEYを作成し、その値を入力してください。
**Group**は「Checkout」を選択します。そうすると**Events**が2件に絞り込まれるので、両方にチェックを入れます（厳密には、今回はクレジット決済しか対応していないので、「Checkout Session completed」だけでOKです）

設定が完了したら保存します。そうするとstripeg側の管理画面で、以下のようにWebhookのエンドポイントが追加されているはずです。

![スクリーンショット 2024-12-31 13.56.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/e7a3afbe-3042-f1e8-3007-d50e5ff08b70.png)

ここまで確認できたら、実際に支払い完了のイベントが検知できるかテストしてみましょう。
kintoneで申し込みを1件作成し、発行された支払い画面からstripeのテスト用カードで決済をしてみてください。
うまくいけば以下のように、「checkout.session.completed」イベントを検知できるはずです。

![スクリーンショット 2024-12-31 14.37.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/ab4d5168-e819-7faa-4659-13f1aeda38ef.png)

以上でstripeの支払い完了イベントの検知は完了です。

### 決済完了をkintoneのレコードに反映する

stripeの決済完了を検知できるようになったため、あとはkintone側のレコードの反映するようにします。
再びkintoneの**Make an API Callモジュール**を使用します。

![スクリーンショット 2024-12-31 14.42.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/4599c746-ef3c-808a-242a-4b7077277859.png)

設定内容は以下の通りです。

![スクリーンショット 2024-12-31 14.45.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/9d915a71-4bcd-8012-c7c1-78a8dc3f2c23.png)

stripeのSession作成時、metadataに`app_id`と`record_id`をセットしましたね。
決済完了イベントのレスポンスにその情報が含まれるため、それを利用して更新対象を特定しています。
これで決済が完了したことをkintone側へ連携できるようになりました。

実際に動かして確認してみましょう。
以下のように`Status code: 200`のレスポンスが確認できれば成功です！

![スクリーンショット 2024-12-31 14.45.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325543/e7b586d7-d761-92aa-3a4e-373533841d9d.png)

## まとめ
だいぶ長くなってしまいましたが、以上で「ユーザーがセミナーに申し込む画面」「申し込み内容を確認する画面」「セミナーの代金を支払う画面」「管理者が決済完了を確認する仕組み」の4つをノーコードで実装することができました。

実際に使用するには在庫など他にも考慮が必要なこともありますが、要となる決済周りに関してはコードを書くことなく実装できてしまいました。
便利な時代になりましたね。

今回の記事では省略しましたが、決済URL発行シナリオの途中でstripeのカスタマー作成を入れると、stripeの銀行決済にも対応させることができます。
またそのうち、そちらも別記事として書いてみようかと思います。

それではお読みいただきありがとうございました。