mcpField
E
extension-stripe

extension-stripe

github.com/caffeinelabsその他228.7k 回インストール6 日前に更新
リポジトリ

extension-stripe について

extension-stripe は、Caffeine AI アプリに Stripe 決済を追加するスキルです。バックエンドの設定やチェックアウトセッション、決済状態の操作に加え、フロントエンドの決済フローと結果ページの実装を扱います。

主な機能

  1. 01Stripe の設定
  2. 02チェックアウトセッションの作成
  3. 03決済状態の確認
  4. 04HTTP outcall のレスポンス変換
  5. 05フロントエンドの決済と結果ページ

活用シーン

  1. 01アプリで Stripe を設定
  2. 02商品用のチェックアウトセッションを作成
  3. 03セッションの決済状態を確認
  4. 04決済結果ページへのルーティング

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/caffeinelabs/skills --skill extension-stripe

インストール後、Claude Code で/extension-stripeを入力して読み込まれたか確認します。

同梱ファイルmigration/v0.x.y-to-v1.x.y.md

詳細

Stripe 決済の統合

Caffeine AI 向けの Stripe 決済拡張機能です。

概要

このスキルは、HTTP アウトコールを使用して Stripe 決済をサポートします。MixinStripe ミックスインは、設定、チェックアウトセッションの作成、決済ステータスの確認、HTTP アウトコールの transform コールバックを提供します。フロントエンドでは、チェックアウトフローと決済結果ページを処理します。

受信 Stripe Webhook(外部からカニスターへの POST)は対象外です。http_request/http_request_update ハンドラーには extension-http-endpoints を使用し、更新パスで Stripe の署名を検証してください。

前提条件:この統合は extension-authorization に依存するため、先にそちらに従う必要があります。

バックエンド

モジュール API

あらかじめ構築されたモジュール mo:caffeineai-stripe/stripe.mo は、低レベルの Stripe HTTP ヘルパーを提供します。変更しないでください。

import OutCall "mo:caffeineai-http-outcalls/outcall";

module {
  public type StripeConfiguration = {
    secretKey : Text;
    allowedCountries : [Text];
  };

  public type StripeState = {
    var configuration : ?StripeConfiguration;
  };

  public func initState() : StripeState;

  public type ShoppingItem = {
    currency : Text;
    productName : Text;
    productDescription : Text;
    priceInCents : Nat;
    quantity : Nat;
  };

  public func createCheckoutSession(configuration : StripeConfiguration, caller : Principal, items : [ShoppingItem], successUrl : Text, cancelUrl : Text, transform : OutCall.Transform) : async Text;

  public type StripeSessionStatus = {
    #failed : { error : Text };
    #completed : { response : Text; userPrincipal : ?Text };
  };

  public func getSessionStatus(configuration : StripeConfiguration, sessionId : Text, transform : OutCall.Transform) : async StripeSessionStatus;
};

main.mo でのセットアップ

include MixinStripe(accessControlState, stripeState) は、カスタムのミックスインファイルではなく、main.mo に配置する必要があります。ミックスインは、以下のパブリックエンドポイントを自動的に提供します。

  • isStripeConfigured()
  • setStripeConfiguration(config)
  • createCheckoutSession(items, successUrl, cancelUrl)
  • getStripeSessionStatus(sessionId)
  • transform(input) — HTTP アウトコールのレスポンス変換に必要です

これらの関数を再宣言しないでください。これらは MixinStripe のみが提供します。

import Stripe "mo:caffeineai-stripe/stripe";
import AccessControl "mo:caffeineai-authorization/access-control";
import MixinAuthorization "mo:caffeineai-authorization/MixinAuthorization";
import MixinStripe "mo:caffeineai-stripe/MixinStripe";
import Map "mo:core/Map";
import Iter "mo:core/Iter";
import Text "mo:core/Text";
import Runtime "mo:core/Runtime";

actor {
  // Include authorization
  let accessControlState : AccessControl.AccessControlState;
  include MixinAuthorization(accessControlState, null);
  let stripeState : Stripe.StripeState;
  include MixinStripe(accessControlState, stripeState);

  public type Product = {
    id : Text;
    // add custom fields
  };

  let products : Map.Map<Text, Product>;

  public query func getProducts() : async [Product] {
    products.values().toArray();
  };

  public shared ({ caller }) func addProduct(product : Product) : async () {
    if (not (AccessControl.hasPermission(accessControlState, caller, #admin))) {
      Runtime.trap("Unauthorized: Only admins can add products");
    };
    products.add(product.id, product);
  };

  public shared ({ caller }) func updateProduct(product : Product) : async () {
    if (not (AccessControl.hasPermission(accessControlState, caller, #admin))) {
      Runtime.trap("Unauthorized: Only admins can update products");
    };
    products.add(product.id, product);
  };

  public shared ({ caller }) func deleteProduct(productId : Text) : async () {
    if (not (AccessControl.hasPermission(accessControlState, caller, #admin))) {
      Runtime.trap("Unauthorized: Only admins can delete products");
    };
    products.remove(productId);
  };

  // Add more data and functions as needed
};

マイグレーションチェーンの先頭:

import Map "mo:core/Map";
import AccessControl "mo:caffeineai-authorization/access-control";

module {
  type Product = {
    id : Text;
  };

  type StripeConfiguration = {
    secretKey : Text;
    allowedCountries : [Text];
  };

  type StripeState = {
    var configuration : ?StripeConfiguration;
  };

  type NewActor = {
    accessControlState : AccessControl.AccessControlState;
    products : Map.Map<Text, Product>;
    stripeState : StripeState;
  };

  public func migration(_old : {}) : NewActor {
    {
      accessControlState = AccessControl.initState();
      products = Map.empty<Text, Product>();
      stripeState = { var configuration = null };
    };
  };
};

フロントエンド

Stripe 決済の統合:

使用方法:

  1. PaymentSetup コンポーネントを実装します。

    • isStripeConfigured() と setStripeConfiguration() を使用します。
    • Stripe 決済が設定済みかどうかを確認します。
    • 未設定の場合は管理パネルを開き、StripeConfiguration を使用して Stripe を初期化するようユーザーに求めます。
      • Stripe のシークレットキー
      • 許可する国のリスト。表記は ["US", "CA", "GB"] などです。Stripe のドキュメントを参照してください。
    • 決済がすでに設定されている場合は、決済設定を表示しないでください。
  2. チェックアウトフックを実装します。

    • バックエンドの createCheckoutSession の結果を JSON として解析する必要があります。
    • 解析したセッションに空でない url が含まれていることを検証します。見つからない場合はエラーをスローし、リダイレクトしないでください。
    import { useMutation } from '@tanstack/react-query';
    import { useActor } from '@caffeineai/core-infrastructure';
    import { ShoppingItem } from '../backend';
    
    export type CheckoutSession = {
        id: string;
        url: string;
    };
    
    export function useCreateCheckoutSession() {
        const { actor } = useActor();
    
        return useMutation({
            mutationFn: async (items: ShoppingItem[]): Promise<CheckoutSession> => {
                if (!actor) throw new Error('Actor not available');
                const baseUrl = `${window.location.protocol}//${window.location.host}`;
                const successUrl = `${baseUrl}/payment-success`;
                const cancelUrl = `${baseUrl}/payment-failure`;
                const result = await actor.createCheckoutSession(items, successUrl, cancelUrl);
                // JSON parsing is important!
                const session = JSON.parse(result) as CheckoutSession;
                if (!session?.url) {
                    throw new Error('Stripe session missing url');
                }
                return session;
            }
        });
    }
    
  3. Payment コンポーネントを実装します。以下を含めてください。

    • useCreateCheckoutSession()
    • ShoppingItem[] を入力として渡します。
    • CheckoutSession の結果を分析します。
    • CheckoutSession 内の URL にウェブページをリダイレクトします。これにより、ユーザーは決済を完了できます。
    • Stripe の URL への移動にルーターのナビゲーションを使用しないでください。window.location.href を使用してください。
    • /undefined には絶対に移動しないでください。session.url が見つからない場合は、エラーを表示して処理を中止してください。
    const session = await createCheckoutSession.mutateAsync(shoppingItems);
    if (!session?.url) throw new Error('Stripe session missing url');
    window.location.href = session.url;
    
  4. 決済の成功または失敗をそれぞれ処理する PaymentSuccess コンポーネントと PaymentFailure コンポーネントを実装します。

  5. 決済ステータス用のコンポーネントに、以下の2つのパスを割り当てます。

    • パス「/payment-success」を PaymentSuccess に割り当てます。
    • パス「/payment-failure」を PaymentFailure に割り当てます。 @tanstack router を使用してください。
  6. 管理画面には Stripe を設定するメニューがあります。未設定の場合は、ログイン時に管理者へ Stripe を設定するよう求めます。

補足:商品画像が商品キャンバス内に適切に表示され、サイズ変更されることを確認してください。

コメント