Skip to content

💳 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

DriverWhat it isRecurringAPI refund
manualOffline: a manager confirms by handNoNo
billBank-transfer invoice, PDF or HTMLNoNo
payanywayMONETA.Assistant, redirect with a callbackNoNo
yoomoneyYooMoney button, a wallet transfer. Roubles onlyNoNo
maibmaib acquiring, direct APIYesYes

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:

EventWhen
pbShopOrderCreateAn order is created
pbShopOrderPaidAn order is paid
pbShopOrderExtendA term is extended
pbShopOrderExpireA term has run out
pbShopOrderCancelAn order is cancelled
pbShopOrderStatusThe status changed
pbShopPaymentCreate, pbShopPaymentSuccess, pbShopPaymentFail, pbShopPaymentRefundPayment 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 order
  • GET|POST /pay/{method}/result — the gateway's callback
  • GET /pay/form/{uuid} — a form page for gateways that only accept POST
  • GET /pay/success/{uuid}, /pay/fail/{uuid}, /pay/pending/{uuid} — where the customer lands
  • POST /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.

© PageBlocks 2019-present