QR Coupon Journeys — coupon fluent API and claim flow

Status: design draft, 18 September 2026. This page specifies future implementation; publishing it does not create an action link, modify a coupon, save a payment method, grant access, or charge anyone. All builder APIs and new node types below are proposed. Source inspection used MeshWeaver.Plugins commit 06757aa79b1c8820ad9673776808d07aea690b3c.

The shared mechanism belongs to Essentials: QR Coupon Journeys. Store supplies its coupon-specific action, offer model and fulfilment. The companion Payment Methods specifies payment-provider contracts, setup verification, consent and later purchases. Neither Store nor an individual coupon builds a second GUID resolver, QR generator, authentication return flow or home-screen installation stack. Essentials already depends on Store; Store must not acquire a dependency on Essentials. Minimal neutral, serializable action/handler/publisher contracts belong in the core MeshWeaver.Mesh.Contract assembly. Essentials composes its publisher with the Store handler through those contracts.

Product decision

An authorized activity author creates a shareable invitation to a particular MeshWeaver site and landing page. Their phone can display its QR card from a home-screen icon. A recipient scans the code, signs in or signs up, and resumes the same offer with the coupon and relevant information already filled in. The offer page is available to signed-in people, including people who own no courses; it is not anonymously readable. The sign-in shell itself may be anonymous, but never reveals the action payload or recipient information.

The first concrete offer uses COURSES3M. Before activating it, the new journey asks the recipient to set up a payment method securely with the payment provider. MeshWeaver contains much free content alongside paid items, so this prepares purchases the recipient may choose later. Nothing is charged today for setup or coupon activation. Each separate purchase requires an explicit confirmation of its item and total.

Coupon redemption can expose additional configured features. The first optional feature is continuing Personal as a paid subscription after the coupon period. Its checkbox starts unchecked. Only a deliberate selection, accepted with the displayed start date, total, currency, cadence and cancellation terms, authorizes recurring charges. When left unchecked, the benefit ends without renewal or payment. Saving a method for individual purchases and agreeing to recurring subscription billing are separate consents; neither implies the other. A mockup may show the checked state to illustrate the choice, but the production default remains unchecked.

The benefit is a bounded Personal plan. It covers packages at the Personal rank or below while active; it does not mean every course regardless of tier. The page can highlight a selected course only after the server proves that the coupon and Personal tier cover it. Installing the phone card and acquiring course access are separate actions with separate labels.

What exists and what must be added

Existing source Current responsibility Proposed integration
Store/Coupon/Source/CouponContent.cs Typed coupon, normalized-code identity, tier and term, validity window, budget and audit Read the existing coupon as authoritative offer data; add no public read grant to Admin/Coupons
Store/Coupon/Source/CouponScope.cs Pure scope rules over package path, category and repository Validate a configured target against the stated rule and package facts
Store/Licensing/Source/CouponRedemption.cs Find, quote, validate, free redemption, standing grants and redemption counting Reuse read/pure policy where correct; add a distinct, durable plan-claim operation for the new journey
Store/Licensing/Source/Subscriptions.cs Subscription records, activation requests, activation-settlement observation and revocable package unlocks Activate the bounded plan through the existing control plane, await its result, unlock through the plan
Store/Subscription/Source/SubscriptionControlPlane.cs Authorize and execute activation/cancellation, revoke plan-minted access Remain the sole writer of subscription lifecycle outcomes
Store/BillingProfile/Source/BillingForm.cs Viewer-private profile creation with name/email prefill; framework-bound editing Reuse identity/profile rules, with an appropriately small setup form
Store/Order/Source/OrderCheckout.cs Store policy over the provider-neutral payment contract Add setup and explicitly opted-in trial/renewal operations; ordinary paid orders remain separate
Store/Catalog/Source/PaymentInboxWatcher.cs Authenticate deliveries and drive order/subscription fulfilment Dispatch verified setup and trial lifecycle evidence to the claim control plane; a zero-due trial completion is not an immediate paid month
src/MeshWeaver.Payments.Stripe/ Optional provider implementation Implement setup-only sessions plus trial subscriptions only where recurring consent exists

The core payment contract lives in MeshWeaver/src/MeshWeaver.Mesh.Contract/Payments/, in the sibling platform repository. Its present checkout request expresses payment or recurring checkout, not the full setup-only or consented zero-due trial lifecycle. Provider-neutral capabilities for these must land there before Store sources bind them. Store source must continue compiling and rendering an honest unavailable state on a mesh with no payment provider. See the platform Payment Provider Contract.

There is no coupon journey fluent builder in the inspected sources. Do not describe an example on this page as an API that already compiles.

Measured COURSES3M offer

A read-only lookup of Admin/Coupons/COURSES3M on https://memex.meshweaver.cloud, on 18 September 2026, returned:

Field Observed value Consequence
Tier personal Personal-rank coverage, not unrestricted access
Tier days 90 A day-based term, not three calendar months
Valid until 2026-12-31T23:59:59Z Redemption and the granted expiry are bounded by this instant
Price Unset Free under the existing coupon rules
Plugins Empty Existing initial redemption has no literal package restriction
Scope / grants-all Unset No scoped key is implied
Maximum redemptions Unset Deliberately unlimited at the time of inspection

This is evidence, not a configuration to copy into a deployment. Resolve and revalidate the coupon on the target site at publication, claim start and final fulfilment. COURSES3M on another site is not assumed to exist or have identical terms. Do not copy production redeemer identities, internal notes or audit history into design examples or recipient pages.

CouponContent.TierExpiry(at) calculates min(at + 90 days, validUntil) for the observed offer. At 20 December 2026, the grant therefore lasts only until the end of 31 December, not for ninety further days. Marketing must show the actual end date and a clear reduction when relevant. A headline may say “Personal access with COURSES3M”; “90 days” is shown only when the calculated duration supports it. A future decision to separate the redemption deadline from benefit expiry is a separate coupon model change and must not be silently implemented by this journey.

The exact benefit instants are fixed in a short-lived server quote and accepted once with the claim's final confirmation. benefitStartsAt is the quote's stated start; benefitEndsAt is CouponContent.TierExpiry(benefitStartsAt). The page displays the resulting date before consent or an external handoff, and rejects an expired quote rather than silently recalculating it. For a selected renewal, renewalStartsAt is exactly that accepted benefitEndsAt. A retry cannot recompute these dates, turn the cap into a full ninety days, or bring a charge forward. Only the baseline no-renewal branch may quote final benefit instants after verified setup. If renewal is selected, freeze and accept exact dates, total and recurring terms before any off-session wallet authorization; preserve that preflight quote through setup and zero-due trial creation. Any changed quote requires fresh explicit recurring authorization.

The campaign owner must still decide whether to preserve the observed December cap or authorize a separate campaign with a full ninety-day benefit for every eligible redeemer. This design defaults to the existing cap and exposes the actual end date. It does not rewrite COURSES3M or promise three calendar months. If the coupon expires or is withdrawn before final authorization, setup may have succeeded but the benefit must not be granted; show that distinction and permit payment-method removal. If a trial subscription already exists during a failed claim, disable its future billing through the recorded recovery phase before declaring the claim cancelled.

Proposed fluent surface

The domain builder produces a neutral typed ActionInvocation contract that Essentials' publisher accepts. It stores a versioned descriptor, not a C# closure, raw script or arbitrary method name in a public URL. The handler is registered by Store and receives a server-resolved site, authenticated recipient and validated claim context. A builder cannot elevate the publishing actor's authority or the recipient's grants.

Illustrative C#; names and signatures are proposals:

// Proposed API. Neither constructing the descriptor nor rendering the card executes it.
var couponAction = Coupons.Journey("COURSES3M")
    .OpenCourse(coursePath)
    .Benefit(b => b.UseCouponTierAndExactExpiry())
    .RequirePaymentSetup(s => s
        .ForIndividuallyApprovedPurchases()
        .OfferEligibleWalletsAndCard()
        .ConsentText("Save a payment method for purchases I choose. "
            + "Nothing is charged today."))
    .WithFeatures(f => f
        .OptionalSubscriptionRenewal("continue-personal", r => r
            .ForCouponTier("personal")
            .DefaultSelected(false)
            .StartAtAcceptedBenefitEnd()
            .QuoteCurrentPlan("personal", cadence: "monthly")
            .RequireSeparateRecurringConsent()
            .ShowStartTotalCadenceAndCancellation()))
    .AfterClaim(a => a.OpenSelectedCourse())
    .BuildAction();

// Proposed Essentials composer, outside Store's dependency graph.
return Actions.Link("courses3m")
    .ForSite(site)
    .RequireSignIn()
    .Do(couponAction)
    .Prefill(p => p
        .Language("en")
        .Campaign("courses-three-months")
        .ProfileFromSignedInRecipient())
    .WithCard(c => c.Title("Personal access with COURSES3M"))
    .Sharing(s => s.ReusableInvitation().AllowRecipientCard())
    .Publish(hub, activity);

Sites.Registered resolves a configured site identity to a canonical HTTPS origin. site and coursePath are publisher-supplied, server-resolved inputs; deployment must resolve the course on that site and verify its current tier, availability and dependency coverage. AllowRecipientCard permits the authenticated recipient to obtain the same campaign invitation card after the flow, subject to publication policy. It shares an invitation to claim independently, never the recipient's claim, payment setup session, subscription record or account session.

The activity action result should contain a safe view model: offer title, target site display name, GUID link, QR image, phone-card route, copy/share actions and installation affordance. Privileged coupon references and the execution descriptor stay server-side. A recipient's card can use a separately attributed link only if that attribution is explicitly authorized and contains no exposed identity or payment data.

An additional recipient-specific overload may propose a name, organization or email for a private invitation. These are suggestions for the addressed recipient, never permission to change the authenticated account, replace its verified email, assign its buyer identity or create a customer for somebody else. Such invitations use a separate protected payload and recipient binding; they must not reuse the public campaign descriptor.

Configurable redemption features

WithFeatures is an extensible, typed feature list. Each registered feature has a stable ID and version, applicability rule, presentation contract, selection default, typed configuration, consent requirements and server handler. The Store action stores these declarations alongside the coupon reference; the neutral action contract does not grow a property for every coupon feature. Unknown required feature versions fail closed. Unsupported optional features are shown as unavailable and cannot be silently selected or submitted.

The initial registry supports optional subscription renewal for the coupon's Personal tier. Future add-ons, destination choices or onboarding features follow the same quote/consent contract, rather than injecting scripts or arbitrary provider fields. A feature that changes money, plan rights or external side effects must disclose its effect and record its own consent. A saved-payment-method prerequisite remains distinct from the optional feature list so declining renewal never blocks the baseline coupon.

The recurring feature quotes Admin/Tiers/personal server-side and supplies the exact first charge date, total including any applicable tax, currency, monthly cadence, cancellation deadline and cancellation route. If a total cannot yet be determined, collect the necessary tax/billing input before asking for recurring consent. A quote has an expiry and a semantic hash; repricing, changed dates, tax, currency, cadence or terms requires a new quote and deliberate consent. If the cap leaves too little time for the provider's trial requirements, mark renewal unavailable for that claim rather than moving the charge or extending the coupon silently. The claim records both a declined and an accepted renewal choice. Missing selection means declined, never implicit opt-in.

The unselected state says: “Personal access until . No automatic renewal.” The optional unchecked control says: “Continue Personal after for per month. The first charge is ; cancel before then in Manage subscription to avoid it.” Once checked, adjacent copy and the final button clearly state “Nothing charged today; then /month from .” Do not leave an unconditional “no automatic renewal” banner visible in the selected state. All displayed dates are in the viewer's time zone, with precise terms retained in UTC.

Builder validation

Construction is pure. Publication and execution are cold IObservable<T> operations. The fluent API must reject incomplete and contradictory descriptors before publishing: missing site, unknown handler/feature version, missing target, renewal selected by default, a renewal feature without separate recurring consent or complete pricing/cancellation terms, unsupported prefill keys, unsafe redirect, coupon not found, nonzero coupon price, unexpected tier, ineligible target, invalid term or missing publishing authority. Per-site capability checks distinguish “payment setup unavailable” from a bad coupon; a site lacking trial support can offer the baseline claim with the optional renewal feature visibly unavailable.

Store owns domain semantics: the coupon reference, plan benefit, selected package, setup prerequisite and final navigation. Essentials owns link issuance, expiry/revocation, login resumption and the reusable page shell. Payment-specific implementations stay behind the platform contract. Any cross-site handoff carries an opaque, narrowly scoped exchange handle; it never forwards a login cookie, provider token or arbitrary return URL.

Recipient journey and prefill rules

  1. Scan. Essentials resolves the GUID on its intended site. Anonymous requests reach sign-in/sign-up with an opaque return handle. A page view performs no coupon or billing write.
  2. Resume. After authentication, bind a fresh recipient claim to the verified home partition. Validate the site, action version, link state, coupon, target package and claimant eligibility again.
  3. Review the offer and features. Show the coupon already applied, Personal coverage, selected course, exact end date and CHF 0 due today. Renewal starts unchecked; show the optional recurring terms beside its control. Prefill the user's own name/email and permitted campaign defaults. Editable preferences remain editable; system-owned offer terms do not become client authority.
  4. Prepare future purchases. Explain: “There is plenty to explore for free. Add a payment method now for paid content you choose later. Nothing is charged today, and you approve each separate purchase.” Collect saving consent for that purpose. Separately, a selected renewal checkbox authorizes only the quoted recurring plan. Show Apple Pay, Google Pay or card as supported by the provider/device and the selected authorization purpose, with a card fallback.
  5. Confirm the selected branch. Unchecked renewal saves for on_session purchases and creates no subscription. Checked renewal collects authorization suitable for the disclosed future off_session recurring payments and creates one provider trial subscription with zero due today and the exact accepted trial end. The payment provider collects sensitive details. Returning to a success URL is only navigation: require authenticated evidence bound to site, customer, recipient, claim, provider session and accepted consent hash.
  6. Activate. Fulfil the claim once, wait for the subscription control plane to settle, unlock the selected course through the plan, then install its permitted dependency closure and material. A selected renewal is linked into this same claim lifecycle, not an independent manual activation racing a second subscription grant. Show durable progress and a recoverable failure if installation has not completed.
  7. Arrive. Open the selected course, or the prefilled destination named by the registered action. Provide “Show invitation QR” and “Add invitation to Home Screen” as separate actions. The phone icon opens the invitation card after the necessary session check; it does not execute the coupon again.

The full billing address required by an ordinary paid order is not automatically required for the baseline setup flow. Collect only what the provider and declared setup purpose need, then request invoice fields when the recipient later purchases something. A chosen subscription additionally collects what is needed to quote and consent to its exact recurring total. Existing values are retained; an empty prefill never overwrites a completed profile. Generic profile prefill is different from provider-hosted payment-field prefill: Stripe Checkout setup mode does not promise to prefill saved card fields. See Stripe's existing-customer behavior.

Proposed durable records

Names and locations in this table are proposed contracts to review during implementation. Create every node through a registered NodeType and its canonical verb. Activity and link handlers never hand-write a subscription or access assignment.

Record Ownership and purpose
Essentials action-link definition Site-scoped server-owned action descriptor, safe presentation, expiry and revocation; no payment data
Store/CouponJourney Authorized publisher's validated offer descriptor and reference to the Essentials action link
Store/CouponClaim under a server-controlled Store claim container One claim per site, normalized coupon offer identity and authenticated viewer, regardless of the number of QR links or tabs
Store/PaymentSetup Provider setup lifecycle and evidence; details specified in Payment Methods
Viewer-private payment-method reference Provider customer/method handles and minimal display summary, with consent provenance; never PAN/CVC or a client-set “verified” flag
Existing Admin/Subscriptions/{viewer} The canonical bounded Personal grant and, only after separate recurring consent, its linked provider trial/renewal state through the existing control plane
Existing plan unlock/install records Target access and installation provenance through existing framework-owned operations

A claim records its immutable subject, action/link provenance, coupon semantic revision, target site/package, accepted offer snapshot, each configured feature's version and explicit selection, a feature/terms hash, and separate saving-consent and recurring-consent text versions/timestamps. It also records provider evidence, frozen benefit instants, the accepted recurring quote if any, provider subscription identity, cancellation state, fulfilment phase, errors and repair history. The recurring snapshot includes first-charge date, amount, currency, cadence, tax treatment, cancellation terms and consent purpose. Material changes require fresh review; redemption counters changing are not offer-term changes. A hash detects mismatches but never substitutes for the server-owned snapshot or authenticated evidence. The coupon's complete node content is not a recipient-safe snapshot.

Recommended base progression: Prepared → SetupPending → SetupVerified → GrantPending → Active → Installing → Complete, with explicit Expired, Revoked, Cancelled and NeedsAttention results. An opted-in claim records additional durable RenewalConsentAccepted → TrialProvisioning → TrialVerified phases, followed later by RenewalActive or RenewalCancelled; the trial can be provisioned in the same provider flow as setup. These are phases of one idempotent claim, not parallel activation writers. The control plane owns transitions. Client input requests Continue, Cancel or Retry; it cannot set a result state, viewer, benefit expiry or provider identity. A successful setup followed by an installation fault is displayed as “Payment method saved; access active; course setup needs attention,” not “Payment failed.”

Authorization, replay and fulfilment

A GUID is an address, not a grant. Publishing a coupon-backed action requires an explicit authorized publishing role or policy; being a global admin alone does not confer read access to arbitrary content. The public signed-in page is a safe projection with a specific authenticated-viewer access rule. Neither _Policy.publicRead nor the Anonymous grant is used to make the claim payload readable. Protect Admin coupon notes, audit entries, counters and setup records independently from the invitation page.

GET, QR scanning, crawler previews, login callbacks and PWA installation never execute the action. Execution requires the authenticated confirmation and the framework's request-authenticity protection. Resolve the buyer from the authenticated principal, not from the posted claim JSON. Same-site redirects must resolve to registered internal routes; cross-site destinations require an allowlisted site and server handoff protocol. Reject scheme-relative, script, encoded external and tenant-confused return paths.

Take an atomic claim before external setup or benefit mutation. A deterministic key spans all links for the same coupon offer and viewer, so printing another QR cannot reset the offer. The handler returns an existing in-flight claim or completed receipt rather than starting another. Provider session creation has its own stable idempotency key, and an uncertain response is recovered by querying/reusing that attempt before another is opened.

Bind every provider request and callback to the claim's accepted feature/terms hash. The no-renewal branch carries only saving consent for individually approved on_session purchases; it must not request off_session permission or create a subscription. The selected-renewal branch carries an additional recurring mandate and an explicitly quoted trial end. Never infer it from a saved method, a customer ID, an older purchase, a query parameter or a prefilled checkbox. Switching off renewal before final submission invalidates the earlier quote/session. Once a provider trial exists, switching it off uses the explicit cancellation operation and records its verified outcome.

Wallet capabilities depend on this distinction. OfferEligibleWalletsAndCard offers only methods that support the current consent purpose. Apple Pay for a later customer-present purchase requires its wallet sheet again; it must not be represented as a stored on_session card. The selected recurring branch can support Apple Pay recurring authorization when provider/device capabilities and tests establish it. Google Pay setup/reuse support also requires concrete verification. A reusable card remains the baseline setup fallback; do not check renewal automatically just to make a wallet available. The Payment Methods design carries the authoritative provider details and any alternative for a wallet-only user.

Coupon budget reservation, release and final count need serialized authority in the coupon's owning hub or an equivalent durable atomic mechanism. Two new viewers cannot each take the last remaining redemption. An abandoned setup must release its reservation and never count as a redemption. A setup completed after reservation expiry requires a fresh budget decision. Unique-provider-event evidence prevents duplicate or reordered callbacks from advancing the claim twice.

Freeze the benefit dates before activation. Retry with the same dates and provenance. Wait for Subscriptions.ObserveActivationSettled, correlated to the claim's actual request; a previously active unrelated plan is not proof that this request succeeded. Extend the control-plane correlation contract if necessary. Count success durably, then resume target access/install work from its recorded phase. A failed write is an actionable phase, not a swallowed exception and a green receipt.

With renewal selected, create exactly one zero-due provider trial using the accepted expiry; verify its customer, tier, recurring total, currency, cadence, trial end and mandate before binding it to the canonical subscription. Extend the subscription control plane to represent the trial without adding a paid month or resetting the coupon expiry. Do not invoke a manual grant and the existing paid checkout fulfilment independently: that would race the provider handle, cadence and expiry. Renewal invoices advance the existing subscription from verified provider period evidence exactly once. Cancelling before the first charge stops renewal while preserving coupon access through the accepted benefit end; the provider's later deletion event must not revoke that free period early. Trial failure, cancellation races, invoice failure and payment-method removal require explicit states and repair policy as specified in Payment Methods.

The current coupon count stores a viewer list and per-package entries; update it from the owning hub's current state, not an old snapshot. Preserve existing unique-viewer budget meaning. A viewer already in RedeemedBy does not receive a fresh ninety-day term by scanning a new campaign link. Reconcile their prior claim/subscription provenance first and return the existing outcome; where legacy evidence cannot prove the granted term, route to support rather than minting another benefit.

Compatibility constraints discovered in the existing code

The implementation must account for these measured behaviors, even where prose elsewhere states a broader intention:

Do not add a Store/Coupon NodeType alias, a second subscription model, or a second coupon database. Store/Coupon is a type within the Store module, so these design nodes live below Store/Coupon/Design; executable runtime additions remain in the module that owns them.

Errors and recovery

Condition Recipient behavior and durable outcome
Link expired or revoked Explain the invitation cannot be used; no payload or side effect leaks
Signed in to the wrong invited identity Preserve the link while offering account switch; never assign the invitation's email to the account
Coupon changed, invalid or target no longer covered Requote before setup; show actual terms or refuse
Setup unavailable Explain that the site cannot prepare payment methods; do not fall back to charging or silently waive the configured prerequisite
Setup cancelled or provider failure Resume the offer; no redemption count and no plan grant
Success redirect arrives before evidence Show “Confirming your payment method”; a finite wait becomes a resumable status, never proof of success
Setup verified, grant failed Saved method remains manageable; claim records repairable activation failure; no “access ready” message
Trial created, claim cannot activate Keep durable evidence, suspend/cancel its future billing as appropriate, expose repair; never leave a hidden charge scheduled
Renewal terms changed after selection Return to the exact quote with the checkbox requiring a new deliberate choice; never substitute a higher price or earlier date
Grant active, install failed Keep the bounded grant and retry install only; no fresh setup or extended term
Benefit expires with renewal unselected or cancelled Existing lapse/revocation rules apply; no automatic charge or new subscription
Benefit expires with accepted renewal still active Provider bills only the accepted recurring subscription; verified lifecycle evidence continues the plan, and failure follows the disclosed payment-failure policy
Already has a paid/permanent/higher/manual plan Preserve the plan and provider handle; do not consume/replace the existing benefit or create overlapping recurring billing

Tests and release evidence

Each new NodeType has executable Test/ cases and a rendered Tests area. Extend the existing Coupon, Licensing, Subscription, Order/Catalog and Stripe suites at their owning boundary. Required coverage:

  1. Descriptor/feature round-trip and unknown-version rejection; missing capability; unchecked renewal default; allowlisted site and same-site return validation; malicious and stale prefills; course tier and scope checks; authenticated viewer derivation.
  2. Anonymous access to the offer payload denied, signed-in fresh viewer can review it, Admin coupon/setup/audit rows remain inaccessible. Use a genuinely different email account with no entitlements or all-access plan; changing the sign-in provider alone may resolve the same account.
  3. Exact day-based expiry across UTC boundaries, leap day and viewer time zones; late-December clamp; coupon invalidation between quote, setup and fulfilment; retry cannot move the start, end or first-charge time. Expired quotes, changed recurring total/tax/currency/cadence/terms and mismatched consent hashes require fresh consent.
  4. Two tabs, two QR links, duplicate provider deliveries, out-of-order delivery, restart after every persisted phase, lost provider response and last-budget-slot contention. One setup attempt, one benefit and one count survive each race.
  5. Forged success URL, unauthenticated webhook, wrong customer/site/session, incomplete setup, removed method, expired reservation and reused setup from another viewer never activate a claim.
  6. Both branches have zero due today and no immediate charge. Unchecked renewal creates no provider subscription, recurring mandate or off_session authorization. Checked renewal creates exactly one trial with the accepted end/total/cadence and separate recurring consent. A changed/omitted checkbox, reuse of another consent, or wallet capability fallback cannot opt anyone in. Later separate purchases display item/total and require fresh explicit confirmation.
  7. A new claim writes no eternal package entitlement. Expiry revokes its plan-minted access while an independently purchased entitlement remains. Existing paid/permanent/higher-tier/manual plans, Stripe ID and cancellation control survive unchanged. Coupon trial and ordinary plan checkout cannot race into two subscriptions.
  8. Activation/installation failure is visible and repairable; retry neither creates another setup/subscription nor grants another term. Removing the saved method never revokes already-earned coupon access; any active renewal is handled by an explicit cancellation/replacement policy. Cancelling an opted-in trial before first charge preserves the free benefit and prevents its first invoice charge. Replayed invoices never add multiple paid periods.
  9. Actual iPhone and Android scans, sign-up/login resumption, provider round trip, home-screen card opening, QR contrast/readability and cross-account logout behavior. Browser-only mocks cannot establish wallet or installation behavior.

Run the repository shape, compilation and manifest gates; execute the affected suites on a local mesh before release. Core payment-contract changes precede its CD platform set, then unified dependent pins, then the provider and Store consumers. Prove optional-provider absence still compiles. Activate a campaign only after real target-site configuration, claim access rules and payment setup evidence are verified in the supported pipeline. This draft authorizes no live coupon edits or campaign activation.

Implementation workstreams

Create GitHub issues with labels astra and design:qr-coupon-journeys, linking to the committed design nodes. The implementation breakdown is:

Workstream Deliverable and dependency
Essentials action links GUID descriptor registry, authenticated continuation, explicit execution, idempotent action receipt and site routing; see Essentials plan
Coupon fluent adapter Proposed builder, versioned configurable features, immutable quote/feature consent, target validation, authorized publication and safe offer projection; depends on action contract
Payment setup and optional renewal Core provider capabilities, Stripe setup/trial implementation, separate saving/recurring consent, private method management, explicit later checkout and cancellation; see Payment Methods
Durable coupon claim Atomic viewer/offer claim and budget, one grant/trial saga, correlated activation, revocable target unlock and resumable install; depends on verified provider evidence
Phone invitation card Shared QR/card/PWA UI and recipient sharing policy; owned by Essentials with Store presentation data
End-to-end release Fresh-viewer access and no-charge acceptance tests, real-device evidence, operational repair surfaces and staged campaign activation

Before coding, resolve the exact campaign target course/site, the publisher permission, cross-site handoff contract, reuse eligibility for an existing saved method, removal/retention policy, payment-failure policy and treatment of pre-existing manual grants. Resolve the existing December cap versus a separately authorized full-ninety-day campaign explicitly. The settled rules remain zero due today, unchecked renewal by default, separate deliberate recurring consent, and individual approval for each unrelated purchase. Actual issues and final API names should replace these workstream labels as implementation is scheduled.

Reconnecting…
The server was updated. Reloading the page to pick up the latest version.