> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encorekit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# completePurchaseRequest()

> Signal purchase outcome to the Encore SDK

Signals to the native SDK whether the purchase triggered by `onPurchaseRequest` succeeded or failed. This **must** be called in every code path of your `onPurchaseRequest` handler.

<Warning>
  **This call is mandatory.** If you do not call `completePurchaseRequest()` after receiving an `onPurchaseRequest` event, the SDK will remain locked and cannot present future offers until the app restarts.
</Warning>

## Signature

```tsx theme={null}
completePurchaseRequest(success: boolean): Promise<{ success: boolean }>
```

## Parameters

| Name      | Type    | Description                                                             |
| --------- | ------- | ----------------------------------------------------------------------- |
| `success` | boolean | `true` if the purchase succeeded, `false` if it failed or was cancelled |

## Usage

### Standard pattern

```tsx theme={null}
Encore.onPurchaseRequest(async ({ productId }) => {
  try {
    await billingService.purchase(productId);
    await Encore.completePurchaseRequest(true);
  } catch (error) {
    await Encore.completePurchaseRequest(false);
  }
});
```

### Common mistake — missing failure path

```tsx theme={null}
// BAD — if purchase throws, completePurchaseRequest is never called
Encore.onPurchaseRequest(async ({ productId }) => {
  await billingService.purchase(productId);
  await Encore.completePurchaseRequest(true);
  // If purchase() throws, the SDK is locked!
});
```

```tsx theme={null}
// GOOD — always call completePurchaseRequest in both paths
Encore.onPurchaseRequest(async ({ productId }) => {
  try {
    await billingService.purchase(productId);
    await Encore.completePurchaseRequest(true);
  } catch (error) {
    await Encore.completePurchaseRequest(false);
  }
});
```

## Behavior

* **One active request**: Only one purchase request is active at a time. Calling `completePurchaseRequest()` resolves the current request.
* **Idempotent**: Calling it when no request is pending returns `{ success: false }` with an error message — it does not throw.
* **Stale cleanup**: If a new `onPurchaseRequest` fires before the previous was completed, the stale request is automatically failed.

<Info>
  On iOS, failing to complete the request keeps the offer sheet coordinator active, preventing future `show()` calls from presenting. On Android, the purchase coroutine remains suspended indefinitely.
</Info>
