# Android purchase eligibility — minimum app version 1.4.6

> HISTORICAL — superseded on 17 September 2026 by [the Android compatibility switch](android-purchase-rollout-switch.md). Do not use the forced-update expectations below for the current code: 1.4.6 now selects eligibility for v2, while older versions remain allowed in compatibility mode.

Implemented 2026-09-17. This is an additive pre-purchase compatibility policy, not a minimum version for the whole backend/app.

## Configuration

`config/purchases.php` reads `ANDROID_PURCHASE_MIN_APP_VERSION`, default **1.4.6**. The value is a stable three-part version such as `1.4.6`; invalid server configuration returns HTTP 503 and does not allow a new purchase.

Uses the existing News API convention: the frontend sends `app_version` from `DeviceInfo.getVersion()`, and the backend uses PHP `version_compare()`. The news table's `min_app_version` remains a content filter and is not reused as purchase policy storage. `1.4.10` correctly compares greater than `1.4.6`.

## Contract

Authenticated `POST /api/android-purchase-policy`:

- `app_version`: installed Android version name, e.g. `1.4.6`.
- `app_build`: installed build number, e.g. `30` (diagnostic metadata; not the eligibility comparator).
- Existing Bearer authentication. No receipt, purchase token, or product is needed for this check.

Allowed (HTTP 200):

```json
{"success":true,"allowed":true,"update_required":false,"min_app_version":"1.4.6","reason":"supported"}
```

Too old, missing, malformed, or prerelease version (HTTP 426):

```json
{"success":false,"allowed":false,"update_required":true,"min_app_version":"1.4.6","reason":"app_update_required"}
```

The app checks on each new purchase attempt, before creating a durable purchase attempt or calling Google Play. Authentication failure, timeout, absent endpoint, server error, or malformed response prevents a NEW store request and gives a retry/sign-in message. HTTP 426 gives an Update app action. No payment failure/completion event is fabricated; a bounded `purchase_preflight_blocked` event explains the pre-store denial.

## Backward compatibility — essential

No app-version gate was added to `/validate-receipt-android`, `/check-pro-status`, restore/reconciliation, or Google notifications/acknowledgement retries. Store-confirmed payments must still be processed, including requests from older clients or with no usable version metadata. The existing successful response fields are preserved.

Already-installed old apps do not call this policy endpoint. They cannot be remotely made to show its Update message just by deploying this code. This is NOT a global rejection of all clients below 1.4.6; their validation route remains compatible. App version is client-supplied compatibility metadata, not proof of identity, purchase, or entitlement. Store verification remains authoritative.

## Deployment order

1. Deploy this additive endpoint/config and the compatible purchase backend. Keep the existing routes intact.
2. Refresh Laravel configuration/route caches using the normal deployment procedure; ensure the effective minimum is 1.4.6. No database migration is required for this policy.
3. Verify authenticated policy requests: 1.4.5 denied, 1.4.6 and 1.4.10 allowed. Verify old-format paid receipt validation still succeeds.
4. Distribute/test Android 1.4.6. Its native version is 1.4.6 and source versionCode is 30; confirm that build code is unused in Play before upload.
5. Validate the real Play sandbox flow before production rollout. Keep the policy endpoint available when rolling back unrelated backend components, or 1.4.6 clients will stop new purchases before payment.

For staging-only QA, temporarily raising the minimum to 1.4.7 lets a 1.4.6 build exercise its Update screen. Restore the minimum to 1.4.6 afterward. Do not change production policy merely to run that test.

The API base URL is still selected manually in mobile `utils/Config.tsx`; this feature does not introduce environment variants or build flags. iOS version/purchase initiation is unchanged.

## Automated tests

```bash
php vendor/bin/phpunit --filter 'AndroidPurchasePolicyControllerTest|PurchaseAndroidControllerTest' tests/Unit/Http/Controllers
```

Tests cover numeric version comparisons, invalid input/configuration, authenticated route registration, and valid paid subscriptions submitted by old, new, missing-version, and malformed-version clients. Google API calls are mocked; these are not live store/deployment tests.
