💳 pbShop
pbShop is payments and orders on top of PageBlocks: plans, one-off purchases and subscriptions.
Not a whole shop
There is no cart, no shipping, no stock. pbShop covers the money instead — order, payment, confirmation, renewal. What is being sold is the site's business, which is why one layer serves an access plan, an event ticket and a service alike.
Requires PageBlocks: the component builds on its models, routing, templates and migrations.
⚙️ What it does
- 💰 Orders with history and line items
- 🔁 Subscriptions with auto-renewal
- 🧾 Bank-transfer invoices with the buyer's own details
- 🌍 Several currencies with fixed prices
- 🔌 Five payment drivers out of the box, and room for your own
- 📣 Events so the site can grant access itself
- 🗂️ A catalogue of plans on the front end
💳 Payment methods
| Driver | What it is | Recurring | API refund |
|---|---|---|---|
manual | Offline: a manager confirms by hand | No | No |
bill | Bank-transfer invoice, PDF or HTML | No | No |
payanyway | MONETA.Assistant, redirect with a callback | No | No |
yoomoney | YooMoney button, a wallet transfer. Roubles only | No | No |
maib | maib acquiring, direct API | Yes | Yes |
Keys and secrets live on the payment method itself, not in system settings: pbShop → Reference books → Payment methods, the JSON settings box. That way several methods with different credentials coexist on one site.
A driver of your own is registered in core/App/config/pbshop.php.
Only maib can charge again
The other drivers take money once. A subscription on them is possible, but it only renews through another manual payment.
🔁 Subscriptions
There is no separate "subscription" entity: it is an order whose product has a period, and which carries an expiry date and an auto-renew flag. On the first payment with auto-renew the gateway saves the card and returns a token, kept on the order; renewals go through it.
Scheduling renewals is the site's job
The component gives you the methods: OrderService::dueForRenewal() finds orders about to expire, renew() charges them, expire() revokes the ones that ran out. What it does not decide is when to call them — the cron is yours to write.
The pbshop_renew_days_before setting says how many days ahead an order is picked up.
📣 Events
The main way to tie a payment to whatever it unlocks. Listeners are registered in the site-owned core/App/config/pbshop.php:
| Event | When |
|---|---|
pbShopOrderCreate | An order is created |
pbShopOrderPaid | An order is paid |
pbShopOrderExtend | A term is extended |
pbShopOrderExpire | A term has run out |
pbShopOrderCancel | An order is cancelled |
pbShopOrderStatus | The status changed |
pbShopPaymentCreate, pbShopPaymentSuccess, pbShopPaymentFail, pbShopPaymentRefund | Payment lifecycle |
Access is granted by the site: the component says what was paid for, and only the site knows what that means.
🗺️ Routes
POST /pay/checkout— place an orderGET|POST /pay/{method}/result— the gateway's callbackGET /pay/form/{uuid}— a form page for gateways that only accept POSTGET /pay/success/{uuid},/pay/fail/{uuid},/pay/pending/{uuid}— where the customer landsPOST /pay/orders/{id}/cancel— cancel a subscription/pay/requisites— the buyer's invoicing details
The catalogue appears when a route prefix is set: category list, category, product.
The callback skips the CSRF check, and it has to
A payment gateway does not know your token, so protection is deliberately off for /pay/{method}/result. Authenticity is the driver's job — it verifies its own gateway's signature — and on top of that the component compares the amount in the callback with the amount billed. Write your own driver and the signature check is entirely on you.
🧮 Money
Amounts are stored as integers in minor units: cents, kopecks, bani. No floating types — 149.99 sits in the database as 14999.
Prices are set per currency and never converted: what you put in euros is what is charged.
🖥️ In the manager
A pbShop section with four entries: Orders, Payments, Products and Reference books (categories, currencies, statuses, payment methods). A payment has confirm and refund buttons — for maib the refund really goes to the gateway.