core: refuse a badge code or invoice while a store receipt is pending

A code redeemed while a store receipt is held put the code's badge on the
profile, and the held receipt then waited behind it; for a subscription the
store kept charging every period meanwhile. Code redemption and invoice
creation now answer "store purchase open" while the profile holds an App
Store subscription receipt, or another receipt whose last failure can clear
on its own. A receipt that failed for good does not block, except an Apple
subscription, which Apple keeps charging; an uncredited Play subscription is
refunded by Play.

Run: 003-badge-subscriptions-ui, slice 6
This commit is contained in:
spacedandy-agents-simplex committed 2026-10-15 06:11:10 +00:00
1 parent 664e5388ff
commit a3b2e5c9f1
8 files changed
+81 -6

No files matched your search

@@ -197,6 +197,9 @@ class BadgeRedeemError_serviceNotConfigured(TypedDict):
class BadgeRedeemError_badgeActive(TypedDict):
type: Literal["badgeActive"]
class BadgeRedeemError_storePurchaseOpen(TypedDict):
type: Literal["storePurchaseOpen"]
class BadgeRedeemError_serviceError(TypedDict):
type: Literal["serviceError"]
serviceError: "BadgeServiceErrorCode"
@@ -216,13 +219,14 @@ BadgeRedeemError = (
| BadgeRedeemError_invalidReceipt
| BadgeRedeemError_serviceNotConfigured
| BadgeRedeemError_badgeActive
| BadgeRedeemError_storePurchaseOpen
| BadgeRedeemError_serviceError
| BadgeRedeemError_invalidResponse
| BadgeRedeemError_unknownKeyIndex
| BadgeRedeemError_credentialNotVerified
)
BadgeRedeemError_Tag = Literal["invalidCode", "invalidReceipt", "serviceNotConfigured", "badgeActive", "serviceError", "invalidResponse", "unknownKeyIndex", "credentialNotVerified"]
BadgeRedeemError_Tag = Literal["invalidCode", "invalidReceipt", "serviceNotConfigured", "badgeActive", "storePurchaseOpen", "serviceError", "invalidResponse", "unknownKeyIndex", "credentialNotVerified"]
BadgeServiceErrorCode = Literal["bad_request", "unsupported_version", "unknown_purchase_key", "unknown_offer_id", "offer_disabled", "offer_mismatch", "product_unavailable", "payment_not_entitled", "payment_pending", "provider_unavailable", "provider_not_configured", "rate_limited", "code_invalid", "code_used", "code_expired", "receipt_invalid", "receipt_used", "test_purchase", "internal"]