Theme plugins control the visual appearance of every customer-facing screen in OwnPay — the checkout page, the payment result screen, and the payment link amount-entry page. A theme overrides template paths and injects CSS/JS assets through event hooks without modifying any core file. OwnPay ships one built-in theme (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/own-pay/OwnPay-Documentation/llms.txt
Use this file to discover all available pages before exploring further.
modules/themes/own-pay/) which serves as the definitive reference.
How Theme Plugins Work
Themes are ordinary plugins oftype: "theme", loaded from modules/themes/{slug}/ through the same two-phase lifecycle as addons (register() then boot()). The checkout controller resolves which template to render by applying three filter hooks:
| Hook | Page Controlled |
|---|---|
checkout.template | Main payment gateway selection and checkout page. |
checkout.status.template | Success / failure / pending result page. |
checkout.payment_link.template | Payment link amount-entry page. |
string and expects a string in return. Your theme overrides these filters to return its own Twig template paths.
The active theme is stored in op_system_settings as active_theme under the general group. Brand-specific visual overrides (color, logo, CSS, JS) are resolved by BrandThemeService::getBrandTheme() using a three-tier cascade: brand-specific settings → merchant JSON column → system-wide defaults.
Directory Structure
The manifest.json File
The
assets object in manifest.json is informational only. Actual asset injection is done via checkout.head and checkout.footer hook callbacks inside register(). OwnPay does not copy assets to public/ automatically.manifest.json Field Reference
manifest.json Field Reference
| Field | Required | Description |
|---|---|---|
name | ✅ | Display name in the admin panel. |
slug | ✅ | Unique lowercase identifier matching directory name. The active_theme setting must match this value exactly. |
version | ✅ | Semantic version. |
type | ✅ | Must be "theme". |
entrypoint | ✅ | PHP filename for the entrypoint class (e.g., Theme.php). |
namespace | ✅ | PSR-4 root namespace. |
requires | ✅ | Semver constraint on OwnPay core. Either "ownpay" or "core" key is accepted. |
settings | ❌ | Default key-value pairs for theme configuration, rendered in the admin theme settings form. |
assets | ❌ | Declarative asset list for documentation purposes. Injection is handled in register(). |
The Entrypoint Class
Twig Templates
OwnPay uses Twig 3.x. All output is auto-escaped by default (HTML context). Use{{ variable }} for escaped output and {{ variable|raw }} only for trusted HTML such as core hook output.
Checkout Template Skeleton
Twig Context Variables
Each checkout template receives these variables from the core controller:- checkout.template
- checkout.status.template
- checkout.payment_link.template
| Variable | Type | Description |
|---|---|---|
transaction | array | The op_transactions row (id, amount, currency, status, etc.). |
brand | array | Result of BrandThemeService::getBrandTheme($merchantId). |
gateways | array | Active gateway configurations for this brand. |
intent | array|null | Payment intent data if applicable. |
The brand Variable
The brand variable follows this structure, resolved by BrandThemeService::getBrandTheme():
op_system_settings rows → merchant settings JSON → global defaults. Per-brand theme selection is achieved by activating different themes per brand via Admin → Appearance → Themes.
Asset Management
OwnPay does not automatically copy theme assets topublic/. Choose one of two strategies:
- Option A — Static Assets
- Option B — Plugin Route
Copy compiled CSS/JS into This is the recommended approach for production themes.
public/assets/ and reference them with absolute paths:CSP Nonce Integration
OwnPay enforces a Content Security Policy. Every<script> tag must carry the nonce from the DI container:
checkout.csp.sources:
Theme Settings and Per-Brand Configuration
Thefields() method defines the configuration form rendered on the Theme Settings admin page. Available field types:
| Type | Rendered As |
|---|---|
text | Single-line text input. |
password | Masked input. |
color | Hex color picker. |
toggle | On/off switch. Stores "1" (on) or "0" (off). |
select | Dropdown with options array (value → label). |
textarea | Multi-line text area. |
SettingsRepository in boot():
Full Hook Reference for Themes
| Hook | Type | Purpose |
|---|---|---|
checkout.template | Filter | Override main checkout Twig template path. |
checkout.status.template | Filter | Override result page template path. |
checkout.payment_link.template | Filter | Override payment link page template. |
checkout.render | Filter | Inject variables into checkout Twig context. |
checkout.intent.render | Filter | Inject variables into payment intent context. |
checkout.head | Action | Inject CSS/fonts into <head>. |
checkout.footer | Action | Inject JS into <footer>. |
checkout.before | Action | React before checkout template is parsed. |
checkout.csp.sources | Filter | Add external domains to the Content Security Policy. |
admin.template.resolve | Filter | Override admin templates (advanced use only). |
Security Requirements
| Rule | Requirement |
|---|---|
| Escape template output | Always use {{ variable }} (auto-escaped) in Twig. Never {{ variable|raw }} for user-supplied data. |
| CSP nonce on inline scripts | Every <script> tag must carry the csp_nonce from the container. |
| Validate color fields | Before using primary_color programmatically, validate with preg_match('/^#[0-9a-fA-F]{6}$/', $color). |
No eval() | PluginLoader token-scans every PHP file. eval() triggers load failure. |
| No OS commands | exec(), shell_exec(), system(), passthru(), popen(), proc_open(), pcntl_exec() are blocked. |
| No hardcoded CSP domains | Use the checkout.csp.sources filter — never inject CSP headers directly. |
Installation and Activation
Upload
Upload via Admin → Appearance → Themes → Install Theme.
PluginInstaller::installFromZip() deploys to modules/themes/{slug}/.Activate
Activate via the Themes list. Activation runs pending migrations and calls
register() on subsequent requests.Checklist
Pre-submission Checklist
Pre-submission Checklist
-
manifest.jsoncomplete;slugmatches directory name. -
typeis"theme". -
namespacedeclared; entrypoint class in correct PSR-4 path. - Entrypoint implements
PluginInterfacewith all six methods. -
register()overrides all three template filters:checkout.template,checkout.status.template,checkout.payment_link.template. - CSS injected via
checkout.headaction hook. - JS injected via
checkout.footeraction hook with CSP nonce applied. - All Twig output of user-supplied or DB-sourced data uses
{{ var }}(escaped), not{{ var|raw }}. - External font/script domains declared via
checkout.csp.sourcesfilter. - Brand colors validated with hex regex before programmatic use.
- No
eval(),exec(),shell_exec(),system(),passthru(). -
icon.svgprovided for display in the admin panel.