# Revenue

> Track in-app purchases and custom payments with the Grovs iOS SDK.

Source: https://docs.grovs.io/docs/sdk/ios/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**. Currency conversion rates may show minor discrepancies.

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

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

Revenue is collected only while this setting is on. 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 App Store Server Notifications

> **Warning:** **Required.** In-app purchase tracking doesn't work without this step. Grovs gets transaction data from Apple through App Store Server Notifications.

In [App Store Connect](https://appstoreconnect.apple.com):

1. Select your app and open **App Information**.
2. Scroll to **App Store Server Notifications**.
3. Set **Production Server URL** to the URL in the Grovs dashboard under **Developers → iOS Setup → Revenue**.
4. Set **Sandbox Server URL** to the sandbox URL in the same section.

Already handle these notifications on your server? Forward the events to the Grovs URLs instead.

## Logging purchases

Use `logInAppPurchase` for Apple In-App Purchases and `logCustomPurchase` for any other payment flow.

### App Store purchases (StoreKit 2)

Pass the StoreKit 2 transaction ID:

```swift
import StoreKit

// After a successful purchase
let result = try await Product.purchase(...)

if case .success(let verification) = result,
   case .verified(let transaction) = verification {

    Grovs.logInAppPurchase(transactionID: transaction.id) { success in
        if success {
            // Revenue event recorded
            Task { await transaction.finish() }
        }
    }
}
```

The SDK sends only the transaction ID. Grovs looks it up through the App Store Server API, using the key configured in the dashboard, and fills in product, price, currency and dates. App Store Server Notifications then keep renewals, cancellations and refunds current, so both setup steps are required. An already-logged transaction ID is skipped, so repeat calls never double-count.

The SDK method runs on iOS 13. The StoreKit 2 code that provides `transaction.id` needs iOS 15.

### Completion result

Both methods save the purchase on the device before sending it, including before authentication completes. The completion gets `true` once it's sent. Once the SDK is configured and enabled, `false` means the purchase is kept and retried at later foregrounds (up to 20 attempts), so don't log it again: a second `logCustomPurchase` call is a second purchase. Before `configure`, or while the SDK is disabled, nothing is saved and the completion gets `false`.

### Custom purchases

For purchases through your own server, Stripe or any non-IAP system:

```swift
Grovs.logCustomPurchase(
    type: .buy,
    priceInCents: 999,       // $9.99
    currency: "USD",
    productID: "premium_monthly"
) { success in
    // Revenue event recorded
}
```

**Parameters:**

| Parameter      | Type              | Description                                    |
| -------------- | ----------------- | ---------------------------------------------- |
| `type`         | `TransactionType` | `.buy`, `.cancel`, or `.refund`                |
| `priceInCents` | `Int`             | Amount in cents (e.g. 999 for $9.99)           |
| `currency`     | `String`          | ISO 4217 currency code (e.g. `"USD"`, `"EUR"`) |
| `productID`    | `String`          | Your product identifier                        |
| `startDate`    | `Date?`           | Transaction date. Defaults to now              |

### Tracking cancellations and refunds

Log cancellations and refunds with `.cancel` and `.refund`:

```swift
// Log a cancellation
Grovs.logCustomPurchase(
    type: .cancel,
    priceInCents: 999,
    currency: "USD",
    productID: "premium_monthly"
) { success in }

// Log a refund
Grovs.logCustomPurchase(
    type: .refund,
    priceInCents: 999,
    currency: "USD",
    productID: "premium_monthly"
) { success in }
```

App Store cancellations and refunds are detected automatically through App Store Server Notifications.

## Dashboard metrics

| Metric                              | Meaning                                    |
| ----------------------------------- | ------------------------------------------ |
| **Total Revenue**                   | Aggregate revenue with currency conversion |
| **ARPU**                            | Average revenue per user                   |
| **ARPPU**                           | Average revenue per paying user            |
| **LTV**                             | Lifetime value per product                 |
| **Units Sold**                      | Total purchases                            |
| **First-time vs. Repeat Purchases** | New vs. returning customers                |
| **Cancellations**                   | Cancelled transactions                     |

Filter every metric by date range, platform and product.
