Order ID: {{order_id}}
Status: {{order_status}}
Total: {{order_total}}
Email: {{order_email}}
Date: {{order_date}}
Thank you for your order!
A starter for Medusa plugins.
npm install order-management
Building blocks for digital commerce
This starter is compatible with versions >= 2.4.0 of @medusajs/medusa.
- Order Management: Cancel and reorder functionality for orders
- Guest Order Portal: Complete look-up system for guest users (OTP-based) with full order management capabilities
- Guest Order Actions: Secure endpoints for guest users to view orders, initiate returns, cancel orders, reorder, and download invoices
- Order Confirmation Emails: Automatically sends email notifications when orders are placed (with "Claim Order" support for registered users)
- Status-Based Notifications: Configure automatic email and SMS notifications for any order status change (order placed, shipped, delivered, etc.)
- Return Orders Admin Panel: Complete return orders management section in the Medusa Admin Panel with list view, filtering, search, detail pages, and status management
- Customer-Initiated Swaps: Complete swap/exchange functionality allowing customers to request item swaps directly from the storefront with full status lifecycle management
The plugin can be configured in your medusa-config.js file:
``js
import { defineConfig } from "@medusajs/framework/utils"
module.exports = defineConfig({
// ...
plugins: [
{
resolve: "order-management",
options: {
// Required options
storefrontUrl: process.env.STOREFRONT_URL || "http://localhost:8000",
jwtSecret: process.env.JWT_SECRET || "medusa-secret-guest-access",
// Email template options
email: {
orderConfirmTemplate: "src/templates/emails/order-confirmation.html", // Path to HTML template file
otpTemplate: "src/templates/emails/otp-verification.html", // Path to HTML template file (required)
},
// Optional SMTP configuration (for email delivery)
smtp: {
enabled: process.env.FORCE_SMTP_REDELIVER === "true",
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT ? Number(process.env.SMTP_PORT) : undefined,
secure: process.env.SMTP_SECURE === "true",
auth: {
user: process.env.SMTP_AUTH_USER,
pass: process.env.SMTP_AUTH_PASS,
},
from: process.env.SMTP_FROM || process.env.SMTP_AUTH_USER,
},
// Optional status-based notification configuration
notifications: {
enabled: true,
statusConfig: {
"pending": [
{
template: "src/templates/emails/order-placed.html",
channel: "email"
},
{
template: "src/templates/sms/order-placed.txt",
channel: "sms"
},
{
template: "src/templates/push/order-placed.txt",
channel: "push"
}
],
"shipped": [
{
template: "src/templates/emails/order-shipped.html",
channel: "email"
}
],
"delivered": [
{
template: "src/templates/emails/order-delivered.html",
channel: "email"
}
],
"canceled": [
{
template: "src/templates/emails/order-canceled.html",
channel: "email"
}
]
}
},
},
},
],
})
`
Required Options:
- storefrontUrl - Your storefront URL (required)jwtSecret
- - JWT secret for guest order tokens (required)
Email Template Options:
- email.orderConfirmTemplate - Path to HTML template file for order confirmation emails (optional)email.otpTemplate
- - Path to HTML template file for OTP verification emails (required for guest OTP functionality)
Optional SMTP Options:
- smtp.enabled - Enable SMTP email delivery (default: false)smtp.host
- - SMTP server host (required if enabled)smtp.port
- - SMTP server port (required if enabled)smtp.secure
- - Use secure connection (default: true)smtp.auth.user
- - SMTP username (required if enabled)smtp.auth.pass
- - SMTP password (required if enabled)smtp.from
- - From email address (optional, defaults to smtp.auth.user)
Optional Status-Based Notification Options:
- notifications.enabled - Enable status-based notifications (default: false)notifications.statusConfig
- - Map of order status to notification configurationstemplate
- Each status can have multiple notification configurations
- Each configuration requires:
- - Path to template file (HTML for email, text for SMS/push)channel
- - Notification channel (supports any string: "email", "sms", "push", or any custom channel)
Notes:
- storefrontUrl and jwtSecret are required. All other options are optional.email.otpTemplate
- Order confirmation emails are only sent if a template path is provided. If no template is configured, emails will not be sent.
- OTP template is mandatory for guest OTP functionality. The guest OTP request API will return an error if is not configured.{{otp}}
- OTP templates should include placeholder for the verification code.
- SMTP configuration is optional. If not enabled, emails will use Medusa's default email provider.
- Status-based notifications are optional. If enabled, notifications will be sent whenever an order reaches a configured status.
The plugin includes a dedicated section in the Medusa Admin Panel for managing customer return orders. This feature provides administrators with comprehensive tools to view, search, filter, and manage all return orders.
- List View: View all return orders in a table format with key information
- Search: Search returns by return ID, order ID, or customer email
- Filtering: Filter returns by status (requested, received, requires_action, completed, canceled)
- Detail Pages: View detailed information for each return order including:
- Return information (ID, status, refund amount, reason, note)
- Related order information (order ID, customer, totals)
- Return items with quantities
- Status history timeline
- Metadata
- Status Management: Update return status with validation and status history tracking
- Pagination: Load more returns with pagination support
Once the plugin is installed and configured, you can access the Return Orders section from the Admin Panel sidebar. The section appears as "Return Orders" with a return icon.
The plugin provides the following admin API endpoints for return orders management:
- GET /admin/returns - List all return orders with filtering, search, and paginationGET /admin/returns/:id
- - Get detailed information for a specific return orderPOST /admin/returns/:id/status
- - Update the status of a return order
The plugin supports the following return statuses:
- requested - Return has been requested by the customerreceived
- - Return items have been receivedrequires_action
- - Return requires manual interventioncompleted
- - Return has been completedcanceled
- - Return has been canceled
Status updates are tracked in the return's metadata with timestamps and admin user IDs.
The Guest Order Portal allows users who placed orders without an account to view their order status, cancel orders, reorder, initiate returns, and download invoices securely via an OTP (One-Time Password) system.
- Strict Separation: Guest orders are strictly filtered by unique guest customer IDs. Registered account orders will never be exposed in the guest portal.
- Access Control: The portal uses short-lived JWT tokens issued upon successful OTP verification.
- Account Protection: Emails belonging to registered accounts are blocked from the guest OTP flow to prevent unauthorized access and encourage secure logins.
For detailed API documentation, usage examples, error handling, and security best practices, see the Guest Order API Guide.
The plugin provides the following store API endpoints for the Guest Order Portal:
#### Authentication
- POST /store/otp/request - Request an OTP for an email or phone number.POST /store/otp/verify
- - Verify the OTP and receive a guest JWT token.
#### Guest Order Management
- GET /store/guest-orders - List summary of orders for the verified guest identifier.GET /store/guest-orders/:id
- - Get full details for a specific guest order (includes items, shipping status, etc.).
#### Guest Order Actions
- POST /store/guest-orders/:id/returns - Initiate a return request for a guest order.POST /store/guest-orders/:id/cancel
- - Cancel a guest order.POST /store/guest-orders/:id/reorder
- - Reorder a guest order (creates a new cart with the same items).GET /store/guest-orders/:id/invoice
- - Download a PDF invoice for the guest order.
Note: All GET and POST requests to guest order endpoints require the JWT token in the Authorization: Bearer header.
The plugin supports custom HTML templates for order confirmation emails. Templates use variable replacement with {{variable_name}} syntax.
Create HTML template files for email notifications. Place them in your project, for example:
``
your-project/
src/
templates/
emails/
order-confirmation.html
Example Email Template (src/templates/emails/order-confirmation.html):
` Order ID: {{order_id}} Status: {{order_status}} Total: {{order_total}} Email: {{order_email}} Date: {{order_date}} Thank you for your order!html`
Order Confirmation
The following variables are available in order confirmation email templates:
| Variable | Description | Example |
|----------|-------------|---------|
| {{order_id}} | Order ID | order_123 |{{order_status}}
| | Order status | pending |{{order_total}}
| | Order total amount | 99.99 |{{order_email}}
| | Customer email address | customer@example.com |{{order_date}}
| | Order creation date | 2024-01-15T10:30:00Z |{{order_items}}
| | Order items array (JSON stringified) | [{"title":"Product","quantity":1}] |{{shipping_address}}
| | Shipping address object (JSON stringified) | {"first_name":"John",...} |{{billing_address}}
| | Billing address object (JSON stringified) | {"first_name":"John",...} |{{is_registered}}
| | Whether the customer email has a registered account | true |{{claim_link}}
| | Link for registered users to claim their guest order | http://.../claim?order_id=... |
The plugin supports automatic notifications (email and SMS) based on order status changes. When an order reaches a configured status, the plugin will automatically send all configured notifications for that status.
π For detailed documentation, see the Notifications Guide
- Multi-Channel Support: Send notifications via email and/or SMS
- Status-Based Configuration: Configure different notifications for different order statuses
- Multiple Notifications Per Status: Send multiple notifications for a single status change
- Template-Based: Use custom templates for each notification
- Automatic Triggering: Notifications are automatically triggered when order status changes
Configure status-based notifications in your medusa-config.js:
`js`
{
resolve: "order-management",
options: {
// ... other options ...
notifications: {
enabled: true,
statusConfig: {
// Status name as key
"pending": [
{
template: "src/templates/emails/order-placed.html",
channel: "email"
},
{
template: "src/templates/sms/order-placed.txt",
channel: "sms"
}
],
"shipped": [
{
template: "src/templates/emails/order-shipped.html",
channel: "email"
}
],
"delivered": [
{
template: "src/templates/emails/order-delivered.html",
channel: "email"
},
{
template: "src/templates/sms/order-delivered.txt",
channel: "sms"
}
],
"canceled": [
{
template: "src/templates/emails/order-canceled.html",
channel: "email"
}
]
}
}
}
}
The notification system supports dynamic channel types, allowing you to configure any channel supported by your notification service:
- email: Sends email notifications to the customer's email address
- sms: Sends SMS notifications to the customer's phone number (from shipping address)
- push: Sends push notifications (browser/mobile notifications) to the customer's device
- Any custom channel: Configure any channel name supported by your notification service (e.g., whatsapp, slack, webhook, etc.)
The system automatically resolves recipients and sends notifications through the appropriate channel handler.
The notification system listens to the following Medusa events:
- order.updated - General order status changesorder.placed
- - When an order is first placedorder.shipment_created
- - When a shipment is createdorder.fulfillment_created
- - When fulfillment startsorder.completed
- - When an order is completedorder.canceled
- - When an order is canceleddelivery.created
- - When an order is delivered
The plugin now supports push notifications (browser/mobile push) alongside email and SMS. Here's how to configure them:
`js`
{
resolve: "order-management",
options: {
// ... other options ...
notifications: {
enabled: true,
statusConfig: {
"shipped": [
{
template: "src/templates/emails/order-shipped.html",
channel: "email"
},
{
template: "src/templates/push/order-shipped.txt",
channel: "push" // Browser/mobile push notification
}
],
"delivered": [
{
template: "src/templates/emails/order-delivered.html",
channel: "email"
},
{
template: "src/templates/sms/order-delivered.txt",
channel: "sms"
},
{
template: "src/templates/push/order-delivered.txt",
channel: "push" // Browser/mobile push notification
}
]
}
}
}
}
Push Notification Template Example (src/templates/push/order-shipped.txt):
``
π¦ Your order {{order_id}} has been shipped! Track your package to stay updated.
Important Notes:
- Push notifications require a configured notification service provider (e.g., FCM, OneSignal, Web Push API)
- The recipient resolver for push notifications uses the customer's email to identify the device token
- You may need to implement custom device token registration in your storefront
- Push notification templates should be concise (similar to SMS)
Note: The plugin includes example templates in src/templates/emails/ and src/templates/sms/ that you can use as a starting point.
#### Email Templates
Create HTML templates for email notifications. Templates use the same variable replacement syntax as order confirmation emails:
Example: Order Shipped Email (src/templates/emails/order-shipped.html)
` Hi there, Great news! Your order {{order_id}} has been shipped and is on its way to you. Order Total: {{order_total}} Shipped on: {{order_date}} Thank you for your purchase!html`
Your Order Has Shipped!
#### SMS Templates
Create plain text templates for SMS notifications:
Example: Order Shipped SMS (src/templates/sms/order-shipped.txt)
``
Your order {{order_id}} has been shipped! Total: {{order_total}}. Thank you for your purchase.
All notification templates have access to the same variables as order confirmation emails:
- {{order_id}} - Order ID{{order_status}}
- - Current order status{{order_total}}
- - Order total amount{{order_email}}
- - Customer email address{{order_date}}
- - Order creation/update date{{order_items}}
- - Order items array (JSON stringified){{shipping_address}}
- - Shipping address object (JSON stringified){{billing_address}}
- - Billing address object (JSON stringified){{is_registered}}
- - Whether the customer has a registered account{{claim_link}}
- - Link for registered users to claim their order
Common Medusa order statuses you can configure notifications for:
- pending - Order placed, payment pendingawaiting
- - Awaiting fulfillmentfulfilled
- - Order fulfilledshipped
- - Order shippeddelivered
- - Order deliveredcompleted
- - Order completedcanceled
- - Order canceledrequires_action
- - Order requires action
Note: You can configure notifications for any custom status your store uses.
The notification system is designed to be extensible. Any channel name you configure will automatically be handled by the notification service. For example:
`js`
notifications: {
enabled: true,
statusConfig: {
"shipped": [
{
template: "src/templates/whatsapp/order-shipped.txt",
channel: "whatsapp" // Custom channel
},
{
template: "src/templates/slack/order-shipped.txt",
channel: "slack" // Custom channel
}
]
}
}
Channel Handler Flow:
1. System detects configured channel (e.g., whatsapp, slack)
2. Resolves recipient using channel-specific resolver (falls back to email/customer ID)
3. Loads and renders the specified template
4. Sends notification via Medusa's notification service with the channel name
5. Your configured notification provider handles the actual delivery
Requirements:
- Configure a notification provider that supports your custom channel
- The provider should handle the channel name in its notification payload
- Implement custom recipient resolvers if needed (by default, uses email or customer ID)
1. Keep SMS/Push Messages Short: SMS and push notifications have character limits, keep messages concise
2. Use Descriptive Subject Lines: For email templates, include clear subject information
3. Test Templates: Test your templates with sample data before going live
4. Provide Value: Only send notifications that provide value to customers
5. Include Contact Info: Add customer support contact information in templates
6. Mobile-Friendly Emails: Design email templates that work well on mobile devices
7. SMS/Push Opt-In: Ensure customers have opted in to receive SMS and push notifications
8. Channel Fallbacks: Consider configuring multiple channels (e.g., email + push) for important notifications
`js`
// In medusa-config.js
notifications: {
enabled: true,
statusConfig: {
// Order placed
"pending": [
{
template: "src/templates/emails/order-placed.html",
channel: "email"
}
],
// Order processing
"awaiting": [
{
template: "src/templates/emails/order-processing.html",
channel: "email"
}
],
// Order shipped
"shipped": [
{
template: "src/templates/emails/order-shipped.html",
channel: "email"
},
{
template: "src/templates/sms/order-shipped.txt",
channel: "sms"
}
],
// Order delivered
"delivered": [
{
template: "src/templates/emails/order-delivered.html",
channel: "email"
},
{
template: "src/templates/sms/order-delivered.txt",
channel: "sms"
},
{
template: "src/templates/push/order-delivered.txt",
channel: "push"
}
]
}
}
This configuration will:
1. Send an email when the order is placed (pending)awaiting
2. Send an email when the order starts processing ()shipped
3. Send both email and SMS when the order is shipped ()delivered
4. Send email, SMS, and push notifications when the order is delivered ()
The plugin includes a complete swap/exchange feature that allows customers to request item swaps directly from the storefront. This extends Medusa v2's native OrderExchange functionality with customer-facing APIs and admin management tools.
- Customer-Initiated Swaps: Customers can request swaps for their orders directly from the storefront
- Complete Status Lifecycle: Full status tracking from requested β approved β return_started β return_shipped β return_received β new_items_shipped β completed
- Admin Management: Complete admin panel for viewing, approving, rejecting, and managing swaps
- Price Difference Calculation: Automatic calculation of price differences between returned and new items
- Status History: Complete audit trail of all status changes
``
requested β approved β return_started β return_shipped β return_received β new_items_shipped β completed
β rejected
β cancelled (from any status except completed)
For detailed implementation instructions on integrating exchange functionality into your storefront, see the Storefront Exchange Implementation Guide. This guide includes:
- Complete API documentation with request/response examples
- React/TypeScript implementation examples
- Error handling best practices
- Status flow diagrams
- Integration checklist
#### Create Swap Request
`bash
POST /store/swaps
Authorization: Bearer
Content-Type: application/json
{
"order_id": "order_123",
"return_items": [
{
"id": "item_123",
"quantity": 1,
"reason": "Wrong size"
}
],
"new_items": [
{
"variant_id": "variant_456",
"quantity": 1
}
],
"reason": "Size exchange",
"note": "Please send size M instead"
}
`
#### List Customer Swaps
`bash`
GET /store/swaps
Authorization: Bearer
#### Get Swap Details
`bash`
GET /store/swaps/{swap_id}
Authorization: Bearer
#### List Swaps for Order
`bash`
GET /store/orders/{order_id}/swaps
Authorization: Bearer
#### Create Swap for Order
`bash
POST /store/orders/{order_id}/swaps
Authorization: Bearer
Content-Type: application/json
{
"return_items": [
{
"id": "item_123",
"quantity": 1
}
],
"new_items": [
{
"variant_id": "variant_456",
"quantity": 1
}
],
"reason": "Size exchange"
}
`
#### Cancel Swap
`bash`
POST /store/swaps/{swap_id}/cancel
Authorization: Bearer
#### List All Swaps
`bash`
GET /admin/swaps?status=requested&order_id=order_123&limit=50&offset=0
#### Get Swap Details
`bash`
GET /admin/swaps/{swap_id}
#### Approve Swap
`bash`
POST /admin/swaps/{swap_id}/approve
#### Reject Swap
`bash
POST /admin/swaps/{swap_id}/reject
Content-Type: application/json
{
"reason": "Item out of stock"
}
`
#### Update Swap Status
`bash
POST /admin/swaps/{swap_id}/status
Content-Type: application/json
{
"status": "return_started",
"metadata": {
"return_label_id": "label_123"
}
}
`
The plugin includes a complete admin UI for managing swaps:
- Swaps List Page: View all swaps with filtering by status, search by order ID, and pagination
- Swap Detail Page: View complete swap information including:
- Swap details (ID, status, dates, price difference)
- Original order information
- Return items list
- New items list
- Status history timeline
- Action buttons (approve/reject/update status)
Access the Swaps section from the Admin Panel sidebar.
The plugin provides helper methods for easy integration:
`typescript
import { createSwapRequest, getSwaps, getSwap, cancelSwap } from "order-management/helpers"
// Create a swap request
const swap = await createSwapRequest({
orderId: "order_123",
returnItems: [
{ id: "item_123", quantity: 1, reason: "Wrong size" }
],
newItems: [
{ variant_id: "variant_456", quantity: 1 }
],
reason: "Size exchange",
note: "Please send size M"
}, container)
// Get swaps for an order
const swaps = await getSwaps("order_123", container)
// Get a specific swap
const swapDetails = await getSwap("swap_123", container)
// Cancel a swap
const cancelledSwap = await cancelSwap("swap_123", container)
`
The swap status flow enforces valid transitions:
- requested β approved, rejected, cancelledreturn_started
- approved β , cancelledreturn_shipped
- return_started β , cancelledreturn_received
- return_shipped β , cancellednew_items_shipped
- return_received β , cancelledcompleted
- new_items_shipped β , cancelled
- rejected β (terminal state)
- completed β (terminal state)
- cancelled β (terminal state)
Invalid status transitions will be rejected with a descriptive error message.
The plugin includes a comprehensive standalone return management system that allows customers to initiate returns directly from the storefront. This extends Medusa v2's native Return functionality with customer-facing APIs, admin management tools, and complete status lifecycle tracking.
- Customer-Initiated Returns: Customers can request returns for their orders directly from the storefront
- Complete Status Lifecycle: Full status tracking from requested β approved β received β refunded β completed
- Admin Management: Complete admin panel with action buttons for approving, rejecting, marking as received, and processing refunds
- Automatic Refund Calculation: Automatic calculation of refund amounts based on returned items
- Status History: Complete audit trail of all status changes with timestamps and admin IDs
- Auto-Linking: Automatic linking between Medusa returns and custom return module via event subscribers
- Multi-Channel Notifications: Support for email, SMS, and push notifications for return status changes
``
requested β approved β received β refunded β completed
β rejected
β cancelled (by customer, only when requested)
#### Create Return Request
`bash
POST /store/returns
Authorization: Bearer
Content-Type: application/json
{
"order_id": "order_123",
"return_items": [
{
"id": "item_123",
"quantity": 1,
"reason": "Defective item",
"variant_id": "variant_123" // Optional
}
],
"reason": "Product defective",
"note": "Item arrived damaged"
}
`
#### List Customer Returns
`bash`
GET /store/returns?order_id=order_123
Authorization: Bearer
#### Get Return Details
`bash`
GET /store/returns/{return_id}
Authorization: Bearer
#### Create Return for Order
`bash
POST /store/orders/{order_id}/returns
Authorization: Bearer
Content-Type: application/json
{
"return_items": [
{
"id": "item_123",
"quantity": 1,
"reason": "Wrong size"
}
],
"reason": "Size issue",
"note": "Ordered wrong size"
}
`
#### Cancel Return
`bash`
POST /store/returns/{return_id}/cancel
Authorization: Bearer
Note: Returns can only be cancelled when status is requested.
#### List All Returns
`bash`
GET /admin/returns?status=requested&order_id=order_123&limit=50&offset=0
#### Get Return Details
`bash`
GET /admin/returns/{return_id}
#### Approve Return
`bash
POST /admin/returns/{return_id}/approve
Content-Type: application/json
{
"return_location_id": "loc_123", // Optional
"return_shipping_method_id": "sm_123" // Optional
}
`
Approving a return will:
- Create a Medusa return entity
- Update status to approved
- Link the Medusa return ID to the custom return
- Track approval in status history
#### Reject Return
`bash
POST /admin/returns/{return_id}/reject
Content-Type: application/json
{
"reason": "Outside return window"
}
`
#### Mark as Received
`bash
POST /admin/returns/{return_id}/mark-received
Content-Type: application/json
{
"note": "Items received in good condition" // Optional
}
`
#### Process Refund
`bash
POST /admin/returns/{return_id}/process-refund
Content-Type: application/json
{
"note": "Refund processed to original payment method" // Optional
}
`
#### Update Return Status (Manual)
`bash
POST /admin/returns/{return_id}/status
Content-Type: application/json
{
"status": "received",
"metadata": {
"note": "Custom status update"
}
}
`
The plugin includes a complete admin UI for managing returns:
- Returns List Page: View all returns with filtering by status, search by return/order ID, and pagination
- Return Detail Page: View complete return information including:
- Return details (ID, status, dates, refund amount, refund status)
- Action buttons (Approve & Process Return, Reject, Mark as Received, Process Refund)
- Medusa return information (when linked)
- Related order information
- Return items list
- Status history timeline
Action Buttons by Status:
- requested: Show "Approve & Process Return" and "Reject Return" buttons
- approved: Show "Mark as Received" button
- received: Show "Process Refund" button
Access the Returns section from the Admin Panel sidebar at /returns.
The return status flow enforces valid transitions:
- requested β approved, rejected, cancelledreceived
- approved β , cancelledrefunded
- rejected β (terminal state)
- received β completed
- refunded β
- completed β (terminal state)
- cancelled β (terminal state)
Invalid status transitions will be rejected with a descriptive error message.
The return flow includes automatic event handling:
#### Return Created Subscriber
- Listens to: return.created, order.return_requestedmedusa_return_id
- Automatically links Medusa returns to custom return module
- Updates return with
- Tracks linking history in metadata
#### Return Received Subscriber
- Listens to: return.receivedreceived
- Automatically updates return status to
- Tracks receipt in status history
#### Return Refunded Subscriber
- Listens to: refund.created, return.refund_processedrefunded
- Automatically updates return status to refund_status
- Updates to refunded
- Tracks refund amount and timestamp
Refund amounts are automatically calculated based on:
- Item unit prices at time of order
- Quantity of items being returned
- Stored in refund_amount field (in cents)
Example calculation:
`typescript`
refund_amount = Ξ£ (item.unit_price * return_quantity) for each returned item
Returns are validated for:
- Order Status: Order must be fulfilled or partially fulfilled
- Fulfillment: Order must have at least one shipped/delivered fulfillment
- Return Window: Default 30 days from fulfillment date (configurable)
- Duplicate Prevention: Cannot create duplicate returns for same items
- Quantity Validation: Return quantity cannot exceed order quantity
- Rate Limiting: Maximum 5 returns per order, 20 returns per customer (configurable)
The plugin provides helper functions for return operations:
`typescript
import {
getOrderReturnData,
validateReturnWindow,
calculateRefundAmount,
canApproveReturn,
canCancelReturn,
canMarkAsReceived,
canProcessRefund,
} from "order-management/helpers/returns"
// Get order data for return
const returnData = await getOrderReturnData("order_123", container)
// Validate return window
const isValid = validateReturnWindow(order.created_at, 30) // 30 days
// Calculate refund amount
const refund = calculateRefundAmount(returnItems, orderItems)
// Check status permissions
const canApprove = canApproveReturn(return.status)
const canCancel = canCancelReturn(return.status)
const canReceive = canMarkAsReceived(return.status)
const canRefund = canProcessRefund(return.status)
`
The custom return module integrates seamlessly with Medusa's native return functionality:
1. Return Creation: When approved, creates a Medusa return entity via orderModuleService.createReturn()medusa_return_id
2. Linking: Stores for bidirectional linking
3. Status Sync: Event subscribers automatically sync status changes from Medusa to custom module
4. View in Medusa: Admin UI provides direct links to view returns in Medusa admin
Returns store rich metadata for audit trails:
`typescript``
{
metadata: {
created_at: "2026-02-05T10:30:00Z",
customer_id: "cus_123",
approved_at: "2026-02-05T11:00:00Z",
approved_by: "admin_456",
received_at: "2026-02-10T14:20:00Z",
refunded_at: "2026-02-10T15:00:00Z",
medusa_return_id: "ret_789",
medusa_return_linked_at: "2026-02-05T11:00:30Z",
status_history: [
{
status: "requested",
timestamp: "2026-02-05T10:30:00Z",
customer_id: "cus_123"
},
{
status: "approved",
timestamp: "2026-02-05T11:00:00Z",
admin_id: "admin_456"
},
{
status: "received",
timestamp: "2026-02-10T14:20:00Z",
admin_id: "admin_456"
},
{
status: "refunded",
timestamp: "2026-02-10T15:00:00Z",
admin_id: "admin_456",
refund_amount: 5000 // in cents
}
]
}
}
Visit the Quickstart Guide to set up a server.
Visit the Plugins documentation to learn more about plugins and how to create them.
Visit the Docs to learn more about our system requirements.
Medusa is a set of commerce modules and tools that allow you to build rich, reliable, and performant commerce applications without reinventing core commerce logic. The modules can be customized and used to build advanced ecommerce stores, marketplaces, or any product that needs foundational commerce primitives. All modules are open-source and freely available on npm.
Learn more about Medusaβs architecture and commerce modules in the Docs.
The community and core team are available in GitHub Discussions, where you can ask for support, discuss roadmap, and share ideas.
Join our Discord server to meet other community members.