# Return Portal + Auto-Label — Complete Documentation

> 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+…

- Page: <https://www.datafirefly.com/en/documentation/dfreturnportal/>
- Language: en
- Last updated: 2026-06-27
- Other languages: [fr](https://www.datafirefly.com/documentation/dfreturnportal/index.md), [es](https://www.datafirefly.com/es/documentation/dfreturnportal/index.md), [de](https://www.datafirefly.com/de/documentation/dfreturnportal/index.md), [it](https://www.datafirefly.com/it/documentation/dfreturnportal/index.md), [pl](https://www.datafirefly.com/pl/documentation/dfreturnportal/index.md), [nl](https://www.datafirefly.com/nl/documentation/dfreturnportal/index.md), [pt](https://www.datafirefly.com/pt/documentation/dfreturnportal/index.md)
- Index: <https://www.datafirefly.com/en/documentation/llms.txt>

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

1. Download the `dfreturnportal.zip` file.
2. In WordPress, go to **Plugins → Add New → Upload Plugin**.
3. Select the ZIP, click **Install Now**.
4. Activate the plugin.

### Method 2 — Via FTP/SSH

```bash
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_attachments` are 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, 10x15).

### 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:

1. **Header**: RMA code + status badge + back to list.
2. **Timeline**: 7 colored dots showing visual progress.
3. **Items to return**: table with product, SKU, quantity, unit price, reason, and per-item inspection (dropdown Approved / Partially / Rejected — activated after `received` status).
4. **Customer photos**: gallery if the customer uploaded supporting evidence.
5. **Activity log**: complete chronological history.
6. **Information**: customer email, order link, resolution preference, customer note.
7. **Return label**: carrier, tracking number, download PDF button, regenerate button.
8. **Actions**: "Move to: [next status]" buttons according to the state machine.
9. **Resolve** (visible if status is `received` or `inspecting`): 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)

```php
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

```php
// 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`:

```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:

```bash
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.log` if `WP_DEBUG_LOG` is 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.
