Use this page with AI Copy this into your coding assistant and add your request.
Read https://openiap.dev/docs/apis/finish-transaction 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 → finishTransaction Complete a purchase transaction. Must be called after verifying the purchase to remove it from the queue.
iOS: Calls Transaction.finish(). Until you do, the same transaction replays through Transaction.updates on every app launch. Apple docs . Android: Acknowledges (acknowledgePurchase) for non-consumables/subscriptions or consumes (consumeAsync) for consumables. Google auto-refunds within 3 days if neither runs. Google docs .
Requires an open connection
React Native / Expo: wait for the hook's connected flag, or call initConnection() and require a true result. Android does not connect implicitly; a disconnected call rejects with not-prepared. iOS can connect on demand, but the same gate keeps behavior consistent across platforms.
Signature func finishTransaction(purchase: PurchaseInput, isConsumable: Bool?) async throwsKotlin example suspend fun finishTransaction(purchase: PurchaseInput, isConsumable: Boolean? = null)TypeScript example finishTransaction(args: MutationFinishTransactionArgs): Promise<void>
interface MutationFinishTransactionArgs {
purchase: PurchaseInput;
isConsumable?: boolean | null;
}Dart example Future<void> finishTransaction({
required PurchaseInput purchase,
bool? isConsumable,
});C# (MAUI) example Task<string> FinishTransactionAsync(PurchaseInput purchase, bool? isConsumable = null);Kotlin (KMP) example suspend fun finishTransaction(purchase: PurchaseInput, isConsumable: Boolean? = null)GDScript example func finish_transaction(purchase, is_consumable: bool = false) -> Variant # Types.VoidResultParameters# The transaction to close, plus an optional consumable flag:
purchase (required, Purchase ) — The purchase / transaction to finalize. Must come from requestPurchase or getAvailablePurchases.isConsumable (optional, boolean, default false) — Android. true consumes the token (re-buyable like coins); false just acknowledges (non-consumables, subscriptions). iOS always calls Transaction.finish() regardless.Returns# Promise<void> — Resolves once the platform finalizes the transaction.
Throws# When the platform finalize call fails.
Example try await OpenIapModule.shared.finishTransaction(
purchase: purchase,
isConsumable: false
)Kotlin example openIapStore.finishTransaction(purchase, false) // false: non-consumableTypeScript example // expo-iap
import { finishTransaction, purchaseUpdatedListener } from 'expo-iap';
// Same API in react-native-iap:
// import { finishTransaction, purchaseUpdatedListener } from 'react-native-iap';
purchaseUpdatedListener((purchase) => {
void verifyOnServer(purchase)
.then(async (verified) => {
if (!verified) return;
await grantProduct(purchase.productId);
const isConsumable = purchase.productId.includes('coins');
await finishTransaction({ purchase, isConsumable });
})
.catch((error) => console.warn('Purchase finalization failed:', error));
});
// --- Or via the useIAP() hook (also exported from react-native-iap) ---
// useIAP wires the purchase listener for you; finish the transaction inside
// the onPurchaseSuccess callback.
import { useIAP } from 'expo-iap';
function PurchaseScreen() {
const { finishTransaction } = useIAP({
onPurchaseSuccess: (purchase) => {
void verifyOnServer(purchase)
.then(async (verified) => {
if (!verified) return;
await grantProduct(purchase.productId);
const isConsumable = purchase.productId.includes('coins');
await finishTransaction({ purchase, isConsumable });
})
.catch((error) =>
console.warn('Purchase finalization failed:', error),
);
},
});
return null;
}Dart example await FlutterInappPurchase.instance.finishTransaction(
purchase: purchase,
);C# (MAUI) example using OpenIap;
using OpenIap.Maui;
await ((MutationResolver)OpenIapClient.Instance).FinishTransactionAsync(
purchase: new PurchaseInput(purchase),
isConsumable: true);Kotlin (KMP) example kmpIAP.finishTransaction(purchase, isConsumable = false)
// --- Or via the DSL API ---
// requestPurchase { } returns a Purchase you pass through
// .toPurchaseInput() into finishTransaction. Use isConsumable = true for
// consumables, false for subscriptions / non-consumables.
val purchase = kmpIAP.requestPurchase {
apple { sku = "com.app.coins_100" }
google { skus = listOf("com.app.coins_100") }
}
// After server-side validation:
kmpIAP.finishTransaction(
purchase = purchase.toPurchaseInput(),
isConsumable = true
)GDScript example await iap.finish_transaction(purchase, false)Critical
Android purchases must be acknowledged within 3 days or they will be automatically refunded. iOS transactions will replay on every app launch if not finished.