bagisto/bagisto-rma
Bagisto RMA adds return merchandise authorization workflows to Bagisto stores, letting customers request returns, exchanges, or refunds from their accounts while admins review reasons, manage statuses, and process resolutions in the backend. It streamlines post-purchase support and order returns for...
Resolve the package name first. The listing says bagisto/bagisto-rma, but Webkul packages are published under webkul/. Check Packagist before pinning anything to your manifest:
composer require webkul/bagisto-rma:^2.2.3
php artisan vendor:publish --provider="Webkul\RMA\Providers\RMAServiceProvider"
php artisan migrate
php artisan storage:link # print view + conversation attachments need this
php artisan optimize:clear
Confirm the service provider is auto-discovered (most Webkul packages are). If the RMA menu doesn't appear in admin, the cause is almost always a missing ACL permission on your admin role — grant it before you start debugging routes.
Read these four things in this order before writing code:
packages/Webkul/RMA/src/Config/ (or config/rma.php post-publish) — master switch, attachment MIME/size rules, reopen policy, per-status behavior.Admin → Configure → RMA — rules/reasons, statuses, and custom fields are admin data, not code. A large share of "we need a feature" tickets are resolved here.packages/Webkul/RMA/src/Routes/ — split into admin, shop, and guest route files. The guest routes are the historical bug epicentre.rma_requests, rma_rules, rma_custom_fields, rma_conversations, rma_attachments). You'll debug via the DB far more than via the controller stack.Your first day-of-work task: enable RMA globally and per-product, create one reason and one status in the admin panel, then walk the full loop yourself as both guest and registered customer, with a configurable product: place order → My Account → Orders → Return → submit → respond in admin conversation → change status → print. This single pass reproduces roughly half of the v2.2.3 changelog and tells you exactly what you need to extend vs. what already works.
Configuration is the extension surface — prefer it to code. Return reasons, status labels, custom fields, attachment rules, and per-product enablement all live in the admin panel. Seed them via your own idempotent seeder (firstOrCreate) so ops can adjust policy without a deploy. Escalate to code only in this order: config → custom field → Blade override → model/service override in your own package.
Reuse the package's eligibility gate. Per-product RMA flags mean "can this line be returned?" is not an order-status predicate. Don't re-derive it in adjacent widgets, emails, or support macros:
$isEligible = app(\Webkul\RMA\Helpers\RMAHelper::class)->isRMAAllowed($product);
Custom fields are your structured-data hook — with caveats. They are the correct way to capture "batch number", "reason detail", etc., without migrations. Two known bugs: the required flag doesn't reliably enforce server-side, and the checkbox type has been broken. Prefer dropdowns for anything feeding downstream logic, and validate required fields in your own FormRequest. If a custom field must drive behavior (refund routing, ERP sync), mirror it into a real column via an observer rather than querying a JSON answers blob at runtime.
Wire integrations on the model, not the controllers. Status transitions (approve/decline/close/cancel/reopen) are the highest-value hook. Keep this the only place that knows about refunds, ERP, or Slack — package upgrades then have one blast radius:
// In your own service provider boot()
\Webkul\RMA\Models\RMA::updated(function ($rma) {
if ($rma->wasChanged('rma_status_id')) {
// map package status -> refund / ERP / notification
}
});
Validate uploads server-side, always. The v2.2.3 WEBP bug was client-side-only validation — the error popup appeared, the file still uploaded. If you build anything on the attachment path (custom uploader, S3 driver, AJAX endpoint), re-check MIME and size in a FormRequest. Never trust the shipped JS.
Guest and customer are separate code paths. They diverge in redirects, session cleanup, and "remove previously created RMA product" behavior. Any change to RMA creation must be tested in both. If your storefront disables guest checkout, gate the guest routes behind a feature flag rather than shipping an untested path.
Print view and attachments are the two most-overridden surfaces. If you use S3/CDN, publish and override the attachment storage config. If product images don't render in print, override the print Blade and resolve the URL through Bagisto's product-image helper rather than the package's raw path.
Seeder discipline. The demo seeder previously regenerated customer RMAs while leaving stale admin records. Restrict it to local/testing, and for staging data write your own idempotent seeder against rules/statuses.
Version coupling is the headline risk. The release explicitly targets Bagisto v2.2.3. Treat any Bagisto upgrade as an RMA regression event, not a routine composer update. Maintain a manual smoke list: guest create, registered create with configurable product, cancel, close-then-reopen declined, wrong-format upload, print view, Arabic status labels.
Config cache is the #1 "it's broken" cause. After any change in Configure → RMA, run php artisan optimize:clear and hard-refresh. A stale cache makes a correctly enabled product look ineligible.
Status parity between panels is a recurring weak spot. The changelog fixes multiple mismatches between customer list, customer view, and admin view. When QA'ing or writing tests, compare the rendered status string in all three places for the same record — asserting on one screen hides the bug.
Translation gaps recur. Arabic status translations were missing on the list page while correct on the view page; conversation messages and product names have both had gaps. Every new string needs entries in both Resources/lang/en/ and Resources/lang/ar/ (or your locale set). The list views are the ones that historically escape.
Reopen is two flags that must agree. "Allow New RMA Request for Declined Request" controls the Reopen action in admin — the corresponding policy and the current status must both permit it. The old "Allow New RMA Request for Pending Orders" setting was removed from the main config, so don't build on it.
Cancellation/closure hide affordances, they don't only change status. After cancel/close, the cancel icon is suppressed in the list. Gate any new buttons on the record's status, not a role check, or you'll ship a button that 500s on a closed record.
Test configurable products explicitly. A dedicated RMA bug exists for parent + variants; verify product name and image resolve on both the creation page and the view page (creation historically rendered blank names).
No API surface. There's no REST/GraphQL layer described. For headless or mobile, plan a thin controller layer over the package's models/services — and re-implement validation yourself, because the package's enforcement has historically lived in the view layer.
Print is HTML, not a PDF pipeline. If you need reliable PDFs, wrap the template with DomPDF/Snappy rather than relying on browser print, and verify product-image resolution (a fixed bug and a regression candidate).
Debug by following the record, not the UI. When a request misbehaves, go straight to the DB: the RMA row (status FK), the custom-field answers, and the attachment row (storage path). Almost every issue is one of those three being inconsistent with what the view assumes — faster than reading the controller stack.
Low adoption means you are the QA team. Seven stars, zero dependents, no community corpus. Budget for your own regression suite around the flows above, keep customizations in your own package (event listeners + view overrides), and maintain a one-command rollback to your pinned version.
How can I help you explore Laravel packages today?