Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Statamic Offers Laravel Package

goldnead/statamic-offers

Statamic Offers is a Laravel package that adds offer management to Statamic CMS. Editors can create and organize offers in the control panel, while developers can surface them in Antlers templates for promotions, deals, or listings.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What this release actually is. v1.18.0 is a different kind of change from the one we assessed at v1.15.0, and the two should not be conflated. The prior release was an additive money read-model (Basket::firstPaymentCent()), a presentation-layer fix with no behaviour change. This one introduces a new domain surface: the funnels map now stores offers as objects — type: offer, id = handle, number = seats accepted in the period — and the package now declares a hard dependency on statamic-brand-context ^1.17. That is a data-shape change plus a dependency change in the same release, which is materially more invasive than the last one.

Version gap. We are reviewing 1.15.0 → 1.18.0 with no recorded assessment for 1.16 or 1.17. The brand-context floor of ^1.17 strongly implies coordinated releases we have not read. Before this goes on a roadmap, someone must diff the changelog for 1.16 and 1.17; assuming 1.18.0 is the whole story is exactly how an unlisted breaking change reaches production.

Architecture fit. Coherent for a Statamic addon: the funnels map becomes a typed reference registry rather than loose identifiers, offers are addressed by handle (content-friendly, stable, human-readable), and the seat count is stored alongside the reference rather than derived elsewhere. The type discriminator is the right call — it implies further referencable types later without another schema break. Two caveats. First, this package is now explicitly a Statamic addon with a Statamic addon dependency, so it no longer sits cleanly in a plain Laravel service; any headless or API-only consumer inherits the Statamic runtime and content model. Second, number = seats accepted in the period introduces a quantity into a package we previously treated as a money formatter. Seats are billable units. That is the seam to watch.

Integration feasibility. Medium, and gated on dependency resolution rather than code. The implementation itself is likely small — read the new object shape, render it. The work is (a) resolving statamic-brand-context ^1.17 against whatever is currently pinned, (b) auditing any code that reads funnel map entries assuming the old shape, and (c) deciding whether seat counts now feed the "due today" calculation. Step (a) is the schedule risk: an addon-to-addon constraint can force a cascading upgrade of Statamic, brand context, and anything else sharing those transitive constraints, and Composer will not negotiate.

Technical risk. Medium, with the prior invariant still live and two new ones added:

  • Seats and money are now adjacent. If pricing is per-seat, firstPaymentCent() must account for number or the "due today" line will be correct for one seat and wrong for many. Nothing in the notes says the two are connected. Assume they are not connected until proven, and if they are not, that is a product decision to schedule.
  • Funnel data shape. Any of our templates, imports, or API resources that read the funnels map as a scalar/handle will break or silently render an empty offer. This is a content-model change wearing a feature-release costume.
  • Dependency coupling. We inherit brand-context's release cadence, PHP/Statamic floor, and defects. A patch to brand-context can now break checkout-adjacent code.
  • Multi-brand ambiguity. Brand context plus a period-scoped seat count plus our unresolved multi-currency questions is a three-way interaction. Which brand's currency, which period boundary, which seat count.
  • Forward compatibility of type. A discriminator invites new types. Anything we write that switches exhaustively on type: offer will fail closed on the next one.
  • Unchanged prior risk. The firstPaymentCent() / charge-path dual source of truth is untouched by this release and remains our most expensive latent defect.

Key questions.

  1. Do 1.16.0 and 1.17.0 contain breaking changes we have not assessed, and does 1.18.0 require them?
  2. Does statamic-brand-context ^1.17 force a Statamic or PHP upgrade, and what else in our dependency graph shares those constraints?
  3. Is the funnels map change backward compatible for existing stored entries, or is a migration/re-save of content required?
  4. Do number seats feed pricing, entitlements, or both — and does firstPaymentCent() include seat quantity today?
  5. What is the period boundary for "seats accepted in the period" — billing period, calendar period, or contract term — and does it align with the invoice?
  6. How are seats decremented on cancellation or downgrade mid-period, and is the count eventually consistent with what we bill?
  7. Which brand's pricing, currency, and tax rules apply when a funnel is served under brand context — and does brand context change the answer to our earlier tax-inclusive/exclusive question?
  8. Are other type values planned, and is the registry extensible by us?
  9. Does resolving offers from the map hit the Stache or trigger per-entry lookups when a funnel list renders many offers?
  10. Do the earlier questions on zero-decimal currencies, empty baskets, and 100% coupons still stand unanswered — and has any of them been addressed in 1.16–1.17?

Integration Approach

Stack fit. Statamic addon within a Laravel app. Two distinct surfaces now: the money read-model we scoped at 1.15 (checkout "due today") and the funnels/offers map (content-side configuration). Treat them as separate workstreams with separate owners, because they fail differently — one is a silent arithmetic drift, the other is a content-shape break. Server-rendered Statamic templates consume both; API consumers should still receive firstPaymentCent() as a precomputed integer and should receive funnel/offer data resolved server-side, never re-derived client-side.

Migration path. Do not treat this as a Composer bump. Sequence: pin 1.18.0 on a branch, let Composer resolve statamic-brand-context ^1.17, and inspect the full lockfile diff before touching application code. Diff the 1.16/1.17 changelogs. Audit every consumer of funnel map data. Only then decide whether 1.18.0 is a maintenance upgrade or a feature we adopt — they have different timelines and should be separable.

Compatibility.

  • Dependency graph. Resolve brand context in isolation first. If it drags Statamic or PHP forward, the blast radius is the whole site, not the checkout.
  • Content model. Existing funnels map entries must be inspected in a staging copy of production content. If the shape changed in place, templates will render blanks rather than error — the worst failure mode, because it passes tests and looks fine until someone reads the page.
  • Seat reconciliation. If seats are billed, reconcile number against the quantity on live subscriptions before enabling anything downstream.
  • Prior surface. netCent() remains unchanged; re-run the 1.15 parity harness to confirm the money path did not move while we were upgrading around it.

Sequencing.

  1. Read 1.16/1.17 changelogs; confirm scope of 1.18.0.
  2. Resolve statamic-brand-context ^1.17 on a branch; review lockfile and platform requirements; estimate cascade.
  3. Export a staging copy of funnels map content; diff old vs new shape; identify every template/resource that reads it.
  4. Decide and record whether seats are in scope for pricing. If yes, extend the parity harness to {seats} × {trials, coupons, setup fees, bumps} before any UI change.
  5. Ship the dependency upgrade alone, with no functional change, and watch for 48 hours.
  6. Ship the funnels/offers rendering change behind a flag; verify offer resolution and seat counts against source-of-truth subscription data.
  7. Re-run the 1.15 "due today" parity harness as a regression gate, not a new task.
  8. Confirm checkout conversion and "amount mismatch" ticket volume are unchanged or better.

Operational Impact

Maintenance. Two invariants now, not one. The first is unchanged: firstPaymentCent() must equal the gateway capture, defended by a parity test and a PR checklist for coupons, trials, setup fees, and bumps. The second is new: the funnels map schema and the type registry must be versioned, and any consumer that switches on type must fail open with a visible error rather than a blank. Add brand context to the dependency watch list — a Renovate/Dependabot rule plus a named owner, because an addon we do not own can now break a checkout-adjacent package.

Support. The money-side benefit from 1.15 still holds: one canonical figure to quote. This release adds a support surface rather than removing one — "why does this funnel show three seats," "why did my offer disappear," "why is this brand priced differently." Those are configuration and entitlement questions, not billing disputes, and they belong to whoever owns funnel setup. Brief them, and give them a read of the funnels map in the control panel before the release lands, not after.

Scaling. The seat count is the scaling concern. number per offer per period implies a computation or a stored counter; either it is recomputed on read (potentially expensive across a long funnel list) or it is stored and can drift. Ask which, and ask what happens at period rollover — a batch job that resets counters is a new operational dependency, and a missed run means wrong entitlements for every customer simultaneously. Offer resolution through brand context may also add per-brand cache segments; confirm the Stache invalidates correctly when brand context changes.

Failure modes to watch.

  • Silent content break. Funnel entries in the old shape render empty rather than throwing. Highest-likelihood failure in this release.
  • Dependency cascade. Brand context 1.17 drags Statamic or PHP forward and breaks an unrelated part of the site.
  • Seat/billing divergence. Displayed seats and billed quantity disagree, producing an invoice dispute that looks like the 1.15 bug but has a different cause.
  • Period boundary mismatch. "Accepted in the period" resolved differently by the funnels map and by billing.
  • Type-registry forward break. A new type arrives in a patch release and our exhaustive switch renders nothing.
  • Brand/currency crossover. Brand context determines currency and tax treatment; our unresolved tax-inclusive and zero-decimal questions now have a second dimension.
  • Unchanged: money drift. Any future change to coupons, trials, setup fees, bumps, or now seats must be made in two places.
  • Unchanged: stale state. Funnel composition or seat count changing after render but before submission.

Ramp-up. Roughly a day for a Laravel/Statamic engineer to implement the funnels-map rendering; the estimate is dominated by dependency resolution, which is unpredictable and not the engineer's to control. Two briefings are required before work starts: the billing owner on whether seats are billable, and the content/funnel owner on the new object shape and what existing entries need to become. Do not let the checkout team infer the seats-to-price relationship from the code, and do not let anyone treat 1.16 and 1.17 as covered by this review.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource