Ownware
Home›Deliora›Manual
Deliora · Manual

Deliora Manual, as shipped in the download

Deliora — User Manual

A digital-product storefront: checkout, fulfilment, download links and licence keys. Version 3.1.1.

About this manual

Every statement here was written by reading Deliora's own source, and each one carries the file it came from in a bracketed src marker. Paths are relative to the folder this docs directory sits in. If a sentence disagrees with the application, the application is right and this manual has a bug — the markers exist so you can check rather than take our word for it.

Nothing is described that the code does not do. Where something is deliberately limited, that is said plainly rather than left out.

What Deliora is

A storefront that takes an order through a payment provider, fulfils it on the provider's webhook, and issues download links and licence keys [src: app/src/Deliora.php:3].

Everything in the domain layer is plain PHP and database work with no reliance on request globals, which is what lets the test suite exercise the real code paths offline [src: app/src/Deliora.php:6].

The sales gate

Deliora can be run with selling switched off, and the gate is a single function every checkout entry point calls first [src: app/controllers/app.php:16].

With sales_live off, checkout routes render a 404 and stop; the storefront shows "Available soon — get notified" in place of those links, so only a stale or direct hit ever reaches the gate — and the demo-mode fake fulfilment is unreachable from the public interface [src: app/controllers/app.php:10].

The webhook endpoint is deliberately not gated [src: app/controllers/app.php:13]. It is authenticated by the provider's signature rather than by the gate, and it has to keep serving refunds and updates for orders that already exist. Switching selling off must not strand a buyer who bought yesterday.

Payments and fulfilment

The provider is chosen in settings, falling back to the value in the configuration file and finally to demo mode; an unrecognised provider is forced back to demo rather than accepted [src: app/src/Deliora.php:26].

Secrets are write-only in the interface and are never echoed or logged [src: app/src/Deliora.php:16]. A settings value overrides the configuration file only when it is non-empty [src: app/src/Deliora.php:17], so clearing a field falls back rather than blanking your live secret.

Download links

A download token is 48 hexadecimal characters, and anything that does not match that shape is rejected before a query is made [src: app/src/Deliora.php:353].

Four things are then checked, and each has its own answer [src: app/src/Deliora.php:345]:

ResultMeaning
404Unknown or malformed token, or the file is missing
410Expired, exhausted, or the order was refunded
200Served, and the use is claimed

The order must still be paid [src: app/src/Deliora.php:357], so a refund closes the link.

The file cannot be escaped to

The file lives outside any web-served path, denied by both the server configuration and the router, and is referenced by database id — user input never touches the path [src: app/src/Deliora.php:348].

Even so, the resolved path is checked against the real base directory with a prefix comparison, on top of taking only the base name [src: app/src/Deliora.php:365]. Two independent defences against the same class of attack.

The use is claimed atomically

The counter is incremented by a conditional update that only lands while the count is below the maximum, so two racing requests can never exceed the limit [src: app/src/Deliora.php:371].

If the claim does not land, the download is refused rather than served [src: app/src/Deliora.php:374]. The count is not a display figure; it is the thing that decides.

Licence keys

A key is verified cryptographically first, then looked up [src: app/src/Deliora.php:400]. The SKU is compared with a constant-time comparison [src: app/src/Deliora.php:403], and a revoked licence is refused [src: app/src/Deliora.php:404].

Activation is idempotent per domain

Re-activating a domain that is already activated succeeds without consuming a slot [src: app/src/Deliora.php:418].

A new domain consumes one atomically — the update only lands while activations used is below the limit [src: app/src/Deliora.php:432]. Over the limit, the answer names the reason and reports how many are left [src: app/src/Deliora.php:437].

And there is a guard on the guard. If two requests activate the same new domain at once, one wins the unique index and the other's insert fails — at which point the loser's slot is refunded rather than silently consumed [src: app/src/Deliora.php:443].

That is the failure mode that would otherwise burn a customer's activation for nothing, and it is handled.

Domains are normalised before comparison — scheme, path, port and www stripped, then lower-cased [src: app/src/Deliora.php:383].

The buyer portal

A buyer enters an email address and receives a magic link.

The response is always the same whether or not the address is known [src: app/src/Deliora.php:495]. An invalid address, an unknown customer and a customer with no orders all return exactly what a real one does, so the form cannot be used to discover who has bought from you.

The link is a 48-character token, valid for one hour and single use [src: app/src/Deliora.php:505], and the consumption is race-safe: the update lands only while the token is unused, and a token that loses that race is refused [src: app/src/Deliora.php:523].

Comparison pages, and the honesty invariant

Deliora renders competitor comparison pages, and they carry a rule worth knowing about as a buyer of the product:

A comparison row is only ever rendered when its source URL is non-empty — every visible competitor price carries a live citation [src: app/src/Storefront.php:8].

It is enforced twice: the query will not return a sourceless row, and the view re-checks each row before rendering it [src: app/src/Storefront.php:9].

That is a structural guarantee rather than an editorial policy. A comparison figure with no source cannot reach the page even if somebody puts one in the database.

Users and roles

Deliora is gated by a role check rather than by a permission map: Auth::requireRole('admin') guards the administration side [src: app/controllers/app.php:2168], and the check passes anyone holding the named role — with admin always allowed [src: app/src/Auth.php:42].

Buyers are a separate identity from staff. Auth::requireBuyer() guards the purchase portal and redirects an unauthenticated visitor to the portal sign-in rather than to the staff area [src: app/src/Auth.php:62].

The large public surface is the storefront itself — product pages, comparison pages, the sitemap and the checkout entry points — which is why it is public.

What Deliora does not do

It does not process the payment. A provider does; Deliora acts on the provider's signed webhook [src: app/src/Deliora.php:3].

It does not serve a download after a refund [src: app/src/Deliora.php:357], or past an expiry or a use limit [src: app/src/Deliora.php:345].

It does not tell an anonymous visitor whether an email address has bought anything [src: app/src/Deliora.php:495].

It does not render a competitor price without a citation [src: app/src/Storefront.php:8].

It does not stop serving existing customers when you switch selling off [src: app/controllers/app.php:13].

← Back to Deliora · Quickstart · API · Test run

Affiliate program
Recommend tools people own — earn 35% on every sale. 90-day tracking, instant delivery, payouts by Lemon Squeezy.
Become an affiliate →