フロー・ペイウォールイベントの処理 - Capacitor
このガイドでは、購入・復元・プロダクト選択・フローのレンダリングに関するイベント処理について説明します。ボタン操作(フローを閉じる、リンクを開く、カスタムアクションなど)のハンドリング設定については、ボタンアクションの処理に関するガイドをご覧ください。
Flow Builder で作成したフローとペイウォールは、購入や復元のために追加コードは不要です。ただし、アプリが対応できるイベントがいくつか生成されます。それらのイベントには、ボタン押下(閉じるボタン、URL、プロダクト選択など)や、フロー上で行われた購入関連アクションの通知が含まれます。これらのイベントへの対応方法については、以下をご覧ください。
アプリ内のフロー画面で発生するプロセスを制御・監視するには、view.setEventHandlers メソッドを実装してください。
イベントごとに設定できるハンドラーは1つだけです。setEventHandlers を複数回呼び出すと、指定したハンドラーが上書きされ、対象イベントのデフォルトおよび以前に設定したハンドラーがいずれも置き換えられます。設定しなかったハンドラーはデフォルトの動作を維持します。setEventHandlers はアンサブスクライブ関数を返し、view.dismiss() はすべてのハンドラーをクリアします。
const view = await createFlowView(flow);
const unsubscribe = await view.setEventHandlers({
onCloseButtonPress() {
return true; // close the flow (default behavior)
},
onAndroidSystemBack() {
return true; // close the flow; by default, it stays open
},
onPurchaseCompleted(purchaseResult, product) {
return purchaseResult.type === 'success'; // close the flow on a successful purchase, keep it open for cancelled or pending purchases
},
onPurchaseStarted(product) { /***/ },
onPurchaseFailed(error, product) { /***/ },
onRestoreCompleted(profile) { /***/ },
onRestoreFailed(error) { /***/ },
onProductSelected(productId) { /***/ },
onError(error) { /***/ },
onLoadingProductsFailed(error) { /***/ },
onUrlPress(url, openIn) {
adapty.openWebUrl({ url, openIn }).catch(console.warn); // same as the SDK default
return false; // keep the flow open
},
onAppeared() { /***/ },
onDisappeared() { /***/ },
onWebPaymentNavigationFinished() { /***/ },
});イベントの例(クリックして展開)
以下の例は、各ハンドラーで利用可能なプロパティを、コメント内にサンプル値とともに示しています。
// onUrlPress
url; // 'https://example.com/terms'
openIn; // 'browser_in_app' or 'browser_out_app'
// onCustomAction
actionId; // 'login'
// onProductSelected
productId; // 'premium_monthly'
// onPurchaseStarted, onPurchaseCompleted, onPurchaseFailed
product.vendorProductId; // 'premium_monthly'
product.localizedTitle; // 'Premium Monthly'
product.localizedDescription; // 'Premium subscription for 1 month'
product.price?.amount; // 9.99
product.price?.currencyCode; // 'USD'
product.price?.localizedString; // '$9.99'
// onPurchaseCompleted
purchaseResult.type; // 'success', 'pending', or 'user_cancelled'
if (purchaseResult.type === 'success') {
purchaseResult.profile.accessLevels['premium']?.isActive; // true
}
// onRestoreCompleted
profile.accessLevels['premium']?.isActive; // true
// onPurchaseFailed, onRestoreFailed, onError, onLoadingProductsFailed
error.message; // 'Purchase failed due to insufficient funds'必要なイベントハンドラーだけを登録し、不要なものは省略できます。この場合、未使用のイベントリスナーは作成されません。必須のイベントハンドラーはありません。
イベントハンドラーはboolean値を返します。trueが返された場合、表示プロセスが完了したとみなされ、フロー画面が閉じられ、そのビューのイベントリスナーが削除されます。
一部のイベントハンドラーにはデフォルトの動作があり、必要に応じてオーバーライドできます:
onCloseButtonPress: 閉じるボタンが押されたときにフローを閉じます。onUrlPress: タップされた URL をadapty.openWebUrl経由でネイティブブラウザで開き、ビルダーで設定された Open in オプションを適用してフローを開いたままにします。onAndroidSystemBack: Back ボタンが押されたときにフローを開いたままにします。閉じるにはtrueを返してください。onPurchaseCompleted: 購入完了後もフローを開いたままにします。閉じるにはtrueを返してください。onRestoreCompleted: 復元が成功した後もフローを開いたままにします。閉じるにはtrueを返してください。onError: レンダリングに失敗した場合にフローを閉じます。
イベントハンドラー
| イベントハンドラー | 説明 |
|---|---|
| onCustomAction | ユーザーがカスタムアクションを実行したとき(例:カスタムボタンのクリック)に呼び出されます。 |
| onUrlPress | ユーザーがフロー内のURLをクリックしたときに呼び出されます。 |
| onAndroidSystemBack | ユーザーがAndroidのシステムのBackボタンをタップしたときに呼び出されます。デフォルトではフローは開いたままです。trueを返すと閉じます。 |
| onCloseButtonPress | 閉じるボタンが表示されており、ユーザーがそれをタップしたときに呼び出されます。このハンドラー内でフロー画面を閉じることを推奨します。 |
| onPurchaseCompleted | 購入が完了したとき(成功・ユーザーによるキャンセル・承認待ちのいずれの場合も)に呼び出されます。購入が成功した場合は、更新済みのAdaptyProfileが提供されます。ユーザーによるキャンセルや保留中の支払い(例:ペアレンタルアプルーバルが必要な場合)はonPurchaseFailedではなく、このイベントをトリガーします。 |
| onPurchaseStarted | ユーザーが「購入」アクションボタンをタップして購入プロセスを開始したときに呼び出されます。 |
| onPurchaseFailed | エラー(例:支払い制限、無効なプロダクト、ネットワーク障害、トランザクション検証の失敗)によって購入が失敗したときに呼び出されます。ユーザーによるキャンセルや保留中の支払いではなく、それらは代わりにonPurchaseCompletedをトリガーします。 |
| onRestoreStarted | ユーザーが購入の復元プロセスを開始したときに呼び出されます。 |
| onRestoreCompleted | 購入の復元が成功し、更新済みのAdaptyProfileが提供されたときに呼び出されます。ユーザーが必要なaccessLevelを持っている場合は画面を閉じることを推奨します。確認方法についてはサブスクリプションのステータスを参照してください。 |
| onRestoreFailed | 復元プロセスが失敗し、AdaptyErrorが提供されたときに呼び出されます。 |
| onProductSelected | フロービュー内でいずれかのプロダクトが選択されたときに呼び出されます。購入前にユーザーが選択した内容を確認できます。 |
| onError | ビューのレンダリング中にエラーが発生し、AdaptyErrorが提供されたときに呼び出されます。このようなエラーは本来発生しないものです。発生した場合はご報告ください。 |
| onLoadingProductsFailed | プロダクトの読み込みが失敗し、AdaptyErrorが提供されたときに呼び出されます。ビュー作成時にprefetchProducts: trueを設定していない場合、AdaptyUIがサーバーから必要なオブジェクトを自動取得します。 |
| onAppeared | フローがユーザーに表示されたときに呼び出されます。iOSでは、ユーザーがフロー内のウェブペイウォールボタンをタップし、アプリ内ブラウザでウェブペイウォールが開いたときにも呼び出されます。 |
| onDisappeared | フローがユーザーによって閉じられたときに呼び出されます。iOSでは、フローからアプリ内ブラウザで開いたウェブペイウォールが画面から消えたときにも呼び出されます。 |
| onWebPaymentNavigationFinished | ウェブペイウォールを購入のために開こうとした後(成功・失敗を問わず)に呼び出されます。 |
| onRequestAppReview | フローからのアプリレビューリクエスト用に予約されています。フローはまだアプリレビューリクエストをトリガーしないため、実装は不要です。 |
| onAnalytics | フローからのカスタム分析イベント用に予約されています。フローはまだこれらをコードに送出しないため、実装は不要です。 |
| onRequestPermission | フローからのシステム権限リクエスト(プッシュ通知やカメラアクセスなど)用に予約されています。フローはまだ権限リクエストをトリガーしないため、実装は不要です。 |
| onObserverPurchaseInitiated | オブザーバーモード専用:ユーザーがフロー内の購入ボタンをタップしたときに呼び出されます。Adaptyは購入を実行しません。独自の購入コードで購入を行い、その後トランザクションをAdaptyに報告してください。下記のオブザーバーモードでの購入処理を参照してください。 |
| onObserverRestoreInitiated | オブザーバーモード専用:ユーザーがフロー内の復元ボタンをタップしたときに呼び出されます。Adaptyは復元を実行しません。自分で復元を行い、復元されたトランザクションを報告してください。下記のオブザーバーモードでの購入処理を参照してください。 |
オブザーバーモードでの購入処理
オブザーバーモード(observerMode: true)でSDKを有効化し、Adaptyがレンダリングするフローを表示している場合、SDKは購入処理を自動で行いません。ユーザーが購入ボタンまたは復元ボタンをタップすると、SDKはonObserverPurchaseInitiatedまたはonObserverRestoreInitiatedを呼び出すので、独自のコードで購入や復元を実行できます。詳細なセットアップ手順はオブザーバーモードでのフローの表示を参照してください。
このガイドでは、購入、復元、プロダクト選択、ペイウォールのレンダリングに関するイベント処理を説明します。ボタン操作(ペイウォールを閉じる、リンクを開くなど)の処理も実装する必要があります。詳しくはボタンアクションの処理に関するガイドをご覧ください。
ペイウォールビルダーで設定したペイウォールは、購入や復元のために追加コードは不要です。ただし、アプリが応答できるイベントが発生します。これらのイベントには、ボタン操作(閉じるボタン、URL、プロダクト選択など)や、ペイウォール上で行われた購入関連アクションの通知が含まれます。これらのイベントへの対応方法については以下をご覧ください。
モバイルアプリ内でペイウォール画面上の処理を制御・監視するには、view.setEventHandlers メソッドを実装してください:
const view = await createPaywallView(paywall);
const unsubscribe = view.setEventHandlers({
onCloseButtonPress() {
console.log('User closed paywall');
return true; // Allow the paywall to close
},
onAndroidSystemBack() {
console.log('User pressed back button');
return true; // Allow the paywall to close
},
onAppeared() {
console.log('Paywall appeared');
return false; // Don't close the paywall
},
onDisappeared() {
console.log('Paywall disappeared');
},
onPurchaseCompleted(purchaseResult, product) {
console.log('Purchase completed:', purchaseResult);
return purchaseResult.type !== 'user_cancelled'; // Close if not cancelled
},
onPurchaseStarted(product) {
console.log('Purchase started:', product);
return false; // Don't close the paywall
},
onPurchaseFailed(error, product) {
console.error('Purchase failed:', error);
return false; // Don't close the paywall
},
onRestoreCompleted(profile) {
console.log('Restore completed:', profile);
return true; // Close the paywall after successful restore
},
onRestoreFailed(error) {
console.error('Restore failed:', error);
return false; // Don't close the paywall
},
onProductSelected(productId) {
console.log('Product selected:', productId);
return false; // Don't close the paywall
},
onRenderingFailed(error) {
console.error('Rendering failed:', error);
return false; // Don't close the paywall
},
onLoadingProductsFailed(error) {
console.error('Loading products failed:', error);
return false; // Don't close the paywall
},
onUrlPress(url) {
window.open(url, '_blank');
return false; // Don't close the paywall
},
});イベント例(クリックして展開)
// onCloseButtonPress
{
"event": "close_button_press"
}
// onAndroidSystemBack
{
"event": "android_system_back"
}
// onAppeared
{
"event": "paywall_shown"
}
// onDisappeared
{
"event": "paywall_closed"
}
// onUrlPress
{
"event": "url_press",
"url": "https://example.com/terms"
}
// onCustomAction
{
"event": "custom_action",
"actionId": "login"
}
// onProductSelected
{
"event": "product_selected",
"productId": "premium_monthly"
}
// onPurchaseStarted
{
"event": "purchase_started",
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
// onPurchaseCompleted - Success
{
"event": "purchase_completed",
"purchaseResult": {
"type": "success",
"profile": {
"accessLevels": {
"premium": {
"id": "premium",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
}
}
},
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
// onPurchaseCompleted - Cancelled
{
"event": "purchase_completed",
"purchaseResult": {
"type": "user_cancelled"
},
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
// onPurchaseFailed
{
"event": "purchase_failed",
"error": {
"code": "purchase_failed",
"message": "Purchase failed due to insufficient funds",
"details": {
"underlyingError": "Insufficient funds in account"
}
}
}
// onRestoreCompleted
{
"event": "restore_completed",
"profile": {
"accessLevels": {
"premium": {
"id": "premium",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
},
"subscriptions": [
{
"vendorProductId": "premium_monthly",
"isActive": true,
"expiresAt": "2024-02-15T10:30:00Z"
}
]
}
}
// onRestoreFailed
{
"event": "restore_failed",
"error": {
"code": "restore_failed",
"message": "Purchase restoration failed",
"details": {
"underlyingError": "No previous purchases found"
}
}
}
// onRenderingFailed
{
"event": "rendering_failed",
"error": {
"code": "rendering_failed",
"message": "Failed to render paywall interface",
"details": {
"underlyingError": "Invalid paywall configuration"
}
}
}
// onLoadingProductsFailed
{
"event": "loading_products_failed",
"error": {
"code": "products_loading_failed",
"message": "Failed to load products from the server",
"details": {
"underlyingError": "Network timeout"
}
}
}必要なイベントハンドラーだけ登録し、不要なものは省略できます。この場合、未使用のイベントリスナーは作成されません。必須のイベントハンドラーはありません。
イベントハンドラーはブール値を返します。true を返すと、表示プロセスが完了したとみなされ、ペイウォール画面が閉じられ、このビューのイベントリスナーが削除されます。
一部のイベントハンドラーにはデフォルトの動作があり、必要に応じてオーバーライドできます:
onCloseButtonPress: 閉じるボタンが押されたときにペイウォールを閉じます。onAndroidSystemBack: Back ボタンが押されたときにペイウォールを閉じます。onRestoreCompleted: 復元が成功した後にペイウォールを閉じます。onPurchaseCompleted: ユーザーがキャンセルしない限りペイウォールを閉じます。onRenderingFailed: レンダリングに失敗した場合にペイウォールを閉じます。onUrlPress: システムブラウザでURLを開き、ペイウォールは閉じません。
イベントハンドラー
| イベントハンドラー | 説明 |
|---|---|
| onCustomAction | ユーザーがカスタムアクション(例:カスタムボタンのクリック)を実行したときに呼び出されます。 |
| onUrlPress | ユーザーがペイウォール内のURLをクリックしたときに呼び出されます。 |
| onAndroidSystemBack | ユーザーがAndroidのシステム Back ボタンをタップしたときに呼び出されます。 |
| onCloseButtonPress | 閉じるボタンが表示されていてユーザーがそれをタップしたときに呼び出されます。このハンドラー内でペイウォール画面を閉じることを推奨します。 |
| onPurchaseCompleted | 購入が完了したときに呼び出されます(成功・ユーザーによるキャンセル・承認待ちのいずれの場合も)。購入が成功した場合は、更新された AdaptyProfile が提供されます。ユーザーによるキャンセルや支払い保留(保護者の承認が必要な場合など)は、onPurchaseFailed ではなくこのイベントをトリガーします。 |
| onPurchaseStarted | ユーザーが「購入」アクションボタンをタップして購入プロセスを開始したときに呼び出されます。 |
| onPurchaseCancelled | ユーザーが購入プロセスを開始した後、手動で中断(支払いダイアログをキャンセル)したときに呼び出されます。 |
| onPurchaseFailed | エラー(支払い制限、無効なプロダクト、ネットワーク障害、取引検証の失敗など)によって購入が失敗したときに呼び出されます。ユーザーによるキャンセルや支払い保留の場合は呼び出されず、代わりに onPurchaseCompleted がトリガーされます。 |
| onRestoreStarted | ユーザーが購入の復元プロセスを開始したときに呼び出されます。 |
| onRestoreCompleted | 購入の復元が成功したときに呼び出され、更新された AdaptyProfile が提供されます。ユーザーが必要な accessLevel を持っている場合は画面を閉じることを推奨します。確認方法についてはサブスクリプションのステータスを参照してください。 |
| onRestoreFailed | 復元プロセスが失敗したときに呼び出され、AdaptyError が提供されます。 |
| onProductSelected | ペイウォールビュー内のいずれかのプロダクトが選択されたときに呼び出されます。購入前にユーザーが何を選択しているかを把握できます。 |
| onAppeared | ペイウォールビューが画面に表示されたときに呼び出されます。iOS では、ユーザーがペイウォール内のウェブペイウォールボタンをタップしてインアプリブラウザでウェブペイウォールが開いた場合にも呼び出されます。 |
| onDisappeared | ペイウォールビューが画面から消えたときに呼び出されます。iOS では、ペイウォールからインアプリブラウザで開いたウェブペイウォールが画面から消えた場合にも呼び出されます。 |
| onRenderingFailed | ビューのレンダリング中にエラーが発生したときに呼び出され、AdaptyError が提供されます。このようなエラーは通常発生しないため、もし遭遇した場合はお知らせください。 |
| onLoadingProductsFailed | プロダクトの読み込みが失敗したときに呼び出され、AdaptyError が提供されます。ビュー作成時に prefetchProducts: true を設定していない場合、AdaptyUI が自動的にサーバーから必要なオブジェクトを取得します。 |