#321 fixed six SDK parsers that threw on an environment or clientPayload.format
value IAPKit can legitimately add, and documented the rule at /docs/kit-compatibility:
optional metadata degrades, it never fails a purchase the store already confirmed.
It fixed them by hand, one parser at a time. Two surfaces still fail closed, and
both are places the next contributor would land on by default rather than by mistake.
Suggested order: §1 first — it removes the failure mode from the generator, so no
future SDK has to remember the rule.
1. Generated decoders throw on an unknown enum value
The hand-written parsers degrade; the generated ones do not. Nothing routes through
them from IAPKit-controlled data today — #321 mapped around them — so this is latent,
not live. It stops being latent the moment someone decodes a response with the
generated type, which is the obvious thing to reach for.
Two changes, neither of which adds an enum member, so neither breaks an exhaustive
when / switch in consumer code:
a. Degrade to the neutral member when the enum already declares one.
IapkitPurchaseState has UNKNOWN and IapStore has Unknown, yet the generated
fromJson throws anyway — packages/gql/codegen/plugins/kotlin.ts:230,
dart.ts:187, csharp.ts:322. Return the neutral member instead, and keep the throw
only for enums that have none.
b. Drop an optional object whose required enum field cannot decode.
clientPayload is optional but its format is a required IapkitClientPayloadFormat,
which has no neutral member. Emit the owner-field decode so an unreadable format
yields null for the whole payload — exactly what the hand-written parsers already do.
With (b), IapkitClientPayloadFormat does not need an Unknown member at all.
Known wrong today:
Scope: the Kotlin, Dart, C#, GDScript and TypeScript plugins under
packages/gql/codegen/plugins/, then bun run generate.
2. One unknown platform empties the whole MAUI KitApi response
KitSubscription.Platform (IapPlatform) and KitProduct.Platform
(KitProductPlatform) are the only wire fields on the KitApi records typed as enums.
Everything else IAPKit could extend — State, Type, BillingPeriod, Kind,
Format — is already string.
Both converters throw on an unrecognised value: IapPlatformJsonConverter.Read in
libraries/maui-iap/src/OpenIap.Maui/Types.cs ends in throw new JsonException(...)
with no Unknown member to land on, and KitProductPlatform uses the stock
JsonStringEnumConverter. Because the body is decoded in one
parsed.Deserialize<T>() in KitApi.cs, a single unknown platform empties the entire
entitlements or products response rather than dropping one row.
IAPKit deploys from main and its verify surface already carries horizon and
amazon, so widening platform is plausible.
Context: #321, and /docs/kit-compatibility for the policy both sections enforce.
#321 fixed six SDK parsers that threw on an
environmentorclientPayload.formatvalue IAPKit can legitimately add, and documented the rule at
/docs/kit-compatibility:optional metadata degrades, it never fails a purchase the store already confirmed.
It fixed them by hand, one parser at a time. Two surfaces still fail closed, and
both are places the next contributor would land on by default rather than by mistake.
Suggested order: §1 first — it removes the failure mode from the generator, so no
future SDK has to remember the rule.
1. Generated decoders throw on an unknown enum value
The hand-written parsers degrade; the generated ones do not. Nothing routes through
them from IAPKit-controlled data today — #321 mapped around them — so this is latent,
not live. It stops being latent the moment someone decodes a response with the
generated type, which is the obvious thing to reach for.
Two changes, neither of which adds an enum member, so neither breaks an exhaustive
when/switchin consumer code:a. Degrade to the neutral member when the enum already declares one.
IapkitPurchaseStatehasUNKNOWNandIapStorehasUnknown, yet the generatedfromJsonthrows anyway —packages/gql/codegen/plugins/kotlin.ts:230,dart.ts:187,csharp.ts:322. Return the neutral member instead, and keep the throwonly for enums that have none.
b. Drop an optional object whose required enum field cannot decode.
clientPayloadis optional but itsformatis a requiredIapkitClientPayloadFormat,which has no neutral member. Emit the owner-field decode so an unreadable format
yields
nullfor the whole payload — exactly what the hand-written parsers already do.With (b),
IapkitClientPayloadFormatdoes not need anUnknownmember at all.Known wrong today:
libraries/flutter_inapp_purchase/lib/types.dart— throwsArgumentErrorforstate, store, and client-payload format.
libraries/godot-iap/addons/godot-iap/types.gd— assigns the raw string into anenum-typed variable, a GDScript runtime type error.
packages/google/.../Types.ktand the kmp mirror — a missing or non-stringformatsilently defaults toToml, so a JSON body would be parsed as TOML.Scope: the Kotlin, Dart, C#, GDScript and TypeScript plugins under
packages/gql/codegen/plugins/, thenbun run generate.2. One unknown platform empties the whole MAUI KitApi response
KitSubscription.Platform(IapPlatform) andKitProduct.Platform(
KitProductPlatform) are the only wire fields on the KitApi records typed as enums.Everything else IAPKit could extend —
State,Type,BillingPeriod,Kind,Format— is alreadystring.Both converters throw on an unrecognised value:
IapPlatformJsonConverter.Readinlibraries/maui-iap/src/OpenIap.Maui/Types.csends inthrow new JsonException(...)with no
Unknownmember to land on, andKitProductPlatformuses the stockJsonStringEnumConverter. Because the body is decoded in oneparsed.Deserialize<T>()inKitApi.cs, a single unknown platform empties the entireentitlements or products response rather than dropping one row.
IAPKit deploys from
mainand its verify surface already carrieshorizonandamazon, so wideningplatformis plausible.(
IapPlatform→string, matchingStateon the same record) in a releasedpackage. The non-breaking alternative is a KitApi-local tolerant converter.
subscriptions,productsandofferselement by element so oneunreadable row cannot empty the array.
Types.csis generated — fix inKitApi.csor the codegen plugin, never by hand.Context: #321, and
/docs/kit-compatibilityfor the policy both sections enforce.