setono/sylius-quickpay-plugin

Quickpay payment plugin for Sylius

Maintainers

Package info

github.com/Setono/SyliusQuickpayPlugin

Type:sylius-plugin

pkg:composer/setono/sylius-quickpay-plugin

Transparency log

Fund package maintenance!

Setono

Statistics

Installs: 50 614

Dependents: 1

Suggesters: 0

Stars: 3

Open Issues: 6

v1.5.0 2026-01-05 12:14 UTC

README

Latest Version Software License Build Status Code Coverage

Adds Quickpay as a payment gateway to your Sylius store. The plugin integrates the setono/payum-quickpay Payum gateway into Sylius' checkout, state machine, and admin.

Requirements

  • PHP 8.1 or higher
  • Sylius 1.x on Symfony ^6.4 (tested against Sylius 1.14)
  • PSR-17 factories discoverable by php-http/discovery (e.g. nyholm/psr7) — the plugin itself brings symfony/http-client as its PSR-18 client

Installation

1. Install the plugin

The plugin builds on setono/payum-quickpay 2.x, which is still in pre-release, so your project must allow the pre-release versions explicitly:

composer require setono/sylius-quickpay-plugin:^2.0@alpha setono/payum-quickpay:^2.0@alpha setono/quickpay-php-sdk:^1.0@beta

If your project does not already provide PSR-17 factories:

composer require nyholm/psr7

2. Register the bundle

<?php
# config/bundles.php

return [
    // ...
    Setono\SyliusQuickpayPlugin\SetonoSyliusQuickpayPlugin::class => ['all' => true],
];

3. Configure the plugin (optional)

The state machine callback that captures, refunds, and cancels Quickpay payments when the corresponding Sylius payment transitions are applied (see How it works) is registered automatically. Each operation can be turned off individually:

# config/packages/setono_sylius_quickpay.yaml
setono_sylius_quickpay:
    operations:
        capture: true  # forward the payment's complete transition to Quickpay as a capture
        refund: true   # forward the refund transition to Quickpay
        cancel: true   # forward the cancel transition to Quickpay

The plugin hooks into both state machine adapters supported by Sylius 1.14 — a winzou_state_machine callback (the default adapter) and a Symfony workflow event subscriber — so the operations are forwarded to Quickpay no matter which adapter your application runs the sylius_payment graph on.

4. Import the routes

# config/routes/setono_sylius_quickpay.yaml
setono_sylius_quickpay:
    resource: "@SetonoSyliusQuickpayPlugin/Resources/config/routes.yaml"

This registers the callback endpoint (POST /payment/quickpay/notify) that Quickpay's servers use to notify your store about payment state changes.

5. Import fixtures (optional, development only)

# config/packages/setono_sylius_quickpay.yaml
imports:
    - { resource: "@SetonoSyliusQuickpayPlugin/Resources/config/app/fixtures.yaml" }

The fixtures create a Quickpay credit card payment method and matching channels. They read the gateway credentials from these environment variables:

QUICKPAY_API_KEY=
QUICKPAY_PRIVATE_KEY=
QUICKPAY_AGREEMENT_ID=
QUICKPAY_ORDER_PREFIX=qp_

Configuration

Create a new payment method of type Quickpay in the admin panel (ConfigurationPayment methods) and fill out the gateway configuration:

Field Description
Api key The API key of the API user in your Quickpay manager (SettingsUsers)
Private key The private key of your merchant account (SettingsIntegration)
Agreement id (optional) The agreement id used for the payment window
Order prefix Prepended to order numbers sent to Quickpay as the order_id — must be unique per project and environment sharing the same Quickpay account (see Troubleshooting), and 11 characters or less
Payment methods Which payment methods the Quickpay payment window offers, e.g. creditcard or mobilepay — see the Quickpay documentation
Auto capture Capture the payment automatically right after authorization — useful for digital products
Synchronized operations Run capture, refund and cancel synchronously instead of relying on the Quickpay callback
Branding id (optional) The payment window branding to use

When you save the payment method, the plugin verifies the API key against Quickpay's API (a lightweight ping) and rejects the form if Quickpay rejects the key — a typo'd key is caught immediately instead of by the first customer whose checkout fails. If Quickpay cannot be reached, the check is skipped so an outage never blocks saving.

How it works

  • During checkout the customer is redirected to the Quickpay payment window through a payment link. The payment is authorized, not captured (unless Auto capture is enabled).
  • Quickpay notifies your store of every payment change on the callback endpoint. The callback's Quickpay-Checksum-SHA256 header is validated against your private key before the payment details are updated.
  • When you complete, refund, or cancel a payment in the Sylius admin, the plugin performs the matching capture, refund, or cancel operation against Quickpay. A failed cancel at Quickpay (e.g. the customer never completed checkout, so there is nothing to cancel) is logged but does not block cancelling the order.
  • A refund targets Quickpay's balance (what is still captured) rather than the original amount — so a payment that was partially refunded directly in the Quickpay manager refunds only the remainder instead of failing. The balance is also persisted into the payment details on every status check and callback. An explicit refund_amount / capture_amount in the payment details is passed through to Quickpay untouched for programmatic partial operations; note that Sylius' payment state machine still treats the payment as a whole — the refund transition can only be applied once.

Operation history in the admin

Each Quickpay payment on the admin order view shows its live operation history — every authorize/capture/refund/cancel with amount, Quickpay status code and message, and timestamp, plus the captured balance and a test-mode badge. The data is fetched from Quickpay after the page has rendered, so the order page is never delayed by a slow gateway; if Quickpay cannot be reached, the panel shows an inline notice with a retry link. Nothing is stored — the panel reflects what Quickpay reports right now.

Reconciling missed callbacks

The Quickpay callback is normally the only way your store learns about a payment state change. If a callback never arrives — misconfigured callback URL on the Quickpay agreement, store unreachable while Quickpay retried — the payment stays stuck in a non-final state and the order never completes. The plugin ships a reconciliation command that closes this gap by polling Quickpay directly:

bin/console setono:sylius-quickpay:reconcile-payments                             # last 7 days, max 100 payments
bin/console setono:sylius-quickpay:reconcile-payments --since="12 hours" --limit=50
bin/console setono:sylius-quickpay:reconcile-payments --dry-run                   # report only, change nothing

For each stuck payment it fetches the current status from Quickpay and applies the matching payment transition — the same one the callback would have triggered. A callback racing the command is harmless: transitions are guarded and the resulting Quickpay operations re-check the remote status first.

Scheduling is your application's choice; a cron entry along these lines is plenty:

*/30 * * * * /usr/bin/php /path/to/shop/bin/console setono:sylius-quickpay:reconcile-payments >> /var/log/quickpay-reconcile.log 2>&1

The command exits non-zero when any payment could not be checked, so cron mail or your monitoring will surface persistent problems.

Testing

composer phpunit       # unit tests
composer analyse       # static analysis (PHPStan)
composer check-style   # coding standards

For manual testing, use the credit card numbers from the Quickpay test data.

Upgrading from 1.x

See UPGRADE-2.0.md for the full list of changes an upgrading store has to make — gateway configuration keys, removed imports, renamed routes and classes, the Klarna removal, and the behavioral changes around refunds and callbacks.

Troubleshooting

  • Not authorized: Not authorized to PUT /payments/:id/link at /payment/authorize/... url:

    You should check at https://manage.quickpay.net/account/{your merchant id}/settings/users that System users > API User > User permissions > Create or update payment link have PUT checkbox checked. Also check QUICKPAY_API_KEY and QUICKPAY_AGREEMENT_ID is filled with API User's api key and agreement id rather than Payment Window's.

  • Validation error: order_id already exists on another payment

    Make sure you changed the Order prefix of your Quickpay payment method to some unique string like qp_<projectname>_<date>_ (when date should be updated to actual every time you recreate dev database) whenever you:

    • Recreating your database on dev environment and your order IDs become same as they was before
    • Use SetonoSyliusQuickpayPlugin at two different projects but with same Quickpay (developer) account credentials
  • Validation error: order_id must have length between 4 and 20

    You should cut the Order prefix of your Quickpay payment method to 11 chars or less.