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

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 phải triển khai xử lý nút (đóng flow, mở liên kết, v.v.). Xem hướng dẫn xử lý hành động trong flow để biết thêm chi tiết.

Flow và paywall được cấu hình bằng Flow Builder hoặc 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, 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. Tìm hiểu cách phản hồi những 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 trong ứng dụng di động của bạn, hãy triển khai các phương thức của interface AdaptyUIFlowsEventsObserver và đăng ký observer với AdaptyUI.setFlowsEventsObserver(). Một số phương thức có sẵn các implementation mặc định xử lý các tình huống phổ biến một cách tự động, vì vậy chỉ cần override những phương thức bạn muốn thay đổi:

AdaptyUI.setFlowsEventsObserver(object : AdaptyUIFlowsEventsObserver {
    // override only the methods you want to change
})

Đây là nơi bạn thêm logic tùy chỉnh để xử lý các sự kiện flow. Bạn có thể dùng view.dismiss() để đóng flow, hoặc triển khai bất kỳ hành vi tùy chỉnh nào khác bạn cần. Lưu ý rằng dismiss() là một suspend function — bên trong callback, hãy khởi chạy nó trên mainUiScope của observer: mainUiScope.launch { view.dismiss() }.

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

Flow xuất hiện và biến mất

Khi một flow xuất hiện hoặc biến mất, các phương thức sau sẽ được gọi:

override fun flowViewDidAppear(view: AdaptyUIFlowView) {
    // Handle flow appearance
    // You can track analytics or update UI here
}

override fun flowViewDidDisappear(view: AdaptyUIFlowView) {
    // Handle flow disappearance
    // You can track analytics or update UI here
}
  • Trên iOS, flowViewDidAppear cũng được gọi khi người dùng nhấn vào nút web paywall bên trong một flow, và web paywall mở trong trình duyệt trong ứng dụng.
  • Trên iOS, flowViewDidDisappear cũng được gọi khi một web paywall được mở từ một flow trong trình duyệt trong ứng dụng biến mất khỏi màn hình.
Ví dụ sự kiện (Nhấn để mở rộng)
// Flow appeared
{
  // No additional data
}

// Flow disappeared
{
  // No additional data
}

Chọn sản phẩm

Nếu người dùng chọn một sản phẩm để mua, phương thức này sẽ được gọi:

override fun flowViewDidSelectProduct(view: AdaptyUIFlowView, productId: String) {
    // Handle product selection
    // You can update UI or track analytics here
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
  "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:

override fun flowViewDidStartPurchase(view: AdaptyUIFlowView, product: AdaptyPaywallProduct) {
    // Handle purchase start
    // You can show loading indicators or track analytics here
}

Trong chế độ Observer, các giao dịch mua được bắt đầu từ một flow sẽ được chuyển đến AdaptyUIObserverModeResolver của bạn.

Ví dụ sự kiện (Nhấn để mở rộng)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

Mua hàng thành công, bị hủy, hoặc đang chờ xử lý

Phương thức này sẽ được gọi khi một giao dịch mua hoàn tất. Mặc định, nó không làm gì cả — flow vẫn mở sau khi mua hàng cho đến khi bạn tự đóng nó, vì vậy hãy tự gọi view.dismiss() sau khi người dùng được cấp quyền truy cập:

override fun flowViewDidFinishPurchase(
    view: AdaptyUIFlowView,
    product: AdaptyPaywallProduct,
    purchaseResult: AdaptyPurchaseResult
) {
    when (purchaseResult) {
        is AdaptyPurchaseResult.Success -> {
            // Check if user has access to premium features
            if (purchaseResult.profile.accessLevels["premium"]?.isActive == true) {
                mainUiScope.launch { view.dismiss() }
            }
        }
        AdaptyPurchaseResult.Pending -> {
            // Handle pending purchase (e.g., user will pay offline with cash)
        }
        AdaptyPurchaseResult.UserCanceled -> {
            // Handle user cancellation
        }
    }
}
Ví dụ sự kiện (Nhấn để mở rộng)
// Successful purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "Success",
    "profile": {
      "accessLevels": {
        "premium": {
          "id": "premium",
          "isActive": true,
          "expiresAt": "2024-02-15T10:30:00Z"
        }
      }
    }
  }
}

// Pending purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "Pending"
  }
}

// User canceled purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "UserCanceled"
  }
}

Chúng tôi khuyến nghị đóng màn hình flow khi mua hàng thành công.

Mua hàng thất bại

Phương thức này sẽ được gọi khi một giao dịch mua hàng thất bại do lỗi. Điều này bao gồm các lỗi từ StoreKit/Google Play Billing (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 và lỗi hệ thống. Lưu ý rằng khi người dùng hủy, flowViewDidFinishPurchase sẽ được gọi với kết quả bị hủy thay vì phương thức này, và các thanh toán đang chờ xử lý cũng không kích hoạt phương thức này.

override fun flowViewDidFailPurchase(
    view: AdaptyUIFlowView,
    product: AdaptyPaywallProduct,
    error: AdaptyError
) {
    // Add your purchase failure handling logic here
    // For example: show error message, retry option, or custom error handling
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": {
    "code": "purchase_failed",
    "message": "Purchase failed due to insufficient funds",
    "details": {
      "underlyingError": "Insufficient funds in account"
    }
  }
}

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:

override fun flowViewDidStartRestore(view: AdaptyUIFlowView) {
    // Handle restore start
    // You can show loading indicators or track analytics here
}

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

Nếu việc khôi phục giao dịch mua thành công, phương thức này sẽ được gọi. Theo mặc định, nó không làm gì — flow vẫn mở sau khi khôi phục cho đến khi bạn đóng nó:

override fun flowViewDidFinishRestore(view: AdaptyUIFlowView, profile: AdaptyProfile) {
    // Add your successful restore handling logic here
    // For example: show success message, update UI, or dismiss the flow

    // Check if user has access to premium features
    if (profile.accessLevels["premium"]?.isActive == true) {
        mainUiScope.launch { view.dismiss() }
    }
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
  "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"
      }
    ]
  }
}

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.

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

Nếu Adapty.restorePurchases() thất bại, phương thức này sẽ được gọi:

override fun flowViewDidFailRestore(view: AdaptyUIFlowView, error: AdaptyError) {
    // Add your restore failure handling logic here
    // For example: show error message, retry option, or custom error handling
}
Ví dụ về sự kiện (Nhấp để mở rộng)
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

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

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

override fun flowViewDidFinishWebPaymentNavigation(
    view: AdaptyUIFlowView,
    product: AdaptyPaywallProduct?,
    error: AdaptyError?
) {
    if (error != null) {
        // Handle web payment navigation error
    } else {
        // Handle successful web payment navigation
    }
}
Ví dụ về sự kiện (Nhấn để mở rộng)
// Successful web payment navigation
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": null
}

// Failed web payment navigation
{
  "product": null,
  "error": {
    "code": "web_payment_failed",
    "message": "Web payment navigation failed",
    "details": {
      "underlyingError": "Network connection 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 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:

override fun flowViewDidFailLoadingProducts(view: AdaptyUIFlowView, error: AdaptyError) {
    // Add your product loading failure handling logic here
    // For example: show error message, retry option, or custom error handling
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

Lỗi hiển thị và lỗi runtime

Nếu xảy ra lỗi trong quá trình hiển thị giao diện, hoặc một lỗi runtime không liên quan đến việc mua hàng, lỗi đó sẽ được báo cáo qua phương thức này. Theo mặc định, flow sẽ bị đóng khi có lỗi — ghi đè phương thức này để giữ flow mở hoặc thêm cách xử lý riêng của bạn:

override fun flowViewDidReceiveError(view: AdaptyUIFlowView, error: AdaptyError) {
    // Handle the error
    // The default implementation dismisses the flow;
    // once you override this method, dismissal is up to you
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
    "error": {
        "code": "rendering_failed",
            "message": "Failed to render flow interface",
            "details": {
            "underlyingError": "Invalid flow configuration"
        }
    }
}

Trong tình huống bình thường, những 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.

Sự kiện analytics

Callback flowViewDidReceiveAnalyticEvent được dành cho các sự kiện analytics tùy chỉnh từ một flow. Các flow chưa phát ra những sự kiện này đến code của bạn, vì vậy bạn không cần triển khai nó:

override fun flowViewDidReceiveAnalyticEvent(
    view: AdaptyUIFlowView,
    name: String,
    paramsJsonString: String
) {
    // Reserved for custom analytic events from a flow
}

Nút Back hệ thống Android

Mặc định, một flow không thể bị đóng bằng nút back hệ thống Android hoặc thao tác vuốt để quay lại — cài đặt mặc định của flowViewDidPerformAction chỉ đóng flow khi nhận CloseAction và bỏ qua AndroidSystemBackAction, do đó người dùng chỉ rời khỏi flow qua con đường bạn định nghĩa, chẳng hạn như nút Close hoặc action on_device_back trong builder. Nếu muốn nút back hệ thống có thể đóng flow, hãy tự xử lý action đó:

override fun flowViewDidPerformAction(view: AdaptyUIFlowView, action: AdaptyUIAction) {
    when (action) {
        is AdaptyUIAction.CloseAction ->
            mainUiScope.launch { view.dismiss() } // default behavior
        is AdaptyUIAction.AndroidSystemBackAction ->
            mainUiScope.launch { view.dismiss() } // not handled by default
        is AdaptyUIAction.OpenUrlAction ->
            AdaptyUI.openWebUrl(action.url, action.openIn) // default behavior
        else -> Unit
    }
}

Xem hướng dẫn xử lý các hành động flow để biết danh sách đầy đủ các hành động.

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, 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 được thực hiện trên paywall. Tìm hiểu 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.

Để 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 của bạn, hãy implement các phương thức của interface AdaptyUIPaywallsEventsObserver. Một số phương thức có implementation mặc định xử lý các trường hợp phổ biến một cách tự động.

Đây là nơi bạn thêm logic tùy chỉnh để phản hồi các sự kiện trên paywall. Bạn có thể dùng view.dismiss() để đóng paywall, hoặc implement bất kỳ hành vi tùy chỉnh nào khác mà bạn cần.

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

Giao diện paywall hiện và ẩn

Khi paywall hiện ra hoặc ẩn đi, các phương thức sau sẽ được gọi:

override fun paywallViewDidAppear(view: AdaptyUIPaywallView) {
    // Handle paywall appearance
    // You can track analytics or update UI here
}

override fun paywallViewDidDisappear(view: AdaptyUIPaywallView) {
    // Handle paywall disappearance
    // You can track analytics or update UI here
}
  • Trên iOS, paywallViewDidAppear 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ở trong trình duyệt trong ứng dụng.
  • Trên iOS, paywallViewDidDisappear cũng được gọi khi một web paywall được mở từ paywall trong trình duyệt trong ứng dụng biến mất khỏi màn hình.
Ví dụ về sự kiện (Nhấp để mở rộng)
// Paywall appeared
{
  // No additional data
}

// Paywall disappeared
{
  // No additional data
}

Chọn sản phẩm

Nếu người dùng chọn một sản phẩm để mua, phương thức này sẽ được gọi:

override fun paywallViewDidSelectProduct(view: AdaptyUIPaywallView, productId: String) {
    // Handle product selection
    // You can update UI or track analytics here
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
  "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:

override fun paywallViewDidStartPurchase(view: AdaptyUIPaywallView, product: AdaptyPaywallProduct) {
    // Handle purchase start
    // You can show loading indicators or track analytics here
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

Mua hàng thành công, bị hủy hoặc đang chờ xử lý

Nếu một giao dịch mua thành công, phương thức này sẽ được gọi. Theo mặc định, nó sẽ tự động đóng paywall trừ khi giao dịch bị người dùng hủy:

override fun paywallViewDidFinishPurchase(
    view: AdaptyUIPaywallView,
    product: AdaptyPaywallProduct,
    purchaseResult: AdaptyPurchaseResult
) {
    when (purchaseResult) {
        is AdaptyPurchaseResult.Success -> {
            // Check if user has access to premium features
            if (purchaseResult.profile.accessLevels["premium"]?.isActive == true) {
                view.dismiss()
            }
        }
        AdaptyPurchaseResult.Pending -> {
            // Handle pending purchase (e.g., user will pay offline with cash)
        }
        AdaptyPurchaseResult.UserCanceled -> {
            // Handle user cancellation
        }
    }
}
Ví dụ sự kiện (Nhấn để mở rộng)
// Successful purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "Success",
    "profile": {
      "accessLevels": {
        "premium": {
          "id": "premium",
          "isActive": true,
          "expiresAt": "2024-02-15T10:30:00Z"
        }
      }
    }
  }
}

// Pending purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "Pending"
  }
}

// User canceled purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "UserCanceled"
  }
}

Chúng tôi khuyến nghị đóng màn hình paywall khi mua hàng thành công.

Mua hàng thất bại

Nếu giao dịch mua hàng thất bại do lỗi, phương thức này sẽ được gọi. Điều này bao gồm các lỗi từ StoreKit/Google Play Billing (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 và lỗi hệ thống. Lưu ý rằng khi người dùng hủy giao dịch, paywallViewDidFinishPurchase sẽ được gọi với kết quả đã hủy, còn các khoản thanh toán đang chờ xử lý sẽ không kích hoạt phương thức này.

override fun paywallViewDidFailPurchase(
    view: AdaptyUIPaywallView,
    product: AdaptyPaywallProduct,
    error: AdaptyError
) {
    // Add your purchase failure handling logic here
    // For example: show error message, retry option, or custom error handling
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": {
    "code": "purchase_failed",
    "message": "Purchase failed due to insufficient funds",
    "details": {
      "underlyingError": "Insufficient funds in account"
    }
  }
}

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:

override fun paywallViewDidStartRestore(view: AdaptyUIPaywallView) {
    // Handle restore start
    // You can show loading indicators or track analytics here
}

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

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

override fun paywallViewDidFinishRestore(view: AdaptyUIPaywallView, profile: AdaptyProfile) {
    // Add your successful restore handling logic here
    // For example: show success message, update UI, or dismiss paywall
    
    // Check if user has access to premium features
    if (profile.accessLevels["premium"]?.isActive == true) {
        view.dismiss()
    }
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
  "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"
      }
    ]
  }
}

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

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

Nếu Adapty.restorePurchases() thất bại, phương thức này sẽ được gọi:

override fun paywallViewDidFailRestore(view: AdaptyUIPaywallView, error: AdaptyError) {
    // Add your restore failure handling logic here
    // For example: show error message, retry option, or custom error handling
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

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

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

override fun paywallViewDidFinishWebPaymentNavigation(
    view: AdaptyUIPaywallView,
    product: AdaptyPaywallProduct?,
    error: AdaptyError?
) {
    if (error != null) {
        // Handle web payment navigation error
    } else {
        // Handle successful web payment navigation
    }
}
Ví dụ về sự kiện (Nhấp để mở rộng)
// Successful web payment navigation
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": null
}

// Failed web payment navigation
{
  "product": null,
  "error": {
    "code": "web_payment_failed",
    "message": "Web payment navigation failed",
    "details": {
      "underlyingError": "Network connection error"
    }
  }
}

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

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

Nếu bạn không truyền các sản phẩm trong quá trình khởi tạo, AdaptyUI sẽ tự 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 này:

override fun paywallViewDidFailLoadingProducts(view: AdaptyUIPaywallView, error: AdaptyError) {
    // Add your product loading failure handling logic here
    // For example: show error message, retry option, or custom error handling
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

Lỗi hiển thị

Nếu có lỗi xảy ra trong quá trình hiển thị giao diện, lỗi đó sẽ được báo cáo qua phương thức này:

override fun paywallViewDidFailRendering(view: AdaptyUIPaywallView, error: AdaptyError) {
    // Handle rendering error
    // In a normal situation, such errors should not occur
    // If you come across one, please let us know
}
Ví dụ sự kiện (Nhấn để mở rộng)
{
    "error": {
        "code": "rendering_failed",
            "message": "Failed to render paywall interface",
            "details": {
            "underlyingError": "Invalid paywall configuration"
        }
    }
}

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, vui lòng thông báo cho chúng tôi.