Xử lý sự kiện flow & paywall - React Native

Important

Hướng dẫn này đề cập đến việc xử lý sự kiện cho các giao dịch mua, khôi phục, lựa chọn sản phẩm và hiển thị flow. Bạn cũng có thể thiết lập xử lý nút (đóng flow, mở liên kết, các hành động tùy chỉnh, v.v.). Xem hướng dẫn xử lý hành động nút để biết thêm chi tiết.

Flow và paywall được xây dựng bằng Flow Builder không cần thêm code để thực hiện và khôi phục giao dịch mua. Tuy nhiên, chúng tạo ra một số sự kiện mà ứng dụng của bạn có thể phản hồi. Những sự kiện đó bao gồm các lần nhấn nút (nút đóng, URL, lựa chọn sản phẩm, v.v.) cũng như các thông báo về các hành động liên quan đến giao dịch mua được thực hiện trên flow. Hãy tìm hiểu cách phản hồi các sự kiện này bên dưới.

Để kiểm soát hoặc theo dõi các tiến trình diễn ra trên màn hình flow trong ứng dụng di động của bạn, hãy triển khai các event handler:

Ví dụ về sự kiện (Nhấn để mở rộng)
// onCloseButtonPress
{
  //Record the event
}

// onAndroidSystemBack
{
  //Record the event
}

// onUrlPress
{
  "url": "https://example.com/terms"
}

// onCustomAction
{
  "actionId": "login"
}

// onProductSelected
{
  "productId": "premium_monthly"
}

// onPurchaseStarted
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onPurchaseCompleted - Success
{
  "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",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onPurchaseCompleted - Cancelled
{
  "purchaseResult": {
    "type": "user_cancelled"
  },
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onPurchaseFailed
{
  "error": {
    "code": "purchase_failed",
    "message": "Purchase failed due to insufficient funds",
    "details": {
      "underlyingError": "Insufficient funds in account"
    }
  },
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onRestoreCompleted
{
  "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
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

// onError
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render flow interface",
    "details": {
      "underlyingError": "Invalid flow configuration"
    }
  }
}

// onLoadingProductsFailed
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

// onAppeared
{
  "view": {
    "id": "3f8a1c7e-9b24-4d51-8e30-6c5b2a9f1d47",
    "placementId": "onboarding_paywall",
    "variationId": "d21c4b6a-57e8-4f39-b0a2-8c7e13f5d94b",
    "locale": "es"
  }
}

// onDisappeared
{
  //Record the event
}

// onWebPaymentNavigationFinished
{
  //Record the event
}

Bạn có thể đăng ký những event handler cần thiết và bỏ qua những cái không cần dùng. Như vậy, các event listener không cần thiết sẽ không được tạo ra. Không có event handler nào là bắt buộc.

Event handler trả về một giá trị boolean. Nếu trả về true, quá trình hiển thị được coi là hoàn tất, flow screen sẽ đóng lại và các event listener của view đó sẽ bị xóa. Một số event handler có hành vi mặc định mà bạn có thể ghi đè nếu cần:

  • onCloseButtonPress: đóng flow khi nhấn nút đóng.
  • onUrlPress: mở URL được nhấn và giữ flow mở.
  • onAndroidSystemBack (chỉ dành cho modal presentation): giữ flow mở khi nhấn nút Back. Trả về true để đóng flow.
  • onRestoreCompleted: giữ flow mở sau khi khôi phục thành công. Trả về true để đóng flow.
  • onPurchaseCompleted: giữ flow mở sau khi hoàn tất mua hàng. Trả về true để đóng flow.
  • onError: đóng flow nếu quá trình render thất bại.

Trình xử lý sự kiện

Event handlerMô tả
onCustomActionĐược gọi khi người dùng thực hiện một hành động tùy chỉnh, ví dụ: nhấn vào nút tùy chỉnh.
onUrlPressĐược gọi khi người dùng nhấn vào một URL trong flow của bạn.
onAndroidSystemBackChỉ dành cho chế độ hiển thị modal: Được gọi khi người dùng nhấn nút Back hệ thống của Android.
onCloseButtonPressĐược gọi khi nút đóng hiển thị và người dùng nhấn vào nó. Nên đóng màn hình flow trong handler này.
onPurchaseCompletedĐược gọi khi giao dịch mua hoàn tất, dù thành công, bị người dùng hủy, hay đang chờ phê duyệt. Nếu mua thành công, sẽ cung cấp AdaptyProfile đã được cập nhật. Trường hợp người dùng hủy và thanh toán đang chờ (ví dụ: cần phê duyệt của phụ huynh) sẽ kích hoạt sự kiện này, không phải onPurchaseFailed.
onPurchaseStartedĐược gọi khi người dùng nhấn nút hành động “Purchase” để bắt đầu quá trình mua.
onPurchaseFailedĐược gọi khi giao dịch mua thất bại do lỗi (ví dụ: hạn chế thanh toán, sản phẩm không hợp lệ, lỗi mạng, lỗi xác minh giao dịch). Không được gọi khi người dùng hủy hoặc thanh toán đang chờ — những trường hợp đó sẽ kích hoạt onPurchaseCompleted.
onRestoreStartedĐược gọi khi người dùng bắt đầu quá trình khôi phục giao dịch mua.
onRestoreCompletedĐược gọi khi khôi phục giao dịch mua thành công và cung cấp AdaptyProfile đã được cập nhật. Nên đóng màn hình nếu người dùng có accessLevel cần thiết. Xem chủ đề Trạng thái gói đăng ký để biết cách kiểm tra.
onRestoreFailedĐược gọi khi quá trình khôi phục thất bại và cung cấp AdaptyError.
onProductSelectedĐược gọi khi người dùng chọn bất kỳ sản phẩm nào trong flow, cho phép bạn theo dõi những gì người dùng chọn trước khi mua.
onErrorĐược gọi khi xảy ra lỗi trong quá trình render view và cung cấp AdaptyError. Các lỗi này không nên xảy ra, vì vậy nếu bạn gặp phải, hãy cho chúng tôi biết.
onLoadingProductsFailedĐược gọi khi tải sản phẩm thất bại và cung cấp AdaptyError. Nếu bạn chưa đặt prefetchProducts: true khi tạo view, AdaptyUI sẽ tự lấy các đối tượng cần thiết từ server.
onAppearedĐược gọi khi flow được hiển thị cho người dùng, và cung cấp view đã xuất hiện — xem Tham số view. Trên iOS, cũng được gọi khi người dùng nhấn vào nút web paywall bên trong flow và web paywall mở trong trình duyệt trong ứng dụng.
onDisappearedChỉ dành cho chế độ hiển thị modal: Được gọi khi người dùng đóng flow. Trên iOS, cũng được gọi khi web paywall được mở từ flow trong trình duyệt trong ứng dụng biến mất khỏi màn hình.
onWebPaymentNavigationFinishedĐược gọi sau khi cố gắng mở web paywall để mua, dù thành công hay thất bại.
onAnalyticsĐược gọi khi flow báo cáo một sự kiện analytics, chẳng hạn như lượt xem màn hình. Xem Sự kiện analytics bên dưới.
onRequestAppReviewDành riêng cho các yêu cầu đánh giá ứng dụng từ flow. Flow chưa kích hoạt các yêu cầu đánh giá ứng dụng, vì vậy bạn chưa cần triển khai nó.
onRequestPermissionDành riêng cho các yêu cầu quyền hệ thống (như thông báo đẩy hoặc truy cập camera) từ flow. Flow chưa kích hoạt các yêu cầu quyền, vì vậy bạn chưa cần triển khai nó.
onObserverPurchaseInitiatedChỉ dành cho chế độ observer: Được gọi khi người dùng nhấn nút mua trong flow. Adapty không thực hiện giao dịch mua — hãy tự thực hiện bằng mã mua của bạn, sau đó báo cáo giao dịch cho Adapty. Xem Xử lý giao dịch mua trong chế độ observer bên dưới.
onObserverRestoreInitiatedChỉ dành cho chế độ observer: Được gọi khi người dùng nhấn nút khôi phục trong flow. Adapty không khôi phục — hãy tự thực hiện, sau đó báo cáo các giao dịch đã khôi phục. Xem Xử lý giao dịch mua trong chế độ observer bên dưới.

Tham số view

Tham số view yêu cầu React Native SDK phiên bản 4.0.3 trở lên. Trong tất cả các flow handler, chỉ có onAppeared nhận được mô tả về chính view đó — một đối tượng FlowEventView với các trường sau:

TrườngMô tả
idMã định danh của view instance này. Đây là giá trị nội bộ của SDK và không khớp với bất kỳ thứ gì trên Adapty Dashboard.
placementIdPlacement mà flow được tải về cho.
variationIdBiến thể mà flow đã phân giải thành, dùng để gán dữ liệu analytics của bạn cho A/B test.
localeBản địa hóa flow mà view được xây dựng với. Giá trị này có thể khác với ngôn ngữ bạn yêu cầu nếu flow không có bản địa hóa tương ứng. Đọc giá trị này để điều chỉnh phần còn lại của màn hình cho khớp với ngôn ngữ mà flow hiển thị. Xem Sử dụng bản địa hóa và mã ngôn ngữ.

Sự kiện analytics

const unsubscribe = view.setEventHandlers({
  onAnalytics(name, params) {
    return false; // keep the flow open
  },
});

Một flow báo cáo sự kiện flow_screen_showed mỗi khi người dùng mở một màn hình trong flow đó. Adapty đếm các sự kiện này trong phân tích flow của riêng mình và cũng gửi chúng đến ứng dụng của bạn, để bạn có thể xây dựng cùng một phễu trong hệ thống phân tích của riêng mình.

Tham sốMô tả
instanceIdID của màn hình mà người dùng đã mở.
screen_orderVị trí của màn hình trong flow.
is_last_screentrue khi màn hình không có đường đi tiếp theo. Một flow có nhánh có thể kết thúc ở nhiều màn hình khác nhau, và mỗi màn hình đó đều báo cáo true.

Cả isBackendEventisCustomerEvent đều là true cho sự kiện này: Adapty vẫn tiếp tục đếm sự kiện, và ứng dụng của bạn cũng nhận được nó.

Xem Theo dõi lượt xem màn hình flow để biết cách xử lý chúng.

Xử lý mua hàng ở chế độ Observer

Nếu bạn kích hoạt SDK ở chế độ Observer (observerMode: true) và hiển thị flow do Adapty render, SDK sẽ không thực hiện mua hàng thay bạn. Khi người dùng nhấn nút mua hàng hoặc khôi phục, SDK sẽ gọi onObserverPurchaseInitiated hoặc onObserverRestoreInitiated thay thế. Hãy tự thực hiện việc mua hàng hoặc khôi phục bằng code của bạn, điều khiển trạng thái loading của flow thông qua các callback được cung cấp, và sau đó báo cáo giao dịch cho Adapty.

const unsubscribe = view.setEventHandlers({
  onObserverPurchaseInitiated(product, onStartPurchase, onFinishPurchase) {
    onStartPurchase(); // show the flow's loading indicator
    myPurchaseApi(product.vendorProductId)
      .then((transactionId) => adapty.reportTransaction(transactionId))
      .finally(() => onFinishPurchase()); // hide the loading indicator
    return false; // keep the flow open; dismiss it yourself after success
  },
  onObserverRestoreInitiated(onStartRestore, onFinishRestore) {
    onStartRestore();
    myRestoreApi()
      .finally(() => onFinishRestore());
    return false;
  },
});
Important

Hướng dẫn này đề cập đến việc xử lý các sự kiện cho giao dịch mua, khôi phục, chọn sản phẩm và hiển thị paywall. Bạn cũng cần triển khai xử lý nút bấm (đóng paywall, mở liên kết, v.v.). Xem hướng dẫn xử lý hành động nút bấm để biết thêm chi tiết.

Các paywall được cấu hình bằng Paywall Builder không cần thêm code để thực hiện và khôi phục giao dịch mua. Tuy nhiên, chúng tạo ra một số sự kiện mà ứng dụng của bạn có thể phản hồi. Các sự kiện đó bao gồm thao tác nhấn nút (nút đóng, URL, chọn sản phẩm, v.v.) cũng như thông báo về các hành động liên quan đến giao dịch mua được thực hiện trên paywall. Hãy xem cách phản hồi các sự kiện này bên dưới.

Warning

Hướng dẫn này chỉ dành cho paywall Paywall Builder mới yêu cầu Adapty SDK v3.0 trở lên.

Щоб контролювати або theo dõi các tiến trình xảy ra trên màn hình paywall trong ứng dụng di động của bạn, hãy triển khai các trình xử lý sự kiện:

Ví dụ về sự kiện (Nhấp để mở rộng)
// onCloseButtonPress
{
  //Record the event
}

// onAndroidSystemBack
{
  //Record the event
}

// onUrlPress
{
  "url": "https://example.com/terms"
}

// onCustomAction
{
  "actionId": "login"
}

// onProductSelected
{
  "productId": "premium_monthly"
}

// onPurchaseStarted
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onPurchaseCompleted - Success
{
  "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",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onPurchaseCompleted - Cancelled
{
  "purchaseResult": {
    "type": "user_cancelled"
  },
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onPurchaseFailed
{
  "error": {
    "code": "purchase_failed",
    "message": "Purchase failed due to insufficient funds",
    "details": {
      "underlyingError": "Insufficient funds in account"
    }
  },
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "price": {
      "amount": 9.99,
      "currencyCode": "USD",
      "currencySymbol": "$",
      "localizedString": "$9.99"
    }
  }
}

// onRestoreCompleted
{
  "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
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

// onRenderingFailed
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render paywall interface",
    "details": {
      "underlyingError": "Invalid paywall configuration"
    }
  }
}

// onLoadingProductsFailed
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

// onPaywallShown
{
  //Record the event
}

// onPaywallClosed
{
  //Record the event
}

// onWebPaymentNavigationFinished
{
  //Record the event
}

Bạn có thể đăng ký các event handler mà bạn cần và bỏ qua những cái không cần thiết. Nhờ vậy, các event listener không dùng đến sẽ không được tạo ra. Không có event handler nào là bắt buộc.

Event handler trả về một giá trị boolean. Nếu trả về true, quá trình hiển thị được coi là hoàn tất, màn hình paywall sẽ đóng lại và các event listener cho view này sẽ bị xóa. Một số event handler có hành vi mặc định mà bạn có thể ghi đè nếu cần:

  • onCloseButtonPress: đóng paywall khi nhấn nút đóng.
  • onUrlPress: mở URL được nhấn và giữ paywall mở.
  • onAndroidSystemBack (chỉ dành cho modal presentation): đóng paywall khi nhấn nút Back.
  • onRestoreCompleted: đóng paywall sau khi khôi phục thành công.
  • onPurchaseCompleted: đóng paywall trừ khi người dùng hủy.
  • onRenderingFailed: đóng paywall nếu quá trình render thất bại.

Trình xử lý sự kiện

Event handlerMô tả
onCustomActionĐược gọi khi người dùng thực hiện một hành động tùy chỉnh, ví dụ: nhấn vào nút tùy chỉnh.
onUrlPressĐược gọi khi người dùng nhấn vào một URL trong paywall.
onAndroidSystemBackChỉ dành cho modal presentation: Được gọi khi người dùng nhấn nút Back hệ thống của Android.
onCloseButtonPressĐược gọi khi nút đóng hiển thị và người dùng nhấn vào đó. Nên đóng màn hình paywall trong handler này.
onPurchaseCompletedĐược gọi khi quá trình mua hoàn tất, dù thành công, người dùng hủy, hay đang chờ phê duyệt. Nếu mua thành công, sẽ trả về AdaptyProfile đã được cập nhật. Việc người dùng hủy và các khoản thanh toán đang chờ (ví dụ: cần phê duyệt của phụ huynh) sẽ kích hoạt sự kiện này, không phải onPurchaseFailed.
onPurchaseStartedĐược gọi khi người dùng nhấn nút hành động “Purchase” để bắt đầu quá trình mua.
onPurchaseFailedĐược gọi khi giao dịch mua thất bại do lỗi (ví dụ: hạn chế thanh toán, sản phẩm không hợp lệ, lỗi mạng, lỗi xác minh giao dịch). Không được gọi khi người dùng hủy hoặc thanh toán đang chờ — những trường hợp đó sẽ kích hoạt onPurchaseCompleted.
onRestoreStartedĐược gọi khi người dùng bắt đầu quá trình khôi phục giao dịch.
onRestoreCompletedĐược gọi khi khôi phục giao dịch thành công và trả về AdaptyProfile đã được cập nhật. Nên đóng màn hình nếu người dùng đã có accessLevel cần thiết. Xem thêm tại Trạng thái gói đăng ký để biết cách kiểm tra.
onRestoreFailedĐược gọi khi quá trình khôi phục thất bại và trả về AdaptyError.
onProductSelectedĐược gọi khi người dùng chọn bất kỳ sản phẩm nào trong paywall, giúp bạn theo dõi lựa chọn của người dùng trước khi mua.
onRenderingFailedĐược gọi khi xảy ra lỗi trong quá trình render view và trả về AdaptyError. Lỗi này thường không nên xảy ra, vì vậy nếu gặp phải, hãy liên hệ với chúng tôi.
onLoadingProductsFailedĐược gọi khi tải sản phẩm thất bại và trả về AdaptyError. Nếu bạn chưa đặt prefetchProducts: true khi tạo view, AdaptyUI sẽ tự lấy các đối tượng cần thiết từ server.
onPaywallShownĐược gọi khi paywall được hiển thị cho người dùng. Trên iOS, cũng được gọi khi người dùng nhấn vào nút web paywall trong paywall và web paywall mở trong trình duyệt in-app.
onPaywallClosedChỉ dành cho modal presentation: Được gọi khi người dùng đóng paywall. Trên iOS, cũng được gọi khi web paywall mở từ paywall trong trình duyệt in-app biến mất khỏi màn hình.
onWebPaymentNavigationFinishedĐược gọi sau khi cố gắng mở web paywall để mua, dù thành công hay thất bại.