MagicStarterBillingView class
The plan and billing screen.
One page over the six independent reads MagicStarterBillingController publishes: the tier the team holds, the catalogue it can move to, what it has spent this cycle, its billing history, the card on file, and where the subscription is managed.
It resolves its controller, it does not construct one
MagicStatefulViewState resolves the controller through Magic.find, and
the registry builder below is zero-argument, so nothing on the render path
can supply a collaborator. The consumer therefore registers the controller
itself before this route is reached:
Magic.put(
MagicStarterBillingController(
usageCopy: withUsageCopy,
formatNumber: formatCount,
storeFundedTeamReader: readStoreFundedTeam,
isOwnerReader: readTeamOwnership,
),
);
That is also why every consumer-supplied thing this screen renders through (the usage copy, the number format) is a REQUIRED parameter on the controller rather than on this widget: a view parameter would need a default at the registration site, and both of the defaults available there are the wrong answer shipped silently.
Page chrome comes from MSPageScaffold
Never hand-rolled. The scaffold routes the page through the host's one
MSPageContainer geometry, so this screen lines up with every other page in
the app rather than centring at its own width.
The four axes that gate every affordance
The rail, never the running platform. Which management surface this
screen offers comes from MagicStarterBillingController.manageVia, which
the producer computes from the rail that sold the subscription. The two are
independent: a subscription bought on an iPhone is still managed in the App
Store when its owner opens the web app, so a branch on the RUNNING platform
(a kIsWeb check, a dart:io host test, the framework's target-platform
value) would offer the wrong surface to a real customer.
The owner, for anything that spends money. The billing write routes are the account owner's server-side, so a member sees the plan grid read-only with an owner-only notice instead of a call to action it would take a 403 to discover. The gate is tri-state (see MagicStarterBillingController.isOwner): only a KNOWN non-owner loses the call to action, since an unresolved membership must not stand between an owner and paying.
The rail this BUILD can serve, for anything it has to call. The
purchase-affecting calls live on WebBillingService and
StoreBillingService, each of which resolves to null where the build
cannot serve it, and no build serves both. That absence gates the checkout
call to action, all three portal affordances and the whole store purchase
surface, and it is a different question from manage_via: one asks where
the customer's subscription is managed, the other whether this binary has an
implementation to invoke.
A configured web origin, for the checkout redirects. A hosted checkout
session needs ABSOLUTE success and cancel urls, and
MagicStarterConfig.billingWebOrigin deliberately carries no default. An
unset origin therefore hides the checkout call to action rather than
building a relative url the rail refuses, because that refusal arrives as a
BillingException whose message goes to the log (see
_MagicStarterBillingViewState._reportBillingFailure) and the adopter would
never learn which config key they forgot.
One store account funds exactly one team
Store tiers share a subscription group so that upgrade and downgrade work,
and a store account holds at most one active subscription per group. So a
second purchase from the same account does not open a second subscription:
it TRANSFERS the one that exists and silently stops funding the team that
had it. Before offering to buy, this screen asks the consumer's
cross-team check and refuses by NAME when another team is already funded,
then asks AGAIN at the tap (see
_MagicStarterBillingViewState._purchaseInStore).
- Inheritance
-
- Object
- DiagnosticableTree
- Widget
- StatefulWidget
- MagicStarterBillingView
Constructors
- MagicStarterBillingView({Key? key})
-
Creates the billing view.
const
Properties
Methods
-
createElement(
) → StatefulElement -
Creates a StatefulElement to manage this widget's location in the tree.
inherited
-
createState(
) → State< MagicStarterBillingView> -
Creates the mutable state for this widget at a given location in the tree.
override
-
debugDescribeChildren(
) → List< DiagnosticsNode> -
Returns a list of DiagnosticsNode objects describing this node's
children.
inherited
-
debugFillProperties(
DiagnosticPropertiesBuilder properties) → void -
Add additional properties associated with the node.
inherited
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
toDiagnosticsNode(
{String? name, DiagnosticsTreeStyle? style}) → DiagnosticsNode -
Returns a debug representation of the object that is used by debugging
tools and by DiagnosticsNode.toStringDeep.
inherited
-
toString(
{DiagnosticLevel minLevel = DiagnosticLevel.info}) → String -
A string representation of this object.
inherited
-
toStringDeep(
{String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) → String -
Returns a string representation of this node and its descendants.
inherited
-
toStringShallow(
{String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) → String -
Returns a one-line detailed description of the object.
inherited
-
toStringShort(
) → String -
A short, textual description of this widget.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited