Download the PHP package goldnead/statamic-products without Composer

On this page you can find all versions of the php package goldnead/statamic-products. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package statamic-products

Statamic Products

The thing that is sold, with a name, a list price, and the access it grants.

statamic-payments knows what something costs. statamic-offers knows how it is presented. statamic-entitlements knows that somebody has access to it. Between them sat a hole: the thing itself existed nowhere, so every site invented it again — and on one of them it got invented twice, as member_packages and access_packages, and the two drifted.

This addon is that missing middle and nothing more. It keeps, it does not deliver. It keeps what a purchase opens (which courses, files, dates, session credits and whether the members area opens) as an access record; what a course shows, which file is streamed and which credit is written stays with the sibling addons and the website, which read it from here.

Installation

Products then live under Utilities → Products.

Each product also has a screen of its own — Offers and buyers in a row's actions — that shows the way back from the product: every offer that sells it, as lead product or in a bundle, and the last fifty people who paid for it, including as an order bump or a follow-up. The offers section is there only with statamic-offers installed; the buyers section only once the payments tables exist. A missing sibling means no section, not an empty one.

Usage

Create products under Utilities → Products. From there they behave like any other entry in the payment catalogue, so nothing else has to learn about this addon:

An offer points at a product by its handle, exactly as it pointed at a config line before:

What a product is

Field Meaning
handle What offers, payments and invoices call it. Unique across every brand, and frozen once the product has been sold.
name What is bought. Goes into the order confirmation and onto the invoice (§ 312j BGB).
type What kind of thing it is. An answer, not an instruction — see below.
ref The pointer at the thing of that kind. Empty for a download.
amount_cent The list price. 0 is allowed and means free. An offer may undercut it; nobody else may.
currency Empty means the shop currency.
digital A tax fact, not a medium: it decides the place of supply and with it the mandatory notice (§ 3a UStG). No default — whoever creates a product answers it.
grants The access a paid copy opens, as a list. Empty is normal.
active Retired rather than deleted.
brand_id Zero on every single-brand install. An agency with three brands gets three catalogues. Travels in the catalogue entry: statamic-payments 1.24.1 and newer stamps a follow-up charge with it instead of inheriting the brand of the payment it follows, and reads zero as "names no brand".

What is deliberately not here: discounts, sales copy, placement, bundling. That is the offer level and it already exists in statamic-offers. A product has a list price and no opinion about how it is advertised.

The kind is an answer, not an instruction

Kind What it is ref points at
download PDF, workbook, recording nothing — the thing is the product
access A course, a members area, a community a Statamic entry id
event Live event, workshop, concert, webinar an event uuid in statamic-events
sessions A package of appointments a booking funnel handle in statamic-booking
cohort A programme with a start, an end and a group a Statamic entry id
feed A paid podcast or newsletter a Statamic collection handle

Nothing here delivers anything. Naming a product an event says it is a live date; it does not reserve a seat. Kajabi and Podia go the other way — there the product type is the delivery, the course type is the player — and that road ends in building a course player, a community engine, a scheduler and podcast hosting. Delivery stays on the website and in the sibling addons that do that job. Some of them do not exist yet.

Which is why an unresolvable pointer is shown as unresolved rather than refused. A pointer has three states, not two:

statamic-events and statamic-booking are optional. Install them and their pointers start being checked; leave them out and products for those kinds can still be filed.

How it reaches the checkout

Two seams on statamic-payments' Catalogue, and they answer different questions.

Requires goldnead/statamic-payments 1.15 or newer for Catalogue::contribute(). Without it a product could be bought but would not appear in any picker — which is how the product select in the offer form used to show three of six products and then refuse the save with a 422.

Config wins

A handle may exist both here and in config/statamic-payments.php. The config file wins: a price in version control was written on purpose, and a deploy must not be silently overruled by a row in a table.

Silent is right for the answer and wrong for the screen, so the collision is shown — a badge on the row and a warning in the form. Two truths about one price is the illness that had a checkout charge 330 while the catalogue said 332, and the person who noticed was a customer.

Nothing has to be migrated. A site whose prices live in a file can install this addon and never notice it.

Accesses

A product grants slugs (grants). An access is the record behind one slug: same handle, and it keeps what that slug contains. Utilities → Accesses (own permission, access product-accesses utility), with a list and a detail page built like the product's.

Field Meaning
handle The grant slug. Unique across every brand, frozen once a grant in statamic-entitlements carries it (checked only when the entitlements table exists). A granted access cannot be deleted either.
name, description, cover What the buyer's account shows. cover is an asset (container::path) or a URL.
active, brand_id As on a product.
opens_members_area Whoever holds it gets into the site's members area.
contents Ordered list of {kind, ref, label}. For files the position is part of the download id.
credits Credit lines {line, session_type, kind: one_time\|subscription, count, per_month, valid_months, ended_at}.

Content kinds: access (another access, nested; a cycle is refused), course (an entry id of the statamic-courses course collection), file (an asset, container::path), event (a uuid in statamic-events). A site adds its own kinds:

Every pointer shows the same three states as a product's ref: found, gone, cannot be checked. A site's own kinds are "cannot be checked" unless the site registers a resolver for them (see Setting it up on a site); only the site knows what they point at.

In the course list, an entry of statamic-courses whose kind field is material is labelled "Title (Material)" (English CP: "Title (material)"). The field is read directly; a missing kind means a course, and statamic-courses is not required.

Credit lines: line is assigned by the model, starts at 0 and is never handed out twice, not even after a line was deleted (credit_lines_issued counts). It is the <index> in the site's idempotency key, so reusing one would make a later credit look like it already happened. An import may bring its own line; the counter then moves past the highest. Once the access has been granted a line can be ended (ended_at), not deleted, and an ended line stays ended. These rules live in the model, so an import that bypasses the form is held to them too (a breach throws a ValidationException). The screen counts lines from 1; line itself stays as stored.

Session types come from the site, with names:

Once any are registered the form picks from them and refuses anything else (a type already stored stays savable and shows as gone). Without a registration the field is free text and every value shows as "cannot be checked".

Two tabs, one access. The detail page sends a fingerprint of the state it loaded. If somebody saved in the meantime, the save is refused with HTTP 409 and a message instead of silently overwriting the newer state.

Files and the cover are picked with core's own assets field and asset browser; the stored value is the asset id, container::path.

Which containers. An access is sold, so a client's private files must not end up in one. The site names the containers an access may take files from:

Without that call every container is offered except the ones of statamic-clientrooms (its statamic-clientrooms.container handle and the per-brand <handle>-<brandId> variants). Other private containers are not recognised; a site that has some registers its list. The server refuses files and covers from any other container; a value already stored stays savable and is shown as gone with a note. With only one allowed container the container dropdown disappears.

A container that holds private files next to the sold ones (an inbox folder, imported files) can be narrowed to one folder with a key: AccessContainers::allow(['private_downloads' => 'downloads']). The picker then opens in that folder, and the server refuses any file outside it (also ../). The picker itself can still browse out of the folder, because core hides subfolders in a restricted picker and the files sit in a subfolder more often than not. Already stored values outside the folder stay savable.

A granted access cannot be deleted, through the screen or through the model.

On the product, "Opens" is a picker over the accesses of the current brand and still stores slugs, so payments and entitlements notice nothing. A slug without an access record stays valid and is listed below the picker as unresolved, not as an error. ref becomes optional when one of the granted slugs is an access, because the access then keeps the contents.

Setting it up on a site

Everything a site tells this addon goes into the boot() of one of its service providers:

A site's own kind with names. Without more, a registered kind is a text field and the person filling the form has to know the identifier. Give it an options source and, optionally, a resolver:

With goldnead/statamic-entitlements (^1.4) installed there is nothing else to do: the addon puts its own PackageResolver in place of entitlements' empty default, and a grant on an access then covers everything the access contains. A resolver the site binds itself always wins, whatever order the providers run in; the addon only replaces NullPackageResolver. To keep bundles off entirely, bind a resolver that returns []. Without entitlements the addon binds nothing and works as before.

One limit: entitlements' EntitlementManager is a singleton and keeps the resolver it got when it was first resolved. If something resolves it before this addon's provider has registered (in an earlier provider's register(), say), it keeps the empty default and this resolver does not apply.

With entitlements 1.6 or later the addon also announces every access by name to entitlements' product catalogue (the container tag entitlements.product-catalog). The grant form under Users > Entitlements, the limits form and the "Zugänge" section on a user page then offer a picker with names instead of a slug to type. Retired accesses are listed too, marked "(inaktiv)", because existing grants on them still apply. With a brand chosen only that brand's accesses are offered; with none chosen all of them, each with its brand name. Read when a form opens, so a new access is in the picker on the next page load. Older entitlements versions never ask, and nothing changes there.

What a grant covers

The same rules for the resolver and for Accesses below:

Reading accesses

find() also returns inactive accesses, and expand() and contentsOf() answer for them as for active ones, because existing grants stay valid. expand() is the exact inverse of the resolver: for each slug it lists, the resolver names this access.

What find() returns is a snapshot: after a save, call find() again. Accesses are read once per request; a long-running console process (a daemon, a loop in a command) sees writes from other processes only after AccessGraph::forget(). Queue workers and Octane reset it between jobs and requests on their own. Before the migration has run, find() throws.

Credits apply to the access granted directly, never through nesting. creditLines() reads the access itself and nothing it contains, so nesting a coaching access into a bundle does not credit sessions twice.

A private link on an event

statamic-events gates nothing; an event's online_url is public. For a paid webinar, an event in an access can carry a link for buyers (the form field under the event, stored as buyer_url, http(s) only). It never appears in contentItems() or contentsOf(). It is read only for someone who holds the access:

Nested accesses count, active does not; a revoked, expired or pending grant hands out nothing. Showing the link (account page, mail) is the site's job.

What it will not do

Requirements

Optional: goldnead/statamic-brand-context for multi-brand catalogues, goldnead/statamic-entitlements ^1.4 for grants that cover what an access contains.

Licence

Proprietary.


All versions of statamic-products with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
statamic/cms Version ^6.0
goldnead/statamic-payments Version ^1.15
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package goldnead/statamic-products contains the following files

Loading the files please wait ...