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

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ị. Việc đóng view và mở liên kết được xử lý bởi implementation mặc định của flowViewDidPerformAction — xem hướng dẫn xử lý hành động button để ghi đè chúng hoặc xử lý các hành động button tùy chỉnh.

Flows và paywalls được cấu hình bằng 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 này 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ư thông báo về các hành động liên quan đến giao dịch mua diễn ra trên flow hoặc paywall. 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 xảy ra trên màn hình flow hoặc paywall trong ứng dụng di động của bạn, hãy triển khai các phương thức AdaptyUIFlowsEventsObserver và thiết lập observer trước khi hiển thị bất kỳ màn hình nào:

AdaptyUI().setFlowsEventsObserver(this);

Ba phương thức observer là bắt buộc — class của bạn sẽ không biên dịch được nếu thiếu chúng: flowViewDidFinishPurchase, flowViewDidFinishRestore, và flowViewDidReceiveError. Tất cả các phương thức còn lại là tùy chọn. Để hủy kết nối một observer đã được thiết lập trước đó, truyền null vào setFlowsEventsObserver.

Muốn xem ví dụ thực tế về cách tích hợp Adapty SDK vào ứng dụng di động? Hãy xem ứng dụng mẫu của chúng tôi, nơi minh họa toàn bộ quá trình thiết lập, bao gồm hiển thị paywall, thực hiện mua hàng và các chức năng cơ bản khác.

Các ví dụ sự kiện dưới đây hiển thị các thuộc tính có sẵn trên từng đối tượng, với các giá trị minh họa trong phần chú thích.

Sự kiện do người dùng tạo ra

View appeared

Phương thức này được gọi khi flow hoặc màn hình paywall xuất hiện trên màn hình.

Trên iOS, phương thức này cũng được gọi khi người dùng nhấn vào nút web paywall bên trong một paywall, và web paywall mở ra trong trình duyệt trong ứng dụng.

void flowViewDidAppear(AdaptyUIFlowView view) {
}

View disappeared

Phương thức này được gọi khi flow hoặc màn hình paywall bị đóng khỏi màn hình.

Trên iOS, cũng được gọi khi một web paywall mở từ paywall trong trình duyệt trong ứng dụng biến mất khỏi màn hình.

void flowViewDidDisappear(AdaptyUIFlowView view) {
}

Chọn sản phẩm

Nếu một sản phẩm được chọn để mua (bởi người dùng hoặc hệ thống), phương thức này sẽ được gọi:

void flowViewDidSelectProduct(AdaptyUIFlowView view, String productId) {
}
Ví dụ sự kiện (Nhấn để mở rộng)
void flowViewDidSelectProduct(AdaptyUIFlowView view, String productId) {
  // productId is a String:
  productId; // 'premium_monthly'
}

Đã bắt đầu mua hàng

Nếu người dùng bắt đầu quá trình mua hàng, phương thức này sẽ được gọi:

void flowViewDidStartPurchase(AdaptyUIFlowView view, AdaptyPaywallProduct product) {
}
Ví dụ sự kiện (Nhấn để mở rộng)
void flowViewDidStartPurchase(AdaptyUIFlowView view, AdaptyPaywallProduct product) {
  // product — AdaptyPaywallProduct:
  product.vendorProductId;        // 'premium_monthly'
  product.localizedTitle;         // 'Premium Monthly'
  product.localizedDescription;   // 'Premium subscription for 1 month'
  product.price.amount;           // 9.99            (double)
  product.price.currencyCode;     // 'USD'
  product.price.localizedString;  // '$9.99'
}

Hoàn tất giao dịch mua

Phương thức này là bắt buộc. Nó được gọi khi một giao dịch mua thành công, người dùng hủy giao dịch, hoặc giao dịch có vẻ đang chờ xử lý:

void flowViewDidFinishPurchase(AdaptyUIFlowView view, 
                               AdaptyPaywallProduct product, 
                               AdaptyPurchaseResult purchaseResult) {
    switch (purchaseResult) {
      case AdaptyPurchaseResultSuccess(profile: final profile):
        // successful purchase
        break;
      case AdaptyPurchaseResultPending():
        // purchase is pending
        break;
      case AdaptyPurchaseResultUserCancelled():
        // user cancelled the purchase
        break;
      default:
        break;
    }
}
Ví dụ sự kiện (Nhấn để mở rộng)
void flowViewDidFinishPurchase(AdaptyUIFlowView view,
                               AdaptyPaywallProduct product,
                               AdaptyPurchaseResult purchaseResult) {
  // product — AdaptyPaywallProduct:
  product.vendorProductId; // 'premium_monthly'

  switch (purchaseResult) {
    case AdaptyPurchaseResultSuccess(profile: final profile):
      // profile — AdaptyProfile:
      profile.accessLevels['premium']?.isActive;  // true
      profile.accessLevels['premium']?.expiresAt; // DateTime(2027, 2, 15, 10, 30)
      break;
    case AdaptyPurchaseResultPending():
      // no additional data
      break;
    case AdaptyPurchaseResultUserCancelled():
      // no additional data
      break;
  }
}

Khác với v3, phương thức này không có hành vi mặc định — view sẽ không tự động bị đóng sau khi mua thành công. Bạn tự quyết định bước tiếp theo: tiếp tục flow hoặc gọi view.dismiss(). Tham khảo Xử lý hành động nút để biết thêm chi tiết về cách đóng màn hình.

Hoàn tất điều hướng thanh toán web

Phương thức này được gọi sau khi có một lần thử mở web paywall cho một sản phẩm cụ thể. Điều này bao gồm cả các lần điều hướng thành công lẫn thất bại:

void flowViewDidFinishWebPaymentNavigation(AdaptyUIFlowView view, 
                                           AdaptyPaywallProduct? product, 
                                           AdaptyError? error) {
}

Tham số:

Tham sốMô tả
productMột AdaptyPaywallProduct mà web paywall đã được mở. Có thể là null.
errorMột đối tượng AdaptyError nếu điều hướng web paywall thất bại; null nếu điều hướng thành công.

Giao dịch thất bại

Phương thức này được gọi khi một giao dịch mua thất bại (ví dụ: do sự cố thanh toán hoặc lỗi mạng). Nó không kích hoạt khi người dùng tự hủy hoặc khi giao dịch đang chờ xử lý—những trường hợp đó được xử lý bởi flowViewDidFinishPurchase:

void flowViewDidFailPurchase(AdaptyUIFlowView view, 
                             AdaptyPaywallProduct product, 
                             AdaptyError error) {
}

Bắt đầu khôi phục

Nếu người dùng bắt đầu quá trình khôi phục, phương thức này sẽ được gọi:

void flowViewDidStartRestore(AdaptyUIFlowView view) {
}

Khôi phục thành công

Phương thức này là bắt buộc. Nếu việc khôi phục mua hàng thành công, nó sẽ được gọi:

void flowViewDidFinishRestore(AdaptyUIFlowView view, AdaptyProfile profile) {
}
Ví dụ sự kiện (Nhấn để mở rộng)
void flowViewDidFinishRestore(AdaptyUIFlowView view, AdaptyProfile profile) {
  // profile — AdaptyProfile:
  profile.accessLevels['premium']?.isActive;            // true
  profile.accessLevels['premium']?.expiresAt;           // DateTime(2027, 2, 15, 10, 30)
  profile.subscriptions['premium_monthly']?.isActive;   // true
  profile.subscriptions['premium_monthly']?.expiresAt;  // DateTime(2027, 2, 15, 10, 30)
}

Chúng tôi khuyến nghị đóng màn hình nếu người dùng đã có accessLevel yêu cầu. Tham khảo chủ đề Trạng thái gói đăng ký để tìm hiểu cách kiểm tra và chủ đề Phản hồi các hành động nút để tìm hiểu cách đóng màn hình.

Khôi phục thất bại

Nếu việc khôi phục giao dịch mua thất bại, phương thức này sẽ được gọi:

void flowViewDidFailRestore(AdaptyUIFlowView view, AdaptyError error) {
}

Tải dữ liệu và hiển thị

Lỗi tải sản phẩm

Nếu bạn không truyền mảng sản phẩm trong quá trình khởi tạo, AdaptyUI sẽ tự động lấy các đối tượng cần thiết từ máy chủ. Nếu thao tác này thất bại, AdaptyUI sẽ báo lỗi bằng cách gọi phương thức sau:

void flowViewDidFailLoadingProducts(AdaptyUIFlowView view, AdaptyError error) {
}

Lỗi view

Phương thức này là bắt buộc. Nó thay thế phương thức paywallViewDidFailRendering của v3: các lỗi xảy ra trong quá trình render giao diện, cũng như các lỗi view khác, sẽ được báo cáo thông qua lần gọi này. Sau khi bạn implement nó, việc đóng view là tùy bạn quyết định — chúng tôi khuyến nghị đóng view khi gặp các lỗi như vậy, đây cũng chính là hành vi mặc định tích hợp sẵn của SDK khi không có observer nào được thiết lập:

void flowViewDidReceiveError(AdaptyUIFlowView view, AdaptyError error) {
  // log the error and dismiss the broken view
  view.dismiss();
}

Trong trường hợp bình thường, lỗi hiển thị 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.

Sự kiện phân tích

Phương thức tùy chọn flowViewDidReceiveAnalyticEvent được dành riêng cho các sự kiện phân tích tùy chỉnh từ một flow. Hiện tại, các flow chưa phát các sự kiện này đến code của bạn, vì vậy bạn chưa cần triển khai nó.

Xử lý giao dịch mua hàng trong Observer mode

Nếu bạn đã kích hoạt SDK ở chế độ Observer mode và hiển thị một flow hoặc paywall do Adapty render, SDK sẽ không thực hiện giao dịch 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 AdaptyUIObserverModeResolver của bạn. Xem Hiển thị flow trong Observer mode để biết cách thiết lập đầy đủ.

Xử lý các yêu cầu hệ thống

AdaptyUISystemRequestsHandler (đăng ký qua AdaptyUI().setSystemRequestsHandler(...)) được dành riêng cho các yêu cầu hệ thống từ một flow: các lời nhắc cấp quyền của hệ điều hành (như thông báo đẩy hoặc quyền truy cập camera) và yêu cầu đánh giá trên App Store. Hiện tại các flow chưa kích hoạt những yêu cầu này, nên bạn chưa cần đăng ký handler. Nếu bạn có đăng ký một cái, hãy lưu ý rằng handlePermission là phương thức bắt buộc của class — hãy yêu cầu quyền bằng code của riêng bạn, sau đó trả về AdaptyUIPermissionResult.granted() hoặc AdaptyUIPermissionResult.denied(); còn handleAppReviewRequest là tùy chọn.

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, chọn sản phẩm và hiển thị paywall. Bạn cũng cần triển khai xử lý nút (đóng paywall, mở liên kết, v.v.). Xem hướng dẫn xử lý hành động nút để 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 các lần 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.

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.

Để kiểm soát hoặc theo dõi các sự kiện xảy ra trên màn hình paywall trong ứng dụng di động của bạn, hãy implement các phương thức AdaptyUIPaywallsEventsObserver và đặt observer trước khi hiển thị bất kỳ màn hình nào:

AdaptyUI().setPaywallsEventsObserver(this);

Muốn xem ví dụ thực tế về cách tích hợp Adapty SDK vào ứng dụng di động? Hãy xem ứng dụng mẫu của chúng tôi, nơi minh họa toàn bộ quá trình thiết lập, bao gồm hiển thị paywall, thực hiện mua hàng và các chức năng cơ bản khác.

Các ví dụ về sự kiện bên dưới hiển thị các thuộc tính có sẵn trên mỗi đối tượng, cùng với các giá trị minh họa trong phần chú thích.

Sự kiện do người dùng tạo ra

Paywall xuất hiện

Phương thức này được gọi khi màn hình paywall hiển thị trên màn hình.

Trên iOS, cũng được gọi khi người dùng nhấn nút web paywall bên trong một paywall, và web paywall mở ra trong trình duyệt trong ứng dụng.

void paywallViewDidAppear(AdaptyUIPaywallView view) {
}

Paywall biến mất

Phương thức này được gọi khi màn hình paywall bị đóng khỏi màn hình.

Trên iOS, cũng được gọi khi một web paywall mở từ một paywall trong trình duyệt trong ứng dụng biến mất khỏi màn hình.

void paywallViewDidDisappear(AdaptyUIPaywallView view) {
}

Chọn sản phẩm

Nếu một sản phẩm được chọn để mua (bởi người dùng hoặc bởi hệ thống), phương thức này sẽ được gọi:

void paywallViewDidSelectProduct(AdaptyUIPaywallView view, String productId) {
}
Ví dụ sự kiện (Nhấn để mở rộng)
void paywallViewDidSelectProduct(AdaptyUIPaywallView view, String productId) {
  // productId is a String:
  productId; // 'premium_monthly'
}

Bắt đầu mua hàng

Khi người dùng bắt đầu quá trình mua hàng, phương thức này sẽ được gọi:

void paywallViewDidStartPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product) {
}
Ví dụ sự kiện (Nhấn để mở rộng)
void paywallViewDidStartPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product) {
  // product — AdaptyPaywallProduct:
  product.vendorProductId;        // 'premium_monthly'
  product.localizedTitle;         // 'Premium Monthly'
  product.localizedDescription;   // 'Premium subscription for 1 month'
  product.price.amount;           // 9.99            (double)
  product.price.currencyCode;     // 'USD'
  product.price.localizedString;  // '$9.99'
}

Hoàn tất thanh toán

Phương thức này được gọi khi giao dịch mua thành công, người dùng hủy giao dịch, hoặc giao dịch đang ở trạng thái chờ xử lý:

void paywallViewDidFinishPurchase(AdaptyUIPaywallView view, 
                                  AdaptyPaywallProduct product, 
                                  AdaptyPurchaseResult purchaseResult) {
    switch (purchaseResult) {
      case AdaptyPurchaseResultSuccess(profile: final profile):
        // successful purchase
        break;
      case AdaptyPurchaseResultPending():
        // purchase is pending
        break;
      case AdaptyPurchaseResultUserCancelled():
        // user cancelled the purchase
        break;
      default:
        break;
    }
}
Ví dụ sự kiện (Nhấn để mở rộng)
void paywallViewDidFinishPurchase(AdaptyUIPaywallView view,
                                  AdaptyPaywallProduct product,
                                  AdaptyPurchaseResult purchaseResult) {
  // product — AdaptyPaywallProduct:
  product.vendorProductId; // 'premium_monthly'

  switch (purchaseResult) {
    case AdaptyPurchaseResultSuccess(profile: final profile):
      // profile — AdaptyProfile:
      profile.accessLevels['premium']?.isActive;  // true
      profile.accessLevels['premium']?.expiresAt; // DateTime(2027, 2, 15, 10, 30)
      break;
    case AdaptyPurchaseResultPending():
      // no additional data
      break;
    case AdaptyPurchaseResultUserCancelled():
      // no additional data
      break;
  }
}

Chúng tôi khuyến nghị đóng màn hình trong trường hợp đó. Tham khảo Phản hồi các hành động nút để biết chi tiết về cách đóng màn hình paywall.

Hoàn tất điều hướng thanh toán web

Phương thức này được gọi sau khi có lần thử mở web paywall cho một sản phẩm cụ thể. Điều này bao gồm cả các lần điều hướng thành công và thất bại:

void paywallViewDidFinishWebPaymentNavigation(AdaptyUIPaywallView view, 
                                               AdaptyPaywallProduct? product, 
                                               AdaptyError? error) {
}

Tham số:

Tham sốMô tả
productMột AdaptyPaywallProduct mà web paywall được mở cho. Có thể là null.
errorMột đối tượng AdaptyError nếu điều hướng web paywall thất bại; null nếu điều hướng thành công.
Ví dụ về sự kiện (Nhấn để mở rộng)
void paywallViewDidFinishWebPaymentNavigation(AdaptyUIPaywallView view,
                                               AdaptyPaywallProduct? product,
                                               AdaptyError? error) {
  // product — AdaptyPaywallProduct?:
  product?.vendorProductId; // 'premium_monthly'

  if (error == null) {
    // navigation succeeded
  } else {
    // error — AdaptyError:
    error.code;    // AdaptyErrorCode.networkFailed (2005)
    error.message; // 'Network request failed'
    error.detail;  // platform-specific underlying error, or null
  }
}

Mua hàng thất bại

Phương thức này được gọi khi một giao dịch mua thất bại (ví dụ: do lỗi thanh toán hoặc lỗi mạng). Nó không kích hoạt khi người dùng chủ động huỷ hoặc giao dịch đang chờ xử lý — những trường hợp đó được xử lý bởi paywallViewDidFinishPurchase:

void paywallViewDidFailPurchase(AdaptyUIPaywallView view, 
                                AdaptyPaywallProduct product, 
                                AdaptyError error) {
}
Ví dụ sự kiện (Nhấn để mở rộng)
void paywallViewDidFailPurchase(AdaptyUIPaywallView view,
                                AdaptyPaywallProduct product,
                                AdaptyError error) {
  // product — AdaptyPaywallProduct:
  product.vendorProductId; // 'premium_monthly'

  // error — AdaptyError:
  error.code;    // AdaptyErrorCode.productPurchaseFailed (1006)
  error.message; // 'Product purchase failed.'
  error.detail;  // platform-specific underlying error, or null
}

Bắt đầu khôi phục

Nếu người dùng khởi động quá trình khôi phục, phương thức này sẽ được gọi:

void paywallViewDidStartRestore(AdaptyUIPaywallView view) {
}

Khôi phục thành công

Nếu việc khôi phục giao dịch thành công, phương thức này sẽ được gọi:

void paywallViewDidFinishRestore(AdaptyUIPaywallView view, AdaptyProfile profile) {
}
Ví dụ sự kiện (Nhấn để mở rộng)
void paywallViewDidFinishRestore(AdaptyUIPaywallView view, AdaptyProfile profile) {
  // profile — AdaptyProfile:
  profile.accessLevels['premium']?.isActive;            // true
  profile.accessLevels['premium']?.expiresAt;           // DateTime(2027, 2, 15, 10, 30)
  profile.subscriptions['premium_monthly']?.isActive;   // true
  profile.subscriptions['premium_monthly']?.expiresAt;  // DateTime(2027, 2, 15, 10, 30)
}

Chúng tôi khuyến nghị đóng màn hình nếu người dùng đã có accessLevel yêu cầu. Tham khảo chủ đề Trạng thái gói đăng ký để biết cách kiểm tra và chủ đề Xử lý hành động nút để biết cách đóng màn hình paywall.

Khôi phục thất bại

Nếu việc khôi phục giao dịch thất bại, phương thức này sẽ được gọi:

void paywallViewDidFailRestore(AdaptyUIPaywallView view, AdaptyError error) {
}
Ví dụ sự kiện (Nhấp để mở rộng)
void paywallViewDidFailRestore(AdaptyUIPaywallView view, AdaptyError error) {
  // error — AdaptyError:
  error.code;    // AdaptyErrorCode.receiveRestoredTransactionsFailed (1011)
  error.message; // 'Error occurred in the process of restoring purchases.'
  error.detail;  // platform-specific underlying error, or null
}

Tải dữ liệu và hiển thị

Lỗi tải sản phẩm

Nếu bạn không truyền mảng sản phẩm trong quá trình khởi tạo, AdaptyUI sẽ tự động lấy các đối tượng cần thiết từ server. Nếu thao tác này thất bại, AdaptyUI sẽ báo lỗi bằng cách gọi phương thức sau:

void paywallViewDidFailLoadingProducts(AdaptyUIPaywallView view, AdaptyError error) {
}
Ví dụ sự kiện (Nhấn để mở rộng)
void paywallViewDidFailLoadingProducts(AdaptyUIPaywallView view, AdaptyError error) {
  // error — AdaptyError:
  error.code;    // AdaptyErrorCode.productRequestFailed (1002)
  error.message; // 'Unable to fetch available In-App Purchase products at the moment.'
  error.detail;  // platform-specific underlying error, or null
}

Lỗi khi hiển thị

Nếu xảy ra lỗi trong quá trình render giao diện, lỗi đó sẽ được báo cáo thông qua phương thức này. Theo mặc định (từ v3.15.2), paywall sẽ tự động bị đóng khi gặp lỗi render, nhưng bạn có thể ghi đè hành vi này nếu cần.

void paywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error) {
  // Default behavior: view.dismiss()
  // Override with custom logic if needed, for example:
  // - Log the error
  // - Show an error message to the user
}
Ví dụ sự kiện (Nhấn để mở rộng)
void paywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error) {
  // error — AdaptyError:
  error.code;    // AdaptyErrorCode.jsException (4105)
  error.message; // 'An exception was thrown from JS during AdaptyUI flow execution.'
  error.detail;  // platform-specific underlying error, or null

  // Default behavior: view.dismiss()
}

Trong trường hợp bình thường, 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.