Skip to content

How to guides

adumont-payplug edited this page Sep 16, 2026 · 1 revision

How-to guides

Home tells you what each class is. These guides tell you what to do — the end-to-end sequence for each payment flow, what your CMS has to provide at each step, and what goes wrong when it doesn't.

These guides are deliberately CMS-agnostic. They use plain PHP and name the obligations your plugin must meet ("persist the alias id against the customer", "hand the webhook controller the raw body") rather than WooCommerce hooks or Sylius state machines. Every rule here was verified against unified-plugin-core's source and against a working integration; where something is unverified, the guide says so rather than guessing.

The guides

Guide Read it when
How to wire the library Always, first — contracts, merchant connection, service construction
How to implement Hosted Fields The shopper types card details on your checkout page
How to implement alias payments The shopper pays with a card they saved earlier (one-click)
How to handle webhooks Always — no payment here is finished without one
How to implement refunds Full or partial refunds from your back office

The four things every integration needs

Whatever flow you implement, the same four pieces have to exist. Most integration bugs are one of them missing rather than an API call being wrong.

  1. The contracts. UPC ships zero concrete implementations. Your plugin provides all 8 interfaces in src/Contracts/ — logging, configuration, payment persistence, order-state mutation, locking, token caching, and the two HTTP clients. See Contracts for the list and How to wire the library for how to build them.
  2. A connected merchant. OAuth2 client credentials, stored by your plugin and read back through IConfigurationRepository. Without them no service call authenticates.
  3. A payment flow. Hosted Fields or alias — both end in the same UnifiedApiPaymentService::createPayment() call and the same PaymentOutput.
  4. A webhook endpoint. Payments here are asynchronous. createPayment() returning successfully does not mean the shopper has paid — the final outcome arrives at your webhook URL. An integration without one has orders that never leave "pending".

The division of labour

Your CMS provides UPC gives back
Credentials and settings storage (IConfigurationRepository) A valid JWT, refreshed and cached (TokenManager)
Order id, amount in cents, currency, customer and address data A validated request body and the HTTP call (createPayment())
A checkout page that collects card data (Hosted Fields) PaymentOutput — 3DS redirect HTML, alias id, raw response
A publicly reachable webhook URL A parsed, verified OperationData (WebhookNotificationHelper)
Order state transitions (IOrderStateMutator) A CMS-neutral outcome vocabulary (PaymentOutcome)
Idempotency and locking (IPaymentRepository, ILock) Nothing — UPC holds no state between requests

UPC is stateless by design. It never persists a credential, never writes an order, and never calls header() to redirect. Every one of those is your plugin's job, which is what makes the same library work across Sylius, WooCommerce, PrestaShop and Magento.

Conventions in these guides

  • Amounts are integer minor units (cents) everywhere. Convert once, at the boundary, with AmountHelper::toCents() — never with * 100.
  • Code samples target PHP 7.1, the floor src/ is held to (see Compatibility). Your plugin is probably on a newer PHP; the samples avoid named arguments and typed properties so they stay copy-pasteable either way.
  • Exceptions: every failure UPC raises extends PayplugException and carries the HTTP status as its code. Guides name the specific type at each step rather than telling you to catch broadly.
  • "Verified" means confirmed against source or a real API call. Anything inferred is labelled.

Clone this wiki locally