Purchases¶
Purchases sells in-app products through StoreKit 2
and Google Play Billing, and fake_store stands in
for both off device. Enable it with use = ["purchases"] under [plugins]. See the
Purchases guide.
In-app purchases: StoreKit 2 on iOS and Google Play Billing on Android.
Enable it in pythonnative.toml:
Products are defined in App Store Connect and the Play Console, and their
ids go here. Off device, and in the browser preview, a fake store stands in;
see fake_store.
Example
Always finish a transaction after granting what it paid for. The store keeps re-delivering an unfinished one, and Play refunds one left unacknowledged for three days.
Classes:
| Name | Description |
|---|---|
Product |
Something for sale, as the store describes it. |
Transaction |
A completed payment. |
PurchaseResult |
The outcome of |
FakeStore |
The store off device and in the browser preview. It sells only what it's told to. |
Purchases |
The platform's store. |
Attributes:
| Name | Type | Description |
|---|---|---|
ProductKind |
What a product is. Play doesn't record whether an in-app product is consumable, so it reports |
|
PurchaseStatus |
How a purchase ended. |
|
fake_store |
The fake store in use off device. |
ProductKind
module-attribute
¶
ProductKind = Literal[
"consumable",
"non_consumable",
"subscription",
"one_time",
]
What a product is. Play doesn't record whether an in-app product is consumable, so it reports one_time.
PurchaseStatus
module-attribute
¶
PurchaseStatus = Literal[
"purchased", "pending", "cancelled"
]
How a purchase ended. pending waits on something outside the app, such as a parent's approval.
Product
dataclass
¶
Product(
id: str,
title: str,
description: str,
price: Decimal,
currency: str,
display_price: str,
kind: ProductKind,
)
Something for sale, as the store describes it.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
The product's identifier. |
title |
str
|
The localized name. |
description |
str
|
The localized description. |
price |
Decimal
|
The price in |
currency |
str
|
The ISO 4217 code. |
display_price |
str
|
The price formatted for the user's locale. |
kind |
ProductKind
|
What it is. |
Transaction
dataclass
¶
Transaction(
id: str,
product_id: str,
purchase_date: datetime,
token: str,
verified: bool,
revoked: bool = False,
)
A completed payment.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
The store's identifier for this transaction. |
product_id |
str
|
The product it paid for. |
purchase_date |
datetime
|
When it happened. |
token |
str
|
What your server sends to the store to check it: the JWS on iOS, and the purchase token on Android. |
verified |
bool
|
On iOS, whether StoreKit's signature check passed. Always
|
revoked |
bool
|
The store took it back, for a refund for example. Only iOS reports this to the device. |
PurchaseResult
dataclass
¶
PurchaseResult(
status: PurchaseStatus,
transaction: Optional[Transaction] = None,
)
The outcome of Purchases.purchase.
Attributes:
| Name | Type | Description |
|---|---|---|
status |
PurchaseStatus
|
How it ended. |
transaction |
Optional[Transaction]
|
The transaction, when |
FakeStore
¶
The store off device and in the browser preview. It sells only what it's told to.
It isn't a simulator of either real store: it answers immediately and
charges nothing. On a device calls never reach it, so setting it up there
has no effect. The module's one instance is
fake_store.
Example
from decimal import Decimal
from pythonnative.purchases import Product, Purchases, fake_store
fake_store.set_products([
Product("pro", "Pro", "Everything", Decimal("4.99"), "USD", "$4.99", "non_consumable"),
])
fake_store.set_outcome("cancelled")
result = await Purchases.purchase("pro")
assert result.status == "cancelled"
Methods:
| Name | Description |
|---|---|
reset |
Forget all products, outcomes, and transactions. |
set_available |
Set what |
set_products |
Set what's for sale. |
set_outcome |
Set how every later purchase ends. An error is raised from |
push_update |
Complete a purchase outside the app, as an approved pending one would, telling |
is_available |
Answer what |
products |
Return the ones |
purchase |
End as |
restore |
Return every transaction not yet consumed. |
finish |
Record the id in |
listen |
Do nothing: updates go straight to listeners. |
set_outcome
¶
set_outcome(
outcome: Union[PurchaseStatus, NativeModuleError],
) -> None
Set how every later purchase ends. An error is raised from purchase.
push_update
¶
push_update(product_id: str) -> Transaction
Complete a purchase outside the app, as an approved pending one would, telling on_update.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
product_id
|
str
|
The product it paid for. |
required |
Returns:
| Type | Description |
|---|---|
Transaction
|
The transaction |
finish
¶
Record the id in finished, and consume it only with consume=True.
That's Google Play's rule, the stricter one: the App Store also
consumes consumables by itself, so code that passes consume here
works with both.
Purchases
¶
The platform's store.
Raises:
| Type | Description |
|---|---|
NativeModuleError
|
If the store fails, or with the code
|
Methods:
| Name | Description |
|---|---|
is_available |
Whether this device can buy anything right now. |
products |
Look up products by id. |
purchase |
Show the store's purchase sheet and wait for the user. |
restore |
Sync with the store and return what the user currently owns. |
finish |
Tell the store the app has delivered what a transaction paid for. |
on_update |
Call |
products
async
staticmethod
¶
purchase
async
staticmethod
¶
purchase(product_id: str) -> PurchaseResult
Show the store's purchase sheet and wait for the user.
A user who already owns the product gets a purchased result with
the existing transaction. Subscriptions buy the product's first offer
on Android.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
product_id
|
str
|
The product to buy. |
required |
Returns:
| Type | Description |
|---|---|
PurchaseResult
|
The outcome. |
PurchaseResult
|
errors. A pending purchase later arrives through |
PurchaseResult
|
Raises:
| Type | Description |
|---|---|
NativeModuleError
|
With the code |
restore
async
staticmethod
¶
restore() -> List[Transaction]
Sync with the store and return what the user currently owns.
On iOS this can prompt the user to sign in, so call it from a "Restore purchases" button. Finished consumables aren't included.
finish
async
staticmethod
¶
finish(
transaction: Transaction, *, consume: bool = False
) -> None
Tell the store the app has delivered what a transaction paid for.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transaction
|
Transaction
|
The transaction. |
required |
consume
|
bool
|
On Android, consume the purchase so it can be bought again. Play can't tell which products are consumable, so this is how you say so. iOS knows from App Store Connect and ignores it. |
False
|
on_update
staticmethod
¶
on_update(
callback: Callable[[Transaction], Any],
) -> Callable[[], None]
Call callback for each transaction that didn't come from a purchase call.
This covers pending purchases completing, purchases made outside the
app, refunds on iOS (revoked), and whatever a previous launch left
unfinished. Subscribe early. The platform holds these for the first
subscriber, so nothing is lost before then.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
callback
|
Callable[[Transaction], Any]
|
Receives a
|
required |
Returns:
| Type | Description |
|---|---|
Callable[[], None]
|
A function that unsubscribes. |