Return Portal + Auto-Label — Complete Documentation
Self-service customer return portal with multi-carrier labels, admin inspection, and automatic resolution for WooCommerce.
Self-service customer return portal, automatically generated multi-carrier return labels, admin inspection workflow, and resolution engine (refund, bonus store credit, replacement) for WooCommerce.
Version: 1.0.7
Compatibility: WordPress 6.2+ • WooCommerce 8.0+ • PHP 8.0+ • HPOS and Cart/Checkout Blocks
Overview
Return Portal + Auto-Label automates the entire lifecycle of a product return for your WooCommerce store:
- Customer side: the customer requests their return without contacting support, selects items, chooses a reason, and immediately receives their PDF label.
- Admin side: dashboard with timeline, line-by-line inspection, complete activity log, and automatic resolution.
- 6 carriers: Manual (native PDF), Colissimo, Mondial Relay, Chronopost, UPS, DPD.
- 3 resolutions: native WooCommerce refund, bonus store credit (+X%), or automatic replacement.
Return statuses
A return moves through up to 7 stages:
| Status | Description |
|---|---|
requested |
Request received, awaiting validation |
approved |
Approved by admin (or auto-approved under threshold) |
label_sent |
Label generated and sent to customer |
in_transit |
Package being shipped |
received |
Package received at warehouse |
inspecting |
Items being inspected |
resolved |
Resolution applied (refund / credit / replacement) |
Two additional terminal statuses: rejected and cancelled.
Installation
Method 1 — Via WordPress admin
- Download the
dfreturnportal.zipfile. - In WordPress, go to Plugins → Add New → Upload Plugin.
- Select the ZIP, click Install Now.
- Activate the plugin.
Method 2 — Via FTP/SSH
cd wp-content/plugins/
unzip dfreturnportal.zip
# Then activate from WordPress admin
Post-installation checks
- A Returns menu appears in the WordPress sidebar.
- An endpoint
/my-account/returns/is automatically created. - The custom tables
wp_dfrp_returns,wp_dfrp_return_items,wp_dfrp_history,wp_dfrp_attachmentsare created.
If the “Returns” tab doesn’t appear in My Account, go to Settings → Permalinks and click “Save” to refresh rewrite rules.
Initial configuration
Go to Returns → Settings.
General
| Setting | Description |
|---|---|
| Eligibility window | Number of days after the order during which a return is allowed. Default: 30 days. |
| Customer portal page | WordPress page where the [dfrp_portal] shortcode will be displayed. Optional if you only use the My Account endpoint. |
| Admin notifications | Email address that will receive notifications of new requests. Default: site admin. |
Carrier
Select the active carrier from the 6 available. You can also configure the label format (A4, A5, A6, 10×15).
Return address
Enter the physical address where return packages will be sent. Required to generate labels. Fields: Company, Street, City, Postcode, Country (ISO 2-letter code), Phone, Email.
Carrier credentials
For each API carrier (Colissimo, Mondial Relay, etc.), a collapsible block contains the required credentials. Only fill in the credentials for the carrier you actually use.
Resolution
| Setting | Description |
|---|---|
| Store credit bonus (%) | Percentage added to the refund amount when the customer chooses bonus store credit. Default: 10%. |
| Auto-approval threshold | Below this amount (shop currency), requests are auto-approved and the label is generated without admin intervention. Set to 0 to disable. |
| Proposed resolution by reason | For each reason (8 default reasons), choose the proposed resolution: refund / bonus credit / replacement. |
Exclusions
- Excluded categories: WooCommerce IDs separated by commas. Products in these categories will never be eligible for return.
- Excluded SKUs: one SKU per line.
Customer experience
Via the My Account page (logged-in customers)
The simplest and most-used flow. The Returns tab appears automatically in the /my-account/ menu next to “Orders”, “Addresses”, etc.
The customer clicks Returns, sees the list of their eligible orders (no need to enter email/number), clicks Start a Return on the relevant order, selects items, chooses a reason and quantity per item, optionally adds photos (if the reason requires it), chooses their preferred resolution, and immediately receives an RMA number (e.g. RMA-20260523-A1B2C3) plus a confirmation email.
Via a public page (guest customers)
Create a WordPress page, insert the shortcode:
[dfrp_portal]
The customer will need to enter their order number + email to authenticate. The rest of the flow is identical.
Track an existing request
On the public portal page, a “Track an existing request” block lets guest customers check their RMA status (status, carrier tracking number, link to the label).
Shortcode customization
[dfrp_portal title="Return Request" context="page"]
| Attribute | Values | Description |
|---|---|---|
title |
free text | Portal title (rarely used visually). |
context |
page or myaccount |
Force the context. myaccount skips the lookup step for logged-in users. |
Admin workflow
Dashboard
Accessible via Returns → Dashboard. Contains 9 colored statistics cards (count by status, clickable to filter the list), the 10 latest RMAs with quick detail access, and a customer portal URL banner with a “Copy” button to share.
Request list
Accessible via Returns → All Requests. Table with filters by status, free-text search (RMA, customer email, order number), and pagination (20 per page).
Request detail page
Components:
- Header: RMA code + status badge + back to list.
- Timeline: 7 colored dots showing visual progress.
- Items to return: table with product, SKU, quantity, unit price, reason, and per-item inspection (dropdown Approved / Partially / Rejected — activated after
receivedstatus). - Customer photos: gallery if the customer uploaded supporting evidence.
- Activity log: complete chronological history.
- Information: customer email, order link, resolution preference, customer note.
- Return label: carrier, tracking number, download PDF button, regenerate button.
- Actions: “Move to: [next status]” buttons according to the state machine.
- Resolve (visible if status is
receivedorinspecting): resolution selector + “Apply” button.
Allowed transitions
The state machine prevents invalid transitions:
requested → approved | rejected | cancelled
approved → label_sent | rejected | cancelled
label_sent → in_transit | cancelled
in_transit → received
received → inspecting
inspecting → resolved | rejected
The statuses resolved, rejected and cancelled are terminal.
Auto-approval
If you have configured an auto-approval threshold (e.g. €50), any request whose total amount is less than or equal to the threshold is automatically moved from requested to approved, the label is generated immediately and sent to the customer, with no admin intervention required.
Supported carriers
Manual (no API)
Ideal to start with. Generates a native PDF slip with QR code, with no external API dependency. Zero configuration, free, works immediately. Limitation: no automatic tracking. Usage: the customer prints it, you put it in the package on initial shipment, or they tape it on for classic postal returns.
Colissimo (La Poste)
Official REST API (Sls Service generateLabel). Required credentials: Contract Number, Password. Specifics: generates a parcelNumber barcode, return type set to “3”, default PDF format.
Mondial Relay
SOAP API (WSI4_CreationEtiquette). Required credentials: Brand code, Private Key, Pickup Point. Specifics: CCC collection mode (Customer-Entrusted Parcel), MD5 signature required.
Chronopost
SOAP API (shippingMultiParcelV5). Required credentials: Account Number, Password, Subaccount. Specifics: Product Code 8R (Relais return), return mode 2.
UPS
REST API v2403 (/ship). Required credentials: Client ID (OAuth2), Client Secret, Shipper Number. Specifics: ReturnService.Code = 8 (Electronic Return Label), default base64 GIF format.
DPD
REST API (cargonet endpoint). Required credentials: Username, Password, Customer ID. Specifics: Basic Auth, PDF A6 format.
Resolutions
Refund
Uses WooCommerce’s native wc_create_refund() function. Re-credits the original payment method, automatic restock (configurable), creates a refund note on the order, and triggers a native WooCommerce confirmation email.
Bonus store credit
Automatically creates a WooCommerce coupon:
- Amount = return total + bonus (configurable %, default 10%).
- Email restriction: usable only by the customer’s email.
- Expiration: 6 months by default.
- Single use.
The customer receives an email with their coupon code in large type.
Replacement
Creates a new WooCommerce order at €0 (free for the customer). Shipping/billing addresses copied from the original order, items identical to the approved returned items, initial status processing (you ship it normally). The customer receives an email with the new order number.
Automatic proposal
The engine analyzes returned-item reasons and proposes the most relevant resolution. Configuration in Returns → Settings → Proposed resolution by reason. Default mapping:
| Reason | Proposed resolution |
|---|---|
| Damaged item | Replacement |
| Defective item | Replacement |
| Wrong item received | Replacement |
| Matches description but doesn’t fit | Refund |
| Changed mind | Bonus credit |
| Wrong size or color | Bonus credit |
| Late delivery | Refund |
| Other | Refund |
Customization
Template overrides
All email templates are overridable via your theme. Copy the source file templates/emails/*.php to yourtheme/dfreturnportal/emails/*.php.
Hooks (actions)
do_action('dfrp_after_return_created', int $returnId, array $return);
do_action('dfrp_status_changed', int $returnId, string $fromStatus, string $toStatus);
do_action('dfrp_label_generated', int $returnId, array $label);
do_action('dfrp_before_resolution', int $returnId, string $resolution);
do_action('dfrp_after_resolution', int $returnId, string $resolution, array $result);
Filters
// Customize eligible order statuses (default: ['completed', 'processing'])
add_filter('dfrp_eligible_order_statuses', function($statuses) {
$statuses[] = 'on-hold';
return $statuses;
});
// Customize return reasons
add_filter('dfrp_reasons', function($reasons) {
$reasons[] = [
'code' => 'custom_reason',
'label' => 'My custom reason',
'require_photo' => false,
];
return $reasons;
});
// Customize My Account endpoint slug (default: 'returns')
add_filter('dfrp_myaccount_endpoint', function() {
return 'my-returns';
});
// Customize My Account menu label
add_filter('dfrp_myaccount_menu_label', function() {
return 'My product returns';
});
// Add a custom carrier
add_filter('dfrp_register_carriers', function($carriers) {
$carriers[] = new MyCustomCarrier();
return $carriers;
});
Clean uninstallation
By default, uninstallation preserves data (tables and options). To purge everything on uninstall, add to wp-config.php:
define('DFRP_DELETE_DATA_ON_UNINSTALL', true);
REST API
All endpoints are under the dfrp/v1 namespace. Base URL: https://yoursite.com/wp-json/dfrp/v1/.
Public endpoints
| Endpoint | Method | Description |
|---|---|---|
/lookup |
POST | Look up an order by number + email. |
/create |
POST | Create a new return request. |
/track |
POST | Track an RMA via code + email. |
/upload-photo |
POST | Upload a supporting photo (multipart). |
Logged-in customer endpoints
| Endpoint | Method | Description |
|---|---|---|
/my-orders |
GET | List eligible orders of the logged-in customer. |
Admin endpoints (capability manage_woocommerce)
| Endpoint | Method | Description |
|---|---|---|
/admin/returns |
GET | Paginated list with filters. |
/admin/returns/{id} |
GET | Return detail. |
/admin/returns/{id}/transition |
POST | Change status. |
/admin/returns/{id}/inspect-item |
POST | Item inspection result. |
/admin/returns/{id}/resolve |
POST | Apply a resolution. |
/admin/returns/{id}/regenerate-label |
POST | Regenerate the label. |
Troubleshooting
The portal shows “Loading…” indefinitely
- Check the browser console (F12) — you should see
[Return Portal] script frontend.js executed. - If nothing appears, a security plugin (Wordfence, Sucuri, NinjaFirewall) is blocking the inline script. Temporarily disable it to test.
- Also check JS optimizers (WP Rocket “Delay JS”, Autoptimize, Cloudflare Rocket Loader) — the plugin already emits the necessary opt-out attributes, but very aggressive configurations may still block.
The “Returns” tab doesn’t appear in My Account
Go to Settings → Permalinks and click “Save Changes” (without changing anything). This forces WordPress to regenerate rewrite rules.
“Constant DFRP_VERSION already defined” error
The plugin folder is duplicated. Check:
ls -la wp-content/plugins/ | grep dfreturnportal
Delete duplicate copies (e.g. dfreturnportal-old/, dfreturnportal-1/).
Customers don’t receive emails
- Make sure email sending works globally.
- Set up a proper SMTP (WP Mail SMTP, FluentSMTP).
- Check the recipient domain’s spam logs.
The PDF label is empty or corrupt
- Make sure the return address is fully filled in.
- For API carriers, validate credentials in test mode first.
- Check PHP logs (
/wp-content/debug.logifWP_DEBUG_LOGis active).
FAQ
Does the plugin require a carrier subscription?
No. The Manual mode generates a native PDF slip without any API dependency. API modes (Colissimo, etc.) are optional.
HPOS (High-Performance Order Storage) compatible?
Yes, fully. The plugin declares its compatibility at WooCommerce startup.
Cart/Checkout Blocks compatible?
Yes.
Are product variations handled?
Yes, each variation is treated as a distinct item.
And virtual or downloadable products?
They are automatically excluded from returns.
Can the customer partially return an order?
Yes. Eligibility takes into account quantities already returned via previous RMAs.
Multi-language?
The plugin is translation-ready (Text Domain dfreturnportal, .pot file provided). Compatible with WPML, Polylang, TranslatePress.
Are customer photos stored securely?
Yes, in the WordPress media library with .htaccess rules preventing directory listing.