# Revenue

> Track store purchases, Unity IAP purchases and custom payments with the Grovs Unity SDK.

Source: https://docs.grovs.io/docs/sdk/unity/revenue

Log purchases to attribute revenue to links and campaigns, and see ARPU, LTV and product and platform breakdowns in the dashboard. Revenue tracking is in **beta**.

Self-hosted: revenue tracking needs the Enterprise edition (`GROVS_EE=true`). A Community edition backend has no purchase endpoint.

## Setup

### 1. Enable revenue tracking in the dashboard

1. In the [Grovs dashboard](https://app.grovs.io), go to **Analytics → Revenue**.
2. Click **Enable Revenue Tracking**. Only instance admins can turn it on or off.

See [Revenue](https://docs.grovs.io/docs/revenue) for the dashboard and [Goals](https://docs.grovs.io/docs/revenue/goals) for revenue goals.

### 2. Configure platform notifications

- **iOS:** configure App Store Server Notifications. See the [iOS Revenue guide](https://docs.grovs.io/docs/sdk/ios/revenue#2-configure-app-store-server-notifications).
- **Android:** set up Google Play Real-Time Developer Notifications. See the [Android Revenue guide](https://docs.grovs.io/docs/sdk/android/revenue#2-configure-google-play-real-time-notifications).

## Unity IAP

When `com.unity.purchasing` 4.0 or newer is in the project, the `GrovsSDK.IAP` assembly compiles and adds `GrovsUnityIAP`. It reads the Unity IAP receipt and supports the Apple App Store, the Mac App Store and Google Play.

IAP 4, or the IAP 5 `Product` path:

```csharp
using GrovsSDK;
using UnityEngine.Purchasing;

public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
    _ = GrovsUnityIAP.LogPurchaseAsync(args.purchasedProduct);
    return PurchaseProcessingResult.Complete;
}
```

IAP 5 `Order` path (needs `com.unity.purchasing` 5.0 or newer):

```csharp
void OnPurchasePending(PendingOrder order)
{
    // Log before confirming: on iOS the receipt can be null afterwards.
    _ = GrovsUnityIAP.LogOrderAsync(order);
    storeController.ConfirmPurchase(order);
}
```

- On IAP 5 on iOS, log **before** you confirm a consumable. After confirming, the receipt can be null.
- A `null` product or order throws `ArgumentNullException`.
- The task resolves `false`, with a `[Grovs] Purchase not logged.` warning, when the receipt can't be used: invalid JSON, no transaction ID, or another store.

## Store purchases without Unity IAP

Pass the platform's transaction yourself:

```csharp
#if UNITY_IOS
// The StoreKit transaction ID, as a decimal string
bool logged = await Grovs.LogInAppPurchaseAsync(transactionId);
#elif UNITY_ANDROID
// The Play Billing purchase's originalJson
bool logged = await Grovs.LogInAppPurchaseAsync(purchase.originalJson);
#endif
```

- An empty or `null` string throws `ArgumentException`.
- **iOS:** the string must be a decimal number, or the task fails with `InvalidArgument`. The result is the native SDK's result.
- **Android:** the result is `true` once the purchase is handed to the native SDK.

The backend then looks the transaction up, fills in product, price and currency, and de-duplicates repeated calls.

## Custom purchases

For payments outside the stores:

```csharp
bool logged = await Grovs.LogCustomPurchaseAsync(GrovsTransactionType.Buy, 199, "USD", "coins_100");
```

| Parameter      | Type                   | Description                                       |
| -------------- | ---------------------- | ------------------------------------------------- |
| `type`         | `GrovsTransactionType` | `Buy`, `Cancel` or `Refund`                       |
| `priceInCents` | `int`                  | Amount in cents, for example `199` for $1.99      |
| `currency`     | `string`               | ISO 4217 code, for example `"USD"`                |
| `productId`    | `string`               | Your product identifier                           |
| `date`         | `DateTime?`            | Transaction date. `null` (the default) means now. |

A `null` currency or product ID throws `ArgumentNullException`. On Android the result is `true` once the purchase is handed to the native SDK.

Log a cancellation or refund with the same call and `GrovsTransactionType.Cancel` or `GrovsTransactionType.Refund`. App Store and Google Play cancellations and refunds are detected through the platform server notifications.

## Errors

Purchase tasks fail with `GrovsException` when Grovs is disabled (`SdkDisabled`) or not started (`SdkNotInitialized`). In the Editor and on other platforms they resolve `false`.
