Use this page with AI Copy this into your coding assistant and add your request.
Read https://openiap.dev/docs/events/purchase-error-listener and https://openiap.dev/llms.txt. Follow the reading instructions, detailed reference, and linked guides relevant to my task before making changes.
Inspect my existing project and reuse its framework and conventions. Ask me for missing product decisions. Implement the requested behavior and run the applicable checks.
Show the working result, the commands and actual test results, and any remaining limitations. Keep your explanation brief.
My request: [describe what customers should be able to do]See an example request → purchaseErrorListener Fired when a purchase fails or is cancelled by the user.
Listener Setup // AsyncSequence approach
var purchaseErrors: AsyncStream<PurchaseError>
// Combine approach
var purchaseErrorPublisher: AnyPublisher<PurchaseError, Never>Kotlin example // Flow approach
val purchaseErrors: Flow<PurchaseError>TypeScript example purchaseErrorListener(
listener: (error: PurchaseError) => void
): SubscriptionDart example Stream<PurchaseError> get purchaseErrorListener;C# (MAUI) example using OpenIap;
using OpenIap.Maui;
using System;
IObservable<PurchaseError> purchaseErrors = OpenIapClient.Instance.PurchaseError;Kotlin (KMP) example // Flow approach
val purchaseErrors: Flow<PurchaseError>Registers a listener for purchase error events.
import OpenIap
let subscription = OpenIapModule.shared.purchaseErrorListener { error in
Task {
print("Purchase error: \(error.code) - \(error.message)")
switch error.code {
case .userCancelled:
// User cancelled - no action needed
break
case .alreadyOwned:
// Restore purchases instead
try await OpenIapModule.shared.restorePurchases()
case .networkError:
showRetryDialog()
default:
showErrorMessage(error.message)
}
}
}
// Cleanup when done
OpenIapModule.shared.removeListener(subscription)Kotlin example // OpenIapStore exposes the latest typed error through status.
lifecycleScope.launch {
openIapStore.status.collect { status ->
val error = status.lastError ?: return@collect
println("Purchase error: ${error.code} - ${error.message}")
when (error.code) {
"user-cancelled" -> {
// User cancelled - no action needed
}
"already-owned" -> {
// Restore purchases instead
openIapStore.restorePurchases()
}
"network-error" -> {
showRetryDialog()
}
else -> {
showErrorMessage(error.message)
}
}
}
}TypeScript example import {
purchaseErrorListener,
ErrorCode,
restorePurchases,
} from 'expo-iap';
// showRetryDialog / showErrorMessage are user-defined UI helpers.
const subscription = purchaseErrorListener((error) => {
console.log('Purchase error:', error.code, error.message);
switch (error.code) {
case ErrorCode.UserCancelled:
// User cancelled - no action needed
break;
case ErrorCode.AlreadyOwned:
// Restore purchases instead
restorePurchases();
break;
case ErrorCode.NetworkError:
// Show retry option
showRetryDialog();
break;
default:
showErrorMessage(error.message);
}
});
// Cleanup when done
subscription.remove();Dart example import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
final subscription = FlutterInappPurchase.instance.purchaseErrorListener.listen((error) {
print('Purchase error: ${error.code} - ${error.message}');
switch (error.code) {
case ErrorCode.UserCancelled:
// User cancelled - no action needed
break;
case ErrorCode.AlreadyOwned:
// Restore purchases instead
FlutterInappPurchase.instance.restorePurchases();
break;
case ErrorCode.NetworkError:
showRetryDialog();
break;
default:
showErrorMessage(error.message);
}
});
// Cleanup when done
subscription.cancel();C# (MAUI) example using OpenIap;
using OpenIap.Maui;
async Task HandlePurchaseErrorSafelyAsync(PurchaseError error)
{
try
{
Console.WriteLine(
quot;Purchase error: {error.Code} - {error.Message}");
switch (error.Code)
{
case ErrorCode.UserCancelled:
break;
case ErrorCode.AlreadyOwned:
await ((MutationResolver)OpenIapClient.Instance).RestorePurchasesAsync();
break;
case ErrorCode.NetworkError:
ShowRetryDialog();
break;
default:
ShowErrorMessage(error.Message);
break;
}
}
catch (Exception failure)
{
Console.WriteLine(
quot;Purchase error recovery failed: {failure.Message}");
}
}
var subscription = OpenIapClient.Instance.PurchaseError.Subscribe(
error => _ = HandlePurchaseErrorSafelyAsync(error));
// Cleanup when done.
subscription.Dispose();Kotlin (KMP) example import io.github.hyochan.kmpiap.KmpIAP
import io.github.hyochan.kmpiap.openiap.ErrorCode
val kmpIAP = KmpIAP()
// Using Flow
lifecycleScope.launch {
kmpIAP.purchaseErrorListener.collect { error ->
println("Purchase error: ${error.code} - ${error.message}")
when (error.code) {
ErrorCode.UserCancelled -> {
// User cancelled - no action needed
}
ErrorCode.AlreadyOwned -> {
// Restore purchases instead
kmpIAP.restorePurchases()
}
ErrorCode.NetworkError -> {
showRetryDialog()
}
else -> {
showErrorMessage(error.message)
}
}
}
}Error Payload The error event delivers a PurchaseError object with error details. See Error Codes for complete reference.
Error Handling Strategy Handle errors based on their error codes :
UserCancelled - No action requiredItemUnavailable - Check product availabilityNetworkError - Retry with backoffAlreadyOwned - Restore purchasesPurchaseVerificationFailed - Retry verification