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 App Api Laravel Package

goldnead/statamic-app-api

A Statamic addon that exposes your site’s content through a clean, app-ready JSON API. Connect mobile apps and custom frontends to collections, entries, and taxonomies using Laravel-powered endpoints built for simple integration and flexible configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is the HTTP/API layer around statamic-payments (and optionally statamic-teams) that exposes subscription billing to your front end — the pieces you touch day-to-day are the billing/subscriptions/{subscription}/cancel endpoint, the serialized subscription payload, and the CP settings under Checkout.

First steps:

  1. Bump the dependency before anything else. v0.2.1 requires statamic-payments >= 1.29.2. Cancellation now delegates to Support\Cancellations::cancel() rather than a local copy of the sequence, so the old behaviour is gone the moment you upgrade. Pin the constraint and run composer update statamic-payments in the same deploy.
  2. Look at a subscription response first, not the source. The payload is the contract your front end consumes: it now carries both ends_at (new) and the existing ended_label, plus copied_to on the cancel response. Dump one cancelled-but-still-paid subscription and one fully-ended subscription side by side — the label semantics differ and that's the whole point of this release.
  3. First use case: the cancel button. POST billing/subscriptions/{subscription}/cancel returns the same answers and sends the same mail as before, so an existing integration keeps working; you're mainly re-reading the label and picking up the new response fields.

The single most important mental model from this release: a cancelled subscription whose paid term still runs is not "ended". It is "cancelled, running until <end of term>" (Gekündigt, läuft bis …), and only after the term does it become Beendet am <end of term>. That mirrors Portal\Display::ending() in statamic-payments — treat that class as the source of truth for any ending-related copy you render yourself.

Implementation Patterns

Render ended_label verbatim; don't rebuild the sentence. The package now produces the exact portal sentence. If your front end concatenates its own "Ends on …" string, you will diverge from the CP and from the confirmation mail. Use ended_label for display and ends_at for logic (sorting, filtering, "can I still use this?" checks, calendar entries).

Distinguish state from label. ends_at is the machine-readable field — the end of the paid term, regardless of whether the user cancelled. Use it for access checks and for deciding whether a resubscribe flow makes sense. Use ended_label only for human-facing text.

Treat cancellation as a fire-and-forget command with a read-back. The endpoint runs payments' own sequence, so it may send mail and write a log entry. Pattern:

$response = Http::post("/billing/subscriptions/{$id}/cancel");
// then re-fetch the subscription and re-render from the fresh payload

Don't optimistically set local state to "ended" — the subscription is still live until ends_at passes.

Handle copied_to explicitly. When the cancelling user's address differs from the team's billing.email, a second, separate mail goes to the team billing address and copied_to tells you where. Useful for:

  • Showing "A copy was sent to billing@…" in the UI,
  • Audit logging in your own app,
  • Detecting the case where the team billing address is a shared inbox and the user expected only their own confirmation.

Respect billing.cancellation_copy_to_team (default on). If a tenant complains about duplicate confirmation mails, this is the switch — exposed in the CP under Checkout, so it's an ops/comms decision, not a code change. Surface it in your own admin UI only if you already proxy other billing settings; otherwise just document where it lives.

Use the delegated sequence as your integration point. Because cancellation is no longer a copy, any custom hook you had bolted around the endpoint (extra logging, webhooks after cancel) should observe the result, not re-implement the steps. If you need to react to cancellation, hook the mail/log/event side in payments rather than re-running logic in your controller.

Version-gate the team copy. statamic-teams is optional; when it's absent there is no billing.email and no copy. Don't assume copied_to is populated — treat it as nullable.

Gotchas and Tips

  • The log entry no longer carries via: app-api. This is the sneakiest change in the release. If you filter, alert, or build reports on that marker to separate API-initiated cancellations from portal ones, those queries now silently return nothing. Migrate to whatever discriminator payments writes, or add your own explicit log entry in your controller in addition to calling the endpoint.
  • Hard dependency floor. statamic-payments >= 1.29.2 is not advisory: the new code path calls Support\Cancellations::cancel() and the label logic mirrors Portal\Display::ending(). On an older payments version you get class-not-found or drifting copy. Add the constraint to CI so a stale lock file fails loudly.
  • German strings are upstream. Gekündigt, läuft bis … / Beendet am … come from payments, not this package. If your app ships other locales, don't string-match on these labels — match on ends_at and on subscription state. Any test asserting on literal label text is a brittle test.
  • The "day of cancellation" bug is fixed, but cached payloads aren't. If you cache subscription JSON (response cache, SPA store, queue payload), old ended_label values survive the deploy. Invalidate subscription caches as part of the upgrade.
  • Cancelling twice. The delegated sequence handles the already-cancelled case the same way the portal does, but your UI should still hide the button once a cancellation is recorded — ends_at plus whatever cancelled-at field your payload exposes.
  • Copy-mail edge cases: same address on both sides → no copy (by design, not a bug). No team, or team without billing.email → no copy. Empty string vs null in billing.email is worth normalising on your side before you compare.
  • Debugging tip: if a cancellation "does nothing" after the upgrade, compare against the portal first. Same subscription, cancel via portal, compare the resulting label, mail, and log entry. Any difference is your layer, not payments.
  • Extension point to prefer: Portal\Display::ending() and Support\Cancellations are the stable seams. If you need ending copy or cancel semantics elsewhere in your app (emails, PDFs, API resources), call into those rather than duplicating this package's formatting.
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.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings