【Bubble】日本独自の決済「Pay.jp」で定期課金を実装する全手法 —— トークン化からAPI連携

国内向けSaaSやサブスクリプションサービスをBubbleで開発する際、避けて通れないのが「決済機能」の実装です。 StripeはBubble標準プラグインが充実していますが、日本の商習慣や手数料の観点からPay.jpを選定したいという相談をよくいただきます。(特に占いなどStripe側で申請できないものをPay.jpで通すケースがあります)

しかし、Pay.jpには公式のBubbleプラグインが存在しない(2025/12/15現在)ため、API ConnectorJavaScriptを駆使して自前で実装する必要があります。

「セキュアかつ確実に動作するPay.jp定期課金の実装パターン」を公開します。
特に、多くの開発者がハマりやすい「JavaScriptの読み込みタイミング(Race Condition)」や「Toolboxプラグインの設定漏れ」についても、具体的な解決策を提示します。


全体のアーキテクチャ

  1. フロントエンド (JS):Pay.jpのフォームを表示し、ユーザーが入力したカード情報をトークン(暗号化された文字列)に変換する。
  2. つなぎ込み (Toolbox):発行されたトークンをBubbleのワークフローに受け渡す。
  3. バックエンド (API):BubbleからPay.jpのAPIを叩き、「顧客作成」→「定期課金登録」を実行する。

ステップ0:Pay.jp側での事前準備

Bubbleでの実装に入る前に、Pay.jpの管理画面で以下の2つを準備しておきます。

1. APIキーの確認

管理画面の「API設定」から、以下の2つのキーを取得します。

  • 公開鍵 (pk_test_…): フロントエンド(JavaScript)で使用します。
  • 秘密鍵 (sk_test_…): バックエンド(Bubble API Connector)で使用します。

2. 定期課金プランの作成

「プラン」メニューから、定期課金のプランを作成します。

  • プランID: 自動生成されるID(例: pln_...)を控えておきます。後ほどBubbleのAPIコールで使用します。
  • 金額・周期: サービスの仕様に合わせて設定(例: 月額980円など)。


ステップ1:API Connectorの設定

Pay.jpの定期課金APIは、セキュリティの仕様上「カード情報」を直接受け取れません。まずは「顧客(Customer)」を作成し、そこにカードを紐付ける必要があります。

BubbleのAPI Connectorで、以下の2つのエンドポイントを設定します。

1. Create Subscription

  • URL: https://api.pay.jp/v1/subscriptions
  • Parameters:
    • plan: (プランID。例:pln_...
    • customer: (Create Customerのレスポンスに含まれる顧客ID cus_... を指定)

2.Create Custmer (テストだけなら、不要)

APIコールの追加
 Pay.jp側へ顧客を新規作成しないとプランに紐づけができないので作成します。

URL: https://api.pay.jp/v1/customers

Name: Create Customer

Use as: Action

Data type: JSON

Method: POST

Headers: (既存の設定と同じ認証ヘッダーを使用)

Parameters:

  • Key: card
  • Value: (空欄またはテスト用トークン)
  • Private: チェックを外す (重要:ここにJSから受け取ったトークンが入ります)
  • Allow blank: チェックを入れておく(初期化用)

⚠️ 技術TIPS
Subscription作成時に直接 card パラメータを送ると Invalid param key エラーが発生します。必ず「Customer」を経由させてください。


ステップ2:デザインとHTMLエレメント

画面上に、Pay.jpが提供するクレジットカード入力フォーム(Elements)を表示するための「描画エリア」を用意します。

BubbleのデザインエディタでHTMLエレメントを配置し、以下のコードを記述します。

HTML

<div id="v2-demo" class="payjs-outer"></div>
<style>
  .payjs-outer {
    border: 1px solid #ccc;
    padding: 10px;
    background-color: white;
    border-radius: 4px;
  }
</style>

🎨 デザインのコツ HTMLエレメントの幅(Width)は、最低でも500px程度確保してください。幅が狭すぎると、カード番号・有効期限・CVCの入力欄が重なって表示されてしまいます。


ステップ3:JavaScriptによる初期化(Race Condition対策)

ここが実装の最大の難所です。 Bubbleのページ読み込み(Page is loaded)は非常に高速なため、Pay.jpのライブラリ読み込みが完了する前に初期化コードが走ってしまい、エラーになるケースが多発します。

これを防ぐため、「ライブラリと描画エリアの準備ができるまで待機する」ロジックを組み込みます。 Toolboxプラグインの「Run javascript」アクションに、以下のコードを使用してください。

JavaScript

// 1. Pay.jpライブラリが未ロードなら強制的に読み込む
if (typeof Payjp === 'undefined') {
    var script = document.createElement('script');
    script.src = 'https://js.pay.jp/v2/pay.js';
    document.head.appendChild(script);
}

// 2. 準備完了を監視する(0.5秒間隔)
var checkReady = setInterval(function() {
    // ライブラリとHTML枠(#v2-demo)の両方が存在するかチェック
    var isPayjpLoaded = (typeof Payjp !== 'undefined');
    // ID指定のクォーテーションを忘れずに!
    var targetElement = document.getElementById('v2-demo');

    if (isPayjpLoaded && targetElement) {
        clearInterval(checkReady); // 監視終了

        try {
            // 公開鍵の設定(pk_test_...)
            // ※本番環境とテスト環境でキーを出し分ける設定推奨
            var payjp = Payjp('pk_test_あなたの公開鍵'); 
            
            var elements = payjp.elements();
            var cardElement = elements.create('card');
            
            // フォームを表示
            cardElement.mount('#v2-demo'); 

            // ボタンクリック時に参照できるようwindowオブジェクトに保存
            window.payjpInstance = payjp;
            window.cardElementInstance = cardElement;
            
            console.log("Pay.jp初期化完了");
            
        } catch (e) {
            console.error("Pay.jp設定エラー: " + e.message);
        }
    }
}, 500);

ステップ4:【最重要】Javascript to Bubble の設定

JavaScriptからBubbleへトークンを渡すためのブリッジとなるのが、ToolboxプラグインのJavascript to Bubbleエレメントです。

この設定に不備があると、「ボタンを押しても何も起きない」という事態に陥ります。必ず以下の設定を確認してください。

✅ 必須チェック項目

Appearanceタブ内の設定で、以下2箇所にチェックを入れます。

  1. Trigger event: ✅ ON
    • これがないと、トークンを受け取った瞬間のワークフロー(Event)が発動しません。
  2. Publish value: ✅ ON
    • これがないと、エレメントがトークンの値(tok_...)を保持できず、後続のAPIコールで空データを送ることになります。
  3. bubble_fn_suffix: ww 
     (重要)JavaScriptコード内の bubble_fn_ww(...) と合わせる必要があります。
  4. Type of data: text
    トークンは文字列なので text にします。ここが空欄だと選択できません。

ステップ5:購入ボタンとワークフロー連携

最後に、「購入する」ボタンをクリックした際のアクションを実装します。

1. トークン化の実行(Run javascript)

ボタンクリック時のワークフローで以下を実行します。

JavaScript

window.payjpInstance.createToken(window.cardElementInstance).then(function(r) {
    if (r.error) {
        // カード情報の不備などのエラー処理
        alert(r.error.message);
    } else {
        // 成功時、Bubbleにトークンを渡す(suffixが 'ww' の場合)
        bubble_fn_ww(r.id); 
    }
});

2. 課金処理の実行

Javascript to Bubble event トリガーを使用し、トークンが渡ってきたタイミングでAPIを実行します。

まずは、page is load でjavascriptを呼び出し

  1. PayJP – Create Customer:
    • card: Javascript to Bubble's value (トークン)を指定。

      ここから、BubbleのWorkflowでAPIをPostし
      *テストで通信したい場合には、固定のidを入れてもOK
  2. PayJP – Create Subscription:
    • customer: Step 1の結果(ID) を指定。
    • plan: 事前に作成したプランIDを指定。
  3. 完了通知:
    • ユーザーに購入完了メッセージを表示、またはサンクスページへ遷移。

まとめ

Pay.jpの実装は、APIとJavaScriptを組み合わせることで、Bubble標準機能だけでは実現できない柔軟な決済フローを構築可能です。

  • 事前準備: 正しいキーの取得とプラン作成。
  • APIの役割分担: 顧客作成を経て課金登録へ。
  • 非同期処理の制御: JS読み込み待ちの実装。
  • プラグインの正確な設定: Toolboxのオプション確認。

この4点を押さえれば、安定した決済システムを実装できます。 AppTalentHubでは、こうした外部API連携を駆使した高度なBubble開発を支援しています。実装にお困りの際は、ぜひご相談ください。

この記事を書いた人

宮崎翼

愛媛県出身・東京都在住。
国立工業高専(新居浜工業高等専門学校)卒業後、外資系ソフトウェア企業などで法人営業・IT導入支援に従事し、BtoB領域で多様な新規開拓やエンタープライズのDX推進を経験。

現在は「AppTalentHub」の理念、ノーコード/ローコードを活用したアプリ開発の標準化と、エンジニアのスキルの可視化による適正評価を実現するためのプロジェクトやコミュニティ運営に取り組んでいます。
https://tsubasa.tech/about