Version

Storefront

What a project sells, as a connector's member surface offers it — the plans on offer by the core's own rule, the portal checkout that opens each, and the one purchase that completes on the connector, an access code.

Subscriby\Connector\Core\Storefront. The portal is where a purchase is made. A connector that cannot run a checkout of its own shows the plans and hands the person to the portal's checkout for the one they pick, coupon included; an access code is the one purchase that completes on the connector, because nothing is paid.

plansFor()

foreach ($storefront->plansFor($installation, $account?->identityRecord()) as $offer) {
    // $offer->name, ->price, ->cycle, ->trialDays, ->seatsLeft, ->checkoutUrl
}

The plans the project offers an account, in the storefront's order, each a PlanOffer: the id, name and description (in the canonical HTML subset), the PlanKind, the price formatted in the plan's currency, the billing cycle for a recurring plan, the trial's length, how many may still buy under a cap, whether a pass is on a schedule, and checkoutUrl, the portal checkout that opens the plan. The core's offer rule applies: only active, purchasable plans; a newcomer's plans to an account the project has never sold to and a customer's to one it has; a single-use plan only to an account that never bought it. Pass null, or an account the project does not know, and the list is what a newcomer sees.

checkoutUrlFor()

$url = $storefront->checkoutUrlFor($installation, $planId, $couponCode);

The portal checkout for one plan, with the coupon applied when the project has a coupon by that code (an unknown code is left off rather than refused, so the portal explains it). Null when the plan is not the project's or the project has no handle. portalUrl() is the portal's home for the same project, for a stranger told to connect their account there.

redeemAccessCode()

try {
    $opened = $storefront->redeemAccessCode($installation, $account->identityRecord(), $typed);
} catch (AccessCodeRefused $refused) {
    return $this->say(match ($refused->reason) {
        AccessCodeRefused::USED => 'That code was already redeemed.',
        AccessCodeRefused::EXPIRED => 'That code has expired.',
        AccessCodeRefused::ALREADY_MEMBER => 'You already hold a membership here.',
        AccessCodeRefused::NOT_A_MEMBER => 'Connect your account on the portal first.',
        default => 'That is not a code this project issued.',
    });
}

Opens the membership an access code stands for, through the action the portal's form runs, and answers an AccessCodeRedemption with the subscription's id, the plan's name and when the access ends. The refusal carries a stable reason (unknown, used, expired, already_member, not_a_member) to key a sentence on, and the core's own message for the log.

Nothing here charges anyone. A connector that can take money natively declares native_payments and runs its own checkout; this contract is for the rest, and for the free door an access code opens.

How is this guide?

Last updated on

Version

On this page

Subscriby is a product
designed by you — for you.
No boardroom full of executives deciding what we ships next. Our roadmap always shaped by you with your feedback.

Share feedback or a request

For AI agents: llms.txt · llms-full.txt