# Billing Sandbox Runbook

Billing sandbox tests verify the real Google Play Billing path for the `premium_lifetime` non-consumable product. They run outside the GMD regression aggregate on a self-hosted runner with a Play-enabled Android device.

## Play Console Setup

- Package name: `com.yorvana`.
- Product: active INAPP product `premium_lifetime`.
- Track: internal test track has an uploaded staging/release-signed build with `versionCode` greater than or equal to the test artifact.
- Test account: `BILLING_SANDBOX_ACCOUNT` is added under Play Console license testing and has country/payment setup that can complete sandbox purchases.
- For R-P03 purchase runs, refund and revoke the prior test order before triggering the workflow again.

## Runner Setup

- GitHub runner labels: `self-hosted` and `android-billing-sandbox`.
- Exactly one Android device is attached and visible to `adb devices`, or the local Play Store emulator from [Headless Play Store emulator](#headless-play-store-emulator) is running.
- The device or emulator has Google Play Store and Play Services.
- The device is signed into the same license-tester account as `BILLING_SANDBOX_ACCOUNT`.
- The runner has Android SDK/ADB and can run Gradle Android builds.

## Secrets

Use the same upload-key names as the Play deployment workflow:

- `KEYSTORE_BASE64`
- `KEYSTORE_PASSWORD`
- `KEY_ALIAS`
- `KEY_PASSWORD`
- `BILLING_SANDBOX_ACCOUNT`

The workflow decodes the keystore to `app/upload.jks` and builds the `staging` variant with `-PbillingSandboxBuild`. Do not commit upload keys or generated keystores.

## Headless Play Store emulator

Use this path when there is no physical Android device attached to the self-hosted runner. It creates a Play Store AVD, starts it without an emulator window, uses KVM plus host GPU acceleration, and serves a temporary browser controller that streams screenshots through ADB and sends taps/text/key events back through ADB.

Prerequisites:

- Linux host with `/dev/kvm` available.
- Android SDK with `emulator`, `platform-tools`, and `cmdline-tools/latest`.
- Host GPU rendering usable by the Android emulator. Verify with:

```bash
emulator -accel-check
```

Start the emulator and controller:

```bash
BILLING_EMULATOR_BIND=0.0.0.0 tools/billing/start-play-emulator.sh
```

The script installs `system-images;android-33;google_apis_playstore;x86_64` if needed, creates `yorvana_billing_pixel2api33_play` under `.tmp/android-avd`, boots it with:

```bash
emulator @yorvana_billing_pixel2api33_play -no-window -gpu host -no-audio -no-snapshot-load
```

It then waits for `adb` and `sys.boot_completed=1`, launches Play Store, and prints browser URLs for the controller. Open the URL from a trusted browser, click **Sign in**, and sign into the Play Console license tester account.

The controller is intentionally temporary local tooling:

- Default bind is `127.0.0.1`; set `BILLING_EMULATOR_BIND=0.0.0.0` only when you need browser access from another machine.
- Default port is `8090`; override with `BILLING_EMULATOR_PORT=8091`.
- It uses plain HTTP and has no authentication. Do not expose it beyond a trusted network.
- Text entry uses `adb shell input text`, which is fine for simple email/password text but may not handle every special character. Use the on-screen keyboard through clicks if a credential field has characters ADB cannot inject.
- Stop the foreground script with `Ctrl-C`. If the emulator remains running, stop it with `adb -s emulator-5554 emu kill`.

Useful overrides:

```bash
BILLING_EMULATOR_AVD=my_billing_avd \
BILLING_EMULATOR_AVD_HOME=/path/to/avds \
BILLING_EMULATOR_PORT=8091 \
BILLING_EMULATOR_BIND=0.0.0.0 \
tools/billing/start-play-emulator.sh
```

## Running

Scheduled runs execute only the restore scenario:

```bash
./gradlew :app:connectedStagingAndroidTest -PbillingSandboxBuild \
  -Pandroid.testInstrumentationRunnerArguments.annotation=com.yorvana.testsupport.tiers.BillingSandbox \
  -Pandroid.testInstrumentationRunnerArguments.billingSandboxScenario=restore
```

Manual purchase smoke runs use `workflow_dispatch` with `scenario=purchase`. Before each purchase run, refund and revoke the previous sandbox order in Play Console so the non-consumable product can be bought again.

## Expected Behavior

- R-P03 purchase: one free vehicle at cap, Add Vehicle opens the paywall, Upgrade launches Play Billing, purchase succeeds, premium is cached, the paywall disappears, and the app lands on Add Vehicle.
- R-P04 restore: fresh app data starts non-premium, Settings restore queries the owned `premium_lifetime` purchase, premium flips true, and Settings shows Premium active.
- Missing account, signing material, device readiness, signed-in account, or product details causes a JUnit assumption skip before UI driving begins.
