loadStoreFundedTeam method
Asks the consumer's storeFundedTeamReader whether a store account already funds another of the caller's teams, and republishes storeFundedTeam with its name when one does.
Asked only on a build with a store rail, because it exists to gate a store purchase and a web build has none to gate: a screen with no store affordance would be spending a request on an answer it cannot use. Skipped entirely when no reader is registered, which storeCheckRegistered reports separately so the gate can refuse rather than guess.
Deliberate degradation on failure: storeFundedTeam keeps whatever it
held, which for a first read means null and therefore permissive. The
producer's TRANSFER handling is what keeps the entitlement itself honest
either way, so this check exists to stop a customer being surprised, not to
stop the data being wrong.
The degradation stays deliberate but it does NOT stay silent. This read is the only thing standing between a store purchase and transferring another team's subscription away, it fails permissive, and the purchase path asks it again at the moment money moves. Swallowing it silently let a real 500 on this endpoint pass the transfer through with nothing in any log to explain it afterwards.
It only ever SETS a name and never clears one, which is a consequence
rather than an oversight: it runs at mount and again before a purchase, and
the second call is only reachable while the answer was already null (a
named refusal renders no CTA to tap), so a name-to-null transition has no
trigger here. A future caller that could produce one has to publish the
empty answer too.
Implementation
Future<void> loadStoreFundedTeam() async {
final int session = _sessionGeneration;
final int readToken = _latestRead.begin(_storeFundedTeamKey);
final MagicStarterStoreFundedTeamReader? reader = storeFundedTeamReader;
if (reader == null) return;
try {
// Inside the try, because resolving a rail is itself a call that can
// fail: `PaymentsManager._optional` returns null for an unfilled role but
// THROWS a BillingException when the role holds something that cannot
// serve the contract, which is what a consumer's mistyped `extend` call
// produces. Reading it above the try would let that escape `load()`,
// where `onInit` fires it unawaited and nothing is left to catch it.
if (storeRail == null) return;
final String? name = await reader();
if (session != _sessionGeneration) return;
if (!_latestRead.isCurrent(readToken, _storeFundedTeamKey)) return;
if (name == null || name.isEmpty) return;
_storeFundedTeam = name;
refreshUI();
} catch (error) {
if (session != _sessionGeneration) return;
if (!_latestRead.isCurrent(readToken, _storeFundedTeamKey)) return;
_reportDegradation('storeFundedTeamReader', error);
}
}