WebBlocks Commerce Operator Guide
This guide explains how to install, configure, and test WebBlocks Commerce. The plugin supports a session-backed public cart, customer and delivery-address collection, a no-payment test-order mode, multi-line hosted checkout through PayPal or SumUp, product and read-only order admin, write-only encrypted provider settings, secret-safe diagnostics, public product pages, and a plugin-owned Commerce Buy Button block. Payment-card data stays on the selected provider's hosted payment surface.
Store owners who want to connect SumUp should start with the task-focused SumUp Quick Start. This operator guide is the technical reference for architecture, APIs, verification, and advanced troubleshooting.
The plugin is developed in its own repository, webblocks-commerce-plugin, alongside the other catalog plugins. It remains a manually installed plugin package and must not be moved into CMS core.
Requirements
Documented package version: 0.14.0. WebBlocks CMS ^1.61.0; PHP >=8.3.
PHP ext-intl is required on both web and CLI runtimes for currency formatting.
Current User Flow
- A CMS operator installs and enables WebBlocks Commerce.
- The operator runs plugin migrations from the plugin detail screen.
- The operator selects PayPal, SumUp, or
Test order (no payment)and a compatible default currency inCommerce Settings. Real providers require credentials; hosting-managed environment values may be used as overrides instead. - The operator opens
Commerce Settingsto confirm checkout and webhook readiness. - The operator creates a commerce product.
- The product detail screen shows a public buy URL.
- The operator adds a
Commerce Buy Buttonblock to a page and selects the product. - The block adds the product to
/plugins/webblocks-commerce/cart; the visitor updates quantities and enters required contact and delivery details. - In test-order mode, Commerce records a pending unpaid order and returns directly to the order status page without contacting a provider.
- With PayPal or SumUp, the visitor approves payment on the selected provider's hosted page and returns to the site.
- Real-provider orders remain pending until a provider-verified webhook confirms payment.
- The operator reviews customer, delivery, item, tax, and payment details under
Commerce Orders.
Install The Plugin
Build the plugin ZIP from the plugin repository:
composer plugin:build
The artifact is written to build/webblocks-commerce-{version}.zip with its SHA-256 alongside.
Then complete the manual plugin lifecycle:
- Open
System -> Plugins. - Upload the generated WebBlocks Commerce ZIP.
- Review the plugin detail screen.
- Enable the plugin.
- Run plugin setup/migrations if the plugin reports
Setup required. - Confirm that health changes from setup-required to ready.
The plugin owns webblocks_commerce_* tables. Disabling the plugin makes routes, menus, settings, and behavior inert. Uninstalling a disabled manually uploaded plugin removes the uploaded package, but preserves plugin-owned tables.
API Automation
Trusted operator tools can perform the setup and page-building workflow through /webadmin/api when the CMS API token has explicit plugin, commerce, and content capabilities.
Plugin lifecycle:
GET /webadmin/api/plugins
POST /webadmin/api/plugins/install
POST /webadmin/api/plugins/webblocks-commerce/enable
POST /webadmin/api/plugins/webblocks-commerce/setup
POST /webadmin/api/plugins/webblocks-commerce/disable
DELETE /webadmin/api/plugins/webblocks-commerce
Commerce resources:
GET /webadmin/api/commerce/products
POST /webadmin/api/commerce/products
PATCH /webadmin/api/commerce/products/{product}
GET /webadmin/api/commerce/orders
GET /webadmin/api/commerce/orders/{order}
Required token capabilities are intentionally split:
- plugin lifecycle:
plugins.read,plugins.install,plugins.manage,plugins.setup, and only when neededplugins.uninstall - product work:
commerce.readandcommerce.products.write - order review:
commerce.orders.read - page placement:
content.validateandcontent.apply
The API flow for adding a buy button is:
- Install, enable, and setup
webblocks-commerce. - Create an active product with
POST /webadmin/api/commerce/products. - Read
GET /webadmin/api/block-typesorGET /webadmin/api/content-contract. - Add a
webblocks-commerce-buy-buttonblock through content validate/apply. - Set
settings.commerce_product_idto the product id returned by the Commerce API.
The Commerce Buy Button block is plugin-owned. It is hidden from block discovery while the plugin is disabled, and content validate/apply rejects missing, unknown, or inactive product ids. Its public renderer posts to the plugin-owned cart; no Trusted HTML block is required. The API does not collect card data; visitors complete payment on the configured PayPal or SumUp hosted checkout.
PayPal Configuration
WebBlocks Commerce uses PayPal REST APIs. PayPal documents that REST APIs use OAuth 2.0 access tokens, and that API calls exchange a client ID and client secret for an access token. Keep the client secret private and never paste it into CMS content, docs pages, screenshots, or support logs.
Official PayPal references:
Open Commerce Settings, select PayPal, select Sandbox, and enter the client ID, client secret,
and webhook ID. The fields are write-only: saved values are encrypted in the plugin settings table
and are never rendered back into the browser. Leaving a field blank preserves its current value;
use the explicit clear checkbox to remove it.
For hosting-managed configuration, the following environment variables remain supported and take precedence over encrypted admin settings:
WEBBLOCKS_COMMERCE_GATEWAY=paypal
WEBBLOCKS_COMMERCE_PAYPAL_MODE=sandbox
WEBBLOCKS_COMMERCE_PAYPAL_CLIENT_ID=your-paypal-client-id
WEBBLOCKS_COMMERCE_PAYPAL_CLIENT_SECRET=your-paypal-client-secret
WEBBLOCKS_COMMERCE_PAYPAL_WEBHOOK_ID=your-paypal-webhook-id
Use WEBBLOCKS_COMMERCE_PAYPAL_MODE=live only after sandbox checkout and webhook verification have been tested.
PayPal Sandbox Setup
In PayPal Developer Dashboard:
- Open
Apps & Credentials. - Use the default REST API app or create a new app.
- Copy the sandbox client ID and client secret into the secure Commerce Settings form (or the install environment when using hosting-managed overrides).
- Create or open the app webhook settings.
- Add this webhook URL:
https://your-site.example/plugins/webblocks-commerce/webhooks/paypal
- Subscribe at minimum to:
CHECKOUT.ORDER.APPROVED
PAYMENT.CAPTURE.COMPLETED
- Copy the PayPal webhook ID into the Commerce Settings form (or
WEBBLOCKS_COMMERCE_PAYPAL_WEBHOOK_IDwhen using an environment override). - Use PayPal sandbox buyer and seller accounts for checkout testing.
For local HTTPS tunnels, use the tunnel HTTPS URL as the webhook URL. For production, use the final public HTTPS site URL.
SumUp Hosted Checkout Configuration
SumUp Hosted Checkout keeps card entry and supported wallet UI on a SumUp-hosted page. The integration creates the checkout server-side and never exposes the API key to the browser.
For a screen-by-screen store-owner workflow, use the SumUp Quick Start. The short setup sequence is:
- Create and select a sandbox merchant under SumUp Dashboard Developer Settings → Sandboxes.
- Copy the sandbox Merchant ID shown in the top-left Dashboard account area.
- Create a secret test API key under Settings → For Developers → Toolkit → API Keys.
- Enter the gateway, mode, API key, and merchant code in Commerce Settings and confirm readiness.
- Test with SumUp's documented sandbox card before using live credentials.
Official SumUp references:
In Commerce Settings, select SumUp, select Sandbox, and enter the API key and merchant code.
Saved credentials are encrypted at rest and remain write-only. Hosting-managed deployments may
instead set these environment variables; they take precedence and make the matching form fields
read-only:
WEBBLOCKS_COMMERCE_GATEWAY=sumup
WEBBLOCKS_COMMERCE_DEFAULT_CURRENCY=EUR
WEBBLOCKS_COMMERCE_SUMUP_MODE=sandbox
WEBBLOCKS_COMMERCE_SUMUP_API_KEY=your-sumup-test-api-key
WEBBLOCKS_COMMERCE_SUMUP_MERCHANT_CODE=your-sandbox-merchant-code
Use the secret API key created for the selected sandbox merchant; do not use the SumUp Public Key.
A test secret key normally starts with sk_test_. Do not paste it into CMS blocks, site settings,
screenshots, support logs, or normal chat. WebBlocks Commerce sends this callback automatically
when it creates each checkout:
https://your-site.example/plugins/webblocks-commerce/webhooks/sumup
No manual webhook registration in the SumUp Dashboard is required for this adapter. The public HTTPS endpoint must nevertheless be reachable by SumUp and must not be blocked by a firewall, maintenance page, HTTP password, or proxy rule.
SumUp calls the configured return_url with CHECKOUT_STATUS_CHANGED and a checkout ID. That
payload is not accepted as proof of payment. WebBlocks Commerce retrieves the checkout from
SumUp, then matches the ID, merchant code, order reference, amount, currency, terminal status,
and successful transaction before marking an order paid. Failed and expired status transitions
release reserved inventory. Unknown event types are safely ignored.
Readiness Diagnostics
Open:
/webadmin/plugins/webblocks-commerce/settings
The settings screen provides write-only credential fields and intentionally shows only safe diagnostics:
- active gateway
- default currency and its configuration source
- PayPal mode
- SumUp mode
- client ID configured or missing
- client secret configured or missing
- webhook ID configured or missing
- checkout readiness
- webhook readiness
- expected webhook URL
- SumUp API key and merchant code configured or missing
- plugin schema readiness
It must not display raw PayPal secrets, SumUp API keys, access tokens, webhook payload signatures, or payment credentials. Blank credential fields preserve existing encrypted values. Explicit clear controls remove stored values, while environment-managed values cannot be edited or cleared from the CMS.
Create A Product
Open:
/webadmin/plugins/webblocks-commerce/products
Create a product with:
- title
- slug
- description
- status
- price amount
- currency
- optional inventory quantity
- optional SKU
- optional site scope
Set the product status to Active when it should be available for checkout. Draft and archived products do not start public checkout.
Currency Behavior
The default currency is stored with the other Commerce settings and is used for new products.
WEBBLOCKS_COMMERCE_DEFAULT_CURRENCY remains an optional environment override; when present, the
selector is read-only. Product currency is selected from the active gateway's supported list, and
the internal product API enforces the same rule.
Switching gateways is blocked if a non-archived product uses a currency unsupported by the target gateway. Mixed-currency carts remain rejected. Checkout performs a final gateway compatibility check before it creates an order or reserves inventory.
Prices are integer minor units, but minor-unit precision is currency-specific rather than always
two digits. Public and admin views use the current CMS locale through PHP intl
NumberFormatter, so symbols and separators are localized for EUR, USD, GBP, JPY, and every
selectable currency. Gateway requests use the same precision. PayPal-specific zero-decimal
requirements for HUF and TWD are honored.
There is no additional Composer dependency. PHP ext-intl is a platform requirement and must be
enabled for both the web server and CLI. The plugin health result warns when it is unavailable.
Supported codes are based on the official
PayPal currency reference and
SumUp Checkout API enum; merchant-country and
account restrictions can still narrow those provider lists.
The product detail screen shows the product's public buy URL:
/plugins/webblocks-commerce/products/{slug}/buy
Add A Buy Button To A Page
After the plugin is enabled and setup-ready, the page-builder block picker shows a plugin-owned Commerce Buy Button block.
Recommended workflow:
- Open the artwork, portfolio, or "Works" page in the page builder.
- Add
Commerce Buy Buttonto the desired slot. - Select an active commerce product.
- Optionally change the button label, alignment, and price display.
- Publish the page when the surrounding content is ready.
The block renders a native public form that adds the selected product to:
/plugins/webblocks-commerce/cart
The product buy URL remains useful for product-detail links and exposes an add-to-cart action. Checkout is completed from the cart so required customer and delivery fields cannot be skipped. The cart, product page, and checkout status views all extend the CMS public layout, preserving the active site's header and footer slots.
Do not paste provider-hosted checkout URLs into CMS content. They are generated per order and should come only from the checkout start flow.
Checkout Behavior
When a visitor uses a Commerce block or product page:
- The plugin checks setup, product status, tracked stock, and cart currency.
- The product is stored in the session-backed server-side cart; no payment data is collected.
- The visitor supplies name, email, delivery address, and optional phone/address addition.
- At checkout, WebBlocks Commerce freezes customer/delivery metadata, localized line titles, prices, VAT, and totals onto a pending order and reserves stock atomically.
- In test-order mode, the visitor returns directly to a signed status page and no payment provider is contacted. With PayPal or SumUp, the active adapter creates a hosted checkout and redirects the visitor away.
- A signed return page can report that processing continues, but it never marks the order paid.
- PayPal webhooks are signature-verified and approved PayPal Orders are captured.
- SumUp status notifications trigger a fresh checkout API retrieval and full order/transaction match.
- Only the verified provider result moves the order and payment attempt to
paid/succeeded.
Webhook events are stored by gateway and event ID so repeated delivery is idempotent.
Review Orders
Open:
/webadmin/plugins/webblocks-commerce/orders
Orders are read-only. The order detail screen shows:
- order number
- customer name, required email, optional phone, and delivery address captured by the public cart
- order status
- line items
- payment attempts
- gateway checkout and payment references
- timestamps
Manual status editing, refunds, shipping-rate calculation, and fulfillment workflows are intentionally deferred. Customer and delivery-address capture, VAT snapshots, and inventory reservation are implemented.
No-Payment Test Orders
Select Test order (no payment) in Commerce Settings when a store owner wants to verify the
complete storefront form and order-recording flow without contacting PayPal or SumUp. The public
cart clearly labels the mode, requires customer and delivery details, creates a pending order,
reserves tracked stock, records a pending fake payment attempt for audit continuity, and redirects
to a signed confirmation page stating that no payment was collected. Switch back to a configured
real provider before accepting paid customer orders.
PayPal Sandbox Verification Checklist
Use this checklist before switching to live mode:
- WebBlocks Commerce is installed, enabled, and setup-ready.
Commerce Settingsshows schema ready.Commerce Settingsshows gatewaypaypal.- PayPal client ID is configured.
- PayPal client secret is configured.
- PayPal webhook ID is configured.
- Webhook URL uses HTTPS and points to
/plugins/webblocks-commerce/webhooks/paypal. - A product is active and has the expected price/currency.
- The product buy URL opens publicly.
- A page with a
Commerce Buy Buttonadds the expected product to/plugins/webblocks-commerce/cart. - Starting checkout redirects to PayPal.
- A sandbox buyer can approve the payment.
- The visitor returns to the signed success page.
- The order stays pending before webhook confirmation.
- PayPal delivers
CHECKOUT.ORDER.APPROVED. - The webhook verifies successfully.
- The PayPal order capture completes.
- The CMS order becomes
paid. - The payment attempt becomes
succeeded. - Re-sending the same webhook does not duplicate payment attempts.
- Invalid webhook signatures are rejected and do not mark orders paid.
- No PayPal secret appears in admin screens, public pages, logs, screenshots, or docs.
SumUp Sandbox Verification Checklist
- The sandbox merchant is selected in SumUp Dashboard.
- The Merchant ID and
sk_test_secret key belong to that same sandbox account. Commerce Settingsshows gatewaysumup, sandbox mode, and checkout ready.- The test API key and sandbox merchant code are configured, but the key value is not rendered.
- A Commerce block adds the active EUR product to
/plugins/webblocks-commerce/cart. - Quantity, VAT, and the final amount are correct before checkout.
- Starting checkout creates one pending order and redirects to
checkout.sumup.com. - The SumUp checkout reference matches the CMS order number.
- Completing a sandbox payment produces
CHECKOUT_STATUS_CHANGEDat/plugins/webblocks-commerce/webhooks/sumup. - The handler retrieves the checkout from SumUp and confirms a successful transaction.
- The CMS order becomes
paidand its payment attempt becomessucceeded. - Re-sending the same paid notification does not create another payment attempt.
- A mismatched merchant code, reference, amount, or currency never marks an order paid.
- Failed or expired SumUp checkouts release reserved inventory.
- The documented successful test card
4200 0000 0000 0091completes with any future expiry date and any three-digit CVV.
Live Mode Checklist
Before switching to WEBBLOCKS_COMMERCE_PAYPAL_MODE=live:
- Confirm the operator owns a PayPal Business account where required by PayPal.
- Create or select the live REST app in PayPal Developer Dashboard.
- Replace sandbox client ID, client secret, and webhook ID with live values.
- Configure the live webhook URL with the production HTTPS domain.
- Confirm the production site can receive public PayPal webhook requests.
- Run one low-value live checkout if acceptable for the operator.
- Review the order in CMS admin.
Keep sandbox and live credentials separate. Do not reuse sandbox webhook IDs in live mode.
For SumUp live mode, select the verified real merchant account, create a separate sk_live_
secret API key, use that account's live Merchant ID, set WEBBLOCKS_COMMERCE_SUMUP_MODE=live,
refresh the application configuration, and run one acceptable low-value payment. Never reuse or
mix a sandbox merchant, test key, live merchant, or live key.
Troubleshooting
If the buy page says checkout is not ready:
- Open
Commerce Settings. - Confirm the selected gateway is
paypalorsumup. - For PayPal, confirm client ID and client secret are configured.
- For SumUp, confirm API key and merchant code are configured.
- Confirm the product is active and has a valid price.
- Confirm plugin migrations have run.
If checkout redirects to PayPal but the order stays pending:
- Confirm the PayPal webhook URL is correct.
- Confirm
WEBBLOCKS_COMMERCE_PAYPAL_WEBHOOK_IDmatches the webhook configured in PayPal. - Confirm PayPal sends
CHECKOUT.ORDER.APPROVED. - Confirm the site is reachable from PayPal over HTTPS.
- Confirm webhook signature verification is not failing.
If a webhook is rejected:
- Check that the webhook event came from the matching PayPal mode.
- Check that sandbox credentials are not mixed with live webhook IDs.
- Check that the webhook ID belongs to the same PayPal REST app as the client credentials.
If a SumUp order stays pending:
- Confirm the public HTTPS URL
/plugins/webblocks-commerce/webhooks/sumupis reachable. - Confirm the API key can read the checkout and belongs to the configured merchant code.
- Confirm the checkout reference, amount, and currency still match the CMS order.
- Confirm SumUp reports
PAIDand includes aSUCCESSFULtransaction.
If the SumUp hosted page reports an expired or missing checkout, start a new checkout from the cart. Hosted Checkout sessions expire after about 30 minutes and their URLs must not be bookmarked or reused.
Current Limitations
The current plugin does not yet include:
- shipping
- coupons
- subscriptions
- refunds from CMS
- customer accounts
- fulfillment workflows
- provider account onboarding (payment credentials can be edited in Commerce Settings)
These remain separate features rather than being hidden inside provider integrations.
Cart, stock and stale orders
Order status is only ever changed through Support\Orders\OrderStateMachine, never a
raw update. It enforces the allowed transition graph (pending → paid|failed|cancelled|expired,
paid → refunded), is idempotent for re-delivered webhooks, and locks the order row so
racing gateway callbacks cannot double-apply a transition.
Tracked stock (inventory_quantity not null) is reserved atomically when checkout starts,
which prevents overselling under concurrent buyers, and released back to the catalog when an
order is cancelled, expires, fails, or is refunded. Products with a null inventory_quantity
are untracked (unlimited) and never decremented.
Abandoned pending orders hold their reservation until they are expired. Run
php artisan webblocks-commerce:expire-stale-orders --minutes=30 on a schedule to release the
stock held by checkouts the buyer never completed. Wire it into the host app's console kernel,
for example $schedule->command('webblocks-commerce:expire-stale-orders')->everyFifteenMinutes();.
Cart API
Carts are server-side, persistent, and single-currency. A cart stores only product
references + quantities; prices and VAT are resolved live from the current catalog and only
frozen onto the order at checkout (StartCheckout::forCart), which builds one multi-line order,
reserves stock atomically for every line, and marks the cart converted. Adding the same
product merges quantities; adding a different currency, or more than tracked stock, is rejected.
Visitors use the session-backed public cart without an API token. Before checkout, the public form requires the customer's name, email, street, postal code, city, and two-letter country code; phone and a second address line remain optional. The details are stored in cart/order metadata and shown on the order status and admin order detail screens.
Public routes:
GET /plugins/webblocks-commerce/cart— review cart lines, VAT, and totalPOST /plugins/webblocks-commerce/cart/items/{product}— add a product from a Commerce block or buy pagePATCH|DELETE /plugins/webblocks-commerce/cart/items/{product}— change quantity or remove a linePOST /plugins/webblocks-commerce/cart/checkout— save customer/delivery details, create the order, and continue to the configured gateway
The public cart, buy page, and checkout status pages extend the CMS public layout and render the
site's own header and footer slots around their content, resolved from the home page by
Support\PublicStorefrontShell. A header held in a Shared Slot works the same way, so changing the
site header changes the storefront's with it. The Commerce Buy Button is a native plugin block and
posts to the cart; it does not require a Trusted HTML block.
Everything the cart does is available over the plugin-owned internal API — mounted into the
CMS internal API group (/webadmin/api, bearer-token auth) via the plugin's apiRoutes() hook,
so AI agents get the same capabilities the admin panel gives humans. Endpoints (capability in
parentheses):
POST /webadmin/api/commerce/cart— create a cart (commerce.cart.write)GET /webadmin/api/commerce/cart/{token}— read a cart with live totals (commerce.cart.read)POST /webadmin/api/commerce/cart/{token}/items— add{product_id, quantity}(commerce.cart.write)PATCH /webadmin/api/commerce/cart/{token}/items/{product}— set{quantity}(0 removes) (commerce.cart.write)DELETE /webadmin/api/commerce/cart/{token}/items/{product}— remove a line (commerce.cart.write)DELETE /webadmin/api/commerce/cart/{token}/items— clear the cart (commerce.cart.write)POST /webadmin/api/commerce/cart/{token}/checkout— start hosted checkout, returnsredirect_url(commerce.cart.write)
Products and orders are exposed the same way (these endpoints are owned by the plugin, not the CMS core, and are only present when the plugin is enabled):
GET|POST /webadmin/api/commerce/products,PATCH /webadmin/api/commerce/products/{id}— catalog incl.tax_class(commerce.read/commerce.products.write)GET /webadmin/api/commerce/orders,GET /webadmin/api/commerce/orders/{id}— read-only, with the full net/tax/gross breakdown (commerce.orders.read)
All of these self-advertise: while the plugin is enabled they appear in the CMS API discovery
(GET /webadmin/api _links, GET /webadmin/api/openapi.json paths, and the discovery guidance)
via the plugin's apiDiscovery() contribution, and disappear when the plugin is disabled.
There is no separate commerce token: the plugin uses the shared CMS API token. Its
commerce.* capabilities are contributed to the CMS's grantable set via apiCapabilities()
(they appear as a "Commerce" group in the token admin UI while the plugin is enabled), so a
single least-privilege token can be scoped to just the commerce capabilities.
Multilingual product content
Storefront product content shares the CMS Site+Locale system rather than a parallel one. The
base product row holds the default/fallback title/description; a per-locale translation row
(webblocks_commerce_product_translations, keyed by product + CMS locale) overrides them. This
is the admin-panel content language axis — distinct from the admin-panel UI language, which
stays in Laravel resources/lang files.
ProductLocalizer resolves the shown title/description for a locale, falling back to the base.
Carts carry a locale, so cart summaries and — critically — the order line title snapshot at
checkout use the localized text the buyer actually saw. The public buy page localizes via a
?locale=<code> query, falling back to the base.
Edit translations in the admin product form (per enabled non-default locale) or over the API (capability in parentheses):
GET /webadmin/api/commerce/products/{product}/translations— list base + translations (commerce.read)PUT /webadmin/api/commerce/products/{product}/translations/{locale}— upsert{title?, description?}(commerce.products.write)DELETE /webadmin/api/commerce/products/{product}/translations/{locale}— remove a locale (commerce.products.write)