decentraland / decentraland/godot-explorer

[IAP] Add purchase-funnel metrics for minimum IAP observability

Open
#2,384 0 comments 0 reactions 1 assignee Claimed by @leanmendoza View on GitHub
credits iOS metrics
Dominant language
Rust
Stars
18
Forks
19
Avg merge
4d 2h
Merged PRs (30d)
43

Description

**Sprint 20** · related to the IAP merge (#2276, #2272, #2280)

## Context

IAP is now live, but the only telemetry currently in place is `track_ios_storekit_environment` (environment detection). The purchase funnel itself is not instrumented: we cannot measure how many purchases are initiated, where users drop off, why purchases fail, or whether credits are ultimately delivered. For a real-money flow, this lack of observability is an operational risk we should close early.

## Goal

Add minimum viable tracking of the IAP purchase funnel, reusing the existing `Metrics` (Segment) surface — `lib/src/analytics/metrics.rs` + `Global.metrics` — the same path the storekit-environment event already uses. No new infrastructure; dashboards are a follow-up.

The instrumentation should let us measure the **initiated → charged → credited** conversion and surface the most common failure and rejection reasons.

## Events to add (hook points already exist in `godot/src/iap/iap_manager.gd`)

Funnel:
1. **products_loaded** — product count (`_on_products_loaded`); a count of `0` indicates an App Store Connect misconfiguration that is currently silent.
2. **purchase_initiated** — `purchase()` (also capture early bails: no wallet, purchase in flight).
3. **quote_result** — `_async_begin_purchase`: `allowed` | `denied` (code: `cap_exceeded` [total/daily] | `service_daily_limit` | `unknown_product`) | `transport_error`.
4. **storekit_result** — `_on_purchase_completed` / `_on_purchase_failed`: `success` | `cancelled` | `failed` (+reason).
5. **verify_result** — `_async_credit_with_backend`: `credited` (alreadyExisted?) | `deferred` (service_daily_limit) | `rejected` (+code: invalid_jws/token_mismatch/revoked/unknown_product/cap_exceeded) | `retry` (transport).
6. **balance_reconciled** — `_async_poll_balance_after_purchase`: `changed` | `timed_out`.

Additional:
- **overlay_timeout** — stuck purchase (`_PURCHASE_OVERLAY_TIMEOUT_SEC` reached).
- **marketplace round-trip** (`MarketplaceTracker`): `open_and_track` and `item_arrived` (category).

Suggested common properties: `product_id`, tier credits, DCL env (org/zone), StoreKit env (sandbox/prod), `is_guest`, and latency where it is inexpensive to capture.

## Constraints

- **No PII or secrets**: never send the raw wallet, receipt, JWS, or `appAccountToken` to analytics. The wallet must be hashed or omitted.
- Respect the **EULA consent gate** (events queue pre-consent, as the environment event does); `flush()` the terminal events so short sessions do not drop them.
- This is product/funnel analytics, not crash reporting — errors already reach Sentry via `printerr`.
- Analytics today is Android-centric (Firebase + Segment) while IAP is iOS-only. Confirm the Segment path actually ships from iOS; the storekit-environment event already does, so reuse that exact path.

## Acceptance criteria

- Each funnel stage emits a single event with consistent properties.
- The data is sufficient to compute the initiated → charged → credited conversion and the top rejection/failure reasons.
- Verified end-to-end on a sandbox TestFlight purchase, with events visible in Segment.

## Out of scope

Dashboards, alerting, and any new analytics infrastructure — minimum tracking only.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.