Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Refund Plugin Laravel Package

sylius/refund-plugin

Sylius Refund Plugin adds refund management to Sylius e-commerce stores. Administrators can create and process order and payment refunds, track refund states and items, and integrate refund workflows with Sylius resources and admin UI.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: sylius/refund-plugin is not a Laravel package. It is a Sylius (Symfony) bundle — no service provider, no config/refund.php, no Eloquent models. If you found it while searching Packagist from a Laravel project, stop: there is no supported way to install it into Laravel without dragging in the entire Sylius/Symfony stack. Two realistic paths:

  1. Run a Sylius store (separate app, e.g. composer create-project sylius/sylius-standard) and install the plugin there. This is the intended use.
  2. Mine it as a reference implementation if you are building refunds in Laravel — its domain model (Refund → RefundUnit → CreditMemo, draft → completed state machine) is a solid blueprint you can port to Eloquent, but no code is reusable directly.

For path 1, minimal install:

composer require sylius/refund-plugin

Then, in the Sylius app:

  • Register Sylius\RefundPlugin\RefundPlugin in config/bundles.php.
  • Import routing in config/routes.yaml (sylius_refund_admin, sylius_refund_shop — only load the ones you need).
  • Import the bundle config (config/packages/_sylius_refund.yaml) and run bin/console doctrine:migrations:migrate.
  • Add the admin resources (grids, forms, menu entries) — either copy from the plugin's src/Resources/config/app/ or include them from your _sylius.yaml. Without this the "Refunds" panel does not appear on the order screen.

Where to look first, in order: src/Resources/config/app/ (what needs wiring), src/Command/ + src/CommandHandler/ (the actual API surface), src/Refund/RefundPaymentMethodResolver.php (how "offline" refunds are decided), and the admin templates for the refund flow.

First use case: open a paid order in admin → Refunds → create a refund from order items/shipments → complete it → a credit memo is generated. If that works, the plumbing is correct.

Implementation Patterns

Programmatic refunds via the message bus. The public API is commands, not services. From your own controller or console command:

$bus->dispatch(new RefundUnits(
    $orderNumber,
    [new RefundUnit($orderItemUnitId, RefundType::ORDER_ITEM_UNIT, $quantity, $amount)],
    $paymentMethodId,
    'Damaged in transit'
));
$bus->dispatch(new CompleteRefund($refundId));

Creating and completing are separate steps; treat "create" as a draft document and "complete" as the irreversible step that generates the credit memo.

Offline refunds are the default, gateway refunds are yours to write. The plugin records that money should be returned and produces a credit memo — it does not call Stripe/PayPal. For automatic PSP refunds, decorate the RefundPayment handler or listen for the completion event and call your gateway there, then store the transaction reference on your extended refund entity. Implement RefundPaymentMethodInterface (plus its supports() method) to expose a "manual refund" channel in the admin dropdown — most teams add one such method meaning "paid out by bank transfer" and reconcile later.

Extend the entities, don't fork them. Use Sylius' resource override mechanism in _sylius.yaml (classes:, resolving_target_entity) and Doctrine mappedSuperclass to add fields like psp_reference. Fork the bundle only as a last resort — you will be maintaining it against every Sylius minor release.

Customize the credit memo by overriding the Twig template and, if the layout differs materially, decorating CreditMemoGenerator. The PDF adapter (dompdf/snappy) is configured app-side; pick it early because it is the most common source of install pain.

Subscribe to lifecycle events (RefundCreated, RefundCompleted, credit-memo-created, plus the command-handler middleware) for notifications, ERP sync, and ledger writes. In Symfony this is the EventDispatcher; do not assume Laravel-style model events exist on the Refund entity — they do not.

Headless / API. Sylius 1.12+ exposes order resources through API Platform, but the refund endpoints are admin-UI oriented. For a mobile app or SPA, write a thin custom resource that dispatches the same commands — that keeps the state machine as the single source of truth.

Gotchas and Tips

  • Version-lock to your Sylius. Plugin majors track Sylius majors; a mismatch surfaces as "class not found" or broken grid definitions, not a Composer error. Check the composer.json constraints before upgrading Sylius.
  • Installation is not just composer require. Missing route imports or missing resource config produce a plugin that "installs fine" but shows no Refunds tab. If the UI is absent, you skipped a config import.
  • Refunds are drafts until completed. A created refund does not change the order, does not mark the payment refunded, and generates no credit memo. Half the "the plugin is broken" reports are this.
  • Money is in minor units (integers). $amount = 1050 means 10.50. Mixing floats in from your own code is the fastest way to produce an over-refund or a credit memo off by 100×. Validate against the remaining refundable amount in your own code — the admin UI guards it, your custom controller probably won't.
  • No idempotency guard out of the box. Dispatching RefundUnits twice from a retried queue job creates two refunds. Check payment state and existing refunds before dispatching, or wrap dispatch in a unique-constrained job.
  • Credit memo failure can strand a refund. If the PDF adapter throws during completion, you get a refund with no memo. Catch/log, and provide an admin action to regenerate.
  • Changing the state machine is fragile. Adding a "pending_psp" state or reordering transitions in the refund state machine config can break the admin buttons silently. Prefer listeners over new transitions.
  • Debugging toolkit: bin/console debug:messenger to confirm handlers are registered, bin/console debug:container Sylius\\RefundPlugin for services, and the Sylius admin grid (shows refund state) as your first health check. Log channel noise is high — filter by sylius_refund.
  • Tests need the bundle enabled in the test env and the Sylius kernel booted; plain PHPUnit unit tests of handlers are awkward because they depend on repositories and the bus. Use KernelTestCase with the test database and assert on Refund state rather than on emitted events where possible.
  • Extension points, ranked by safety: template override → event listener → command-handler decoration → entity extension → bundle fork. Stay in the top three unless you genuinely need schema changes.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource