extension-stripe について
extension-stripe は、Caffeine AI アプリに Stripe 決済を追加するスキルです。バックエンドの設定やチェックアウトセッション、決済状態の操作に加え、フロントエンドの決済フローと結果ページの実装を扱います。
主な機能
- 01Stripe の設定
- 02チェックアウトセッションの作成
- 03決済状態の確認
- 04HTTP outcall のレスポンス変換
- 05フロントエンドの決済と結果ページ
活用シーン
- 01アプリで Stripe を設定
- 02商品用のチェックアウトセッションを作成
- 03セッションの決済状態を確認
- 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 決済の統合:
使用方法:
-
PaymentSetup コンポーネントを実装します。
isStripeConfigured()とsetStripeConfiguration()を使用します。- Stripe 決済が設定済みかどうかを確認します。
- 未設定の場合は管理パネルを開き、
StripeConfigurationを使用して Stripe を初期化するようユーザーに求めます。- Stripe のシークレットキー
- 許可する国のリスト。表記は ["US", "CA", "GB"] などです。Stripe のドキュメントを参照してください。
- 決済がすでに設定されている場合は、決済設定を表示しないでください。
-
チェックアウトフックを実装します。
- バックエンドの
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; } }); } - バックエンドの
-
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; -
決済の成功または失敗をそれぞれ処理する PaymentSuccess コンポーネントと PaymentFailure コンポーネントを実装します。
-
決済ステータス用のコンポーネントに、以下の2つのパスを割り当てます。
- パス「/payment-success」を PaymentSuccess に割り当てます。
- パス「/payment-failure」を PaymentFailure に割り当てます。 @tanstack router を使用してください。
-
管理画面には Stripe を設定するメニューがあります。未設定の場合は、ログイン時に管理者へ Stripe を設定するよう求めます。
補足:商品画像が商品キャンバス内に適切に表示され、サイズ変更されることを確認してください。