Metadata-Version: 2.4
Name: flet-billing
Version: 0.1.0
Summary: Flet extension for in-app purchases and subscriptions - a thin 1:1 wrapper over the Flutter in_app_purchase package (Google Play Billing / Apple StoreKit).
Author: Nwokike
License-Expression: MIT
Project-URL: Homepage, https://github.com/Nwokike/flet-billing
Project-URL: Repository, https://github.com/Nwokike/flet-billing
Project-URL: Issues, https://github.com/Nwokike/flet-billing/issues
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: flet>=1.0.1
Dynamic: license-file

# flet-billing

[![pypi](https://img.shields.io/pypi/v/flet-billing.svg)](https://pypi.python.org/pypi/flet-billing)
[![python](https://img.shields.io/badge/python-%3E%3D3.10-%2334D058)](https://pypi.org/project/flet-billing)
[![license](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/Nwokike/flet-billing/blob/main/LICENSE)

A [Flet](https://flet.dev) extension for in-app purchases and subscriptions — a
thin 1:1 wrapper over the Flutter [`in_app_purchase`](https://pub.dev/packages/in_app_purchase)
package (Google Play Billing on Android, StoreKit on iOS/macOS).

> **Not Google Pay.** Selling digital goods inside an app requires Google Play
> Billing; this wraps exactly that (via the official Flutter team package).

## Platform Support

| Platform | Windows | macOS | Linux | iOS | Android | Web |
|----------|---------|-------|-------|-----|---------|-----|
| Supported|    ❌   |  ✅   |   ❌  |  ✅ |    ✅   |  ❌ |

## Installation

- Using `uv`:
    ```bash
    uv add flet-billing
    ```
- Using `pip`:
    ```bash
    pip install flet-billing
    ```

## Usage

```python
import flet as ft
from flet_billing import Billing, PurchaseStatus

def main(page: ft.Page):
    billing = Billing(
        on_purchase_updated=lambda e: handle_purchases(e.purchases),
        on_error=lambda e: print("billing error:", e.message),
    )
    page.services.append(billing)

    async def start():
        if not await billing.is_available():
            print("billing unavailable")
            return
        result = await billing.query_products(["premium_unlock"])
        for product in result.products:
            print(product.id, product.price)
        await billing.buy_non_consumable("premium_unlock")

    def handle_purchases(purchases):
        for p in purchases:
            if p.status in (PurchaseStatus.PURCHASED, PurchaseStatus.RESTORED):
                # 1. verify p.verification_data.server_verification_data
                #    on your server (Play Developer API)
                # 2. deliver the content
                # 3. acknowledge — must run within 3 days:
                page.run_task(billing.complete_purchase, p.purchase_id)

    page.run_task(start)

ft.run(main)
```

### Methods

| Method | Platform | Description |
|--------|----------|-------------|
| `is_available()` | all | Whether the payment platform is ready. |
| `query_products(ids)` | all | Fetch product details (also caches them for buy calls). |
| `buy_consumable(product_id, auto_consume=True, offer_token=None)` | all | Start a consumable/subscription purchase. |
| `buy_non_consumable(product_id, offer_token=None)` | all | Start a non-consumable/subscription purchase. |
| `complete_purchase(purchase_id)` | all | Acknowledge a delivered purchase. |
| `restore_purchases()` | all | Restore non-consumables/subscriptions. |
| `get_country_code()` | all | Store-front country (ISO-3166-1 alpha2). |
| `query_past_purchases()` | Android | All purchases still owned. |
| `is_feature_supported(feature)` | Android | Play Billing feature probe. |
| `show_in_app_messages()` | Android | Play billing message dialog (e.g. fix payment). |

### Events

| Event | Payload |
|-------|---------|
| `on_purchase_updated` | `e.purchases: list[Purchase]` (status, product_id, purchase_id, verification_data, error, `pending_complete_purchase`) |
| `on_error` | `e.message` |

## The two rules

1. **Always `complete_purchase()` within 3 days** of a `purchased`/`restored`
   transaction (when `pending_complete_purchase` is `True`). Google Play
   refunds and revokes unacknowledged purchases automatically.
2. **Verify on your server** using `verification_data.server_verification_data`
   (Play Developer API / App Store Server API) before granting anything, and
   subscribe to Real-time Developer Notifications for revocations. Never trust
   the client alone.

### Play Console setup (for live testing)

- Products → create the product IDs you query (one-time or subscriptions).
- Internal/Closed test track — billing only works from an installed,
  signed test build.
- Settings → License testing → add tester Gmails and use test payment cards.

## Upstream bumps

This package deliberately contains no logic of its own — every method and event
is a direct forward to `in_app_purchase`. To pick up a new upstream release:
bump the pin in `src/flutter/flet_billing/pubspec.yaml`, push a tag, done.

## Development

```bash
uv sync
uv run flet run main.py   # runs the example app (root forwarder)
```

CI (GitHub Actions) runs `dart analyze` against the pinned Flutter package and
publishes the wheel to PyPI on `v*` tags — no local builds needed.

## License

MIT
