Let the store take PRO back, and finish every purchase

This commit is contained in:
2026-09-16 17:24:48 +07:00
parent 7b013fb6f8
commit 5a5c4b0d11
+18 -1
View File
@@ -50,6 +50,11 @@ const grant = async () => {
await AsyncStorage.setItem(CACHE_KEY, '1').catch(() => {});
};
const revoke = async () => {
set({ owned: false, error: null });
await AsyncStorage.removeItem(CACHE_KEY).catch(() => {});
};
// Play hands the purchase to the listener. Grant it, then finalise: an Android
// purchase left unacknowledged for three days is automatically refunded.
// ponytail: the grant is client-side. Server-side verification of
@@ -80,7 +85,19 @@ export async function initProUnlock() {
]);
const listed = (Array.isArray(products) ? products : []).find((p) => p.id === PRO_SKU);
set({ price: listed?.displayPrice ?? null });
if (purchases.some((p) => p.productId === PRO_SKU)) await grant();
const bought = purchases.find((p) => p.productId === PRO_SKU);
if (bought) {
await grant();
// A purchase Play never got from us finalised (the app was killed between
// the two) is refunded automatically after three days. Harmless to repeat.
await finishTransaction({ purchase: bought as never, isConsumable: false }).catch(() => {});
} else if (listed) {
// The store is the authority: a refunded purchase is no longer in the
// list, so the cache must not outlive it. Only when the query ANSWERED and
// the product is still on sale — an offline or broken query also returns
// nothing, and that must never revoke a paying user.
await revoke();
}
} catch (e) {
set({ error: e instanceof Error ? e.message : 'Store unavailable' });
}