> ## 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.

# onPurchaseComplete

> Callback invoked after Encore completes a native Play Billing purchase

## Signature

```kotlin theme={null}
fun onPurchaseComplete(
    handler: suspend (result: BillingPurchaseResult, productId: String) -> Unit
): Encore
```

## Parameters

| Parameter | Type                                              | Description                                                            |
| :-------- | :------------------------------------------------ | :--------------------------------------------------------------------- |
| `handler` | `suspend (BillingPurchaseResult, String) -> Unit` | Suspend function receiving the `BillingPurchaseResult` and `productId` |

### BillingPurchaseResult

```kotlin theme={null}
data class BillingPurchaseResult(
    val productId: String,
    val purchaseToken: String,
    val orderId: String?,
)
```

## When It Fires

`onPurchaseComplete` fires **only** when:

1. No `onPurchaseRequest` handler is registered
2. Encore successfully completes a native Play Billing purchase
3. The purchase is acknowledged

It does **not** fire when:

* An `onPurchaseRequest` handler is set
* The user cancels the purchase
* The purchase fails

## Usage

### Adapty

```kotlin theme={null}
Encore.shared.onPurchaseComplete { result, productId ->
    Adapty.reportTransaction(result.purchaseToken)
}
```

### Qonversion

```kotlin theme={null}
Encore.shared.onPurchaseComplete { result, productId ->
    Qonversion.shared().syncStoreKit2Purchases()
}
```

<Note>
  RevenueCat and Superwall auto-detect Play Billing transactions when configured in observer mode. They do not need `onPurchaseComplete`.
</Note>

## Chaining

```kotlin theme={null}
Encore.shared
    .onPurchaseComplete { result, productId ->
        Adapty.reportTransaction(result.purchaseToken)
    }
    .onPassthrough { placementId ->
        handleOriginalAction(placementId)
    }
```

## Behavior

* **Survives `reset()`** — app-level infrastructure, not user state
* **Last handler wins** — setting a new handler replaces the previous one
