Developing Returns (RMA) System for E-commerce
RMA (Return Merchandise Authorization) — formalized return and processing workflow. Without system, returns handled manually via email, get lost, delayed, cause dissatisfaction. Takes 5–8 business days.
Business Logic
Fix return rules:
- Return request deadline (14 days per RF law; store can extend)
- Returnable product categories (software, underwear, personalized — not returnable)
- Return reasons (defect, wrong size, wrong item, changed mind)
- Compensation: refund / exchange / store credit
- Shipping requirement or photo enough
Data Schema
CREATE TABLE returns (
id BIGSERIAL PRIMARY KEY,
rma_number VARCHAR(20) UNIQUE NOT NULL,
order_id BIGINT REFERENCES orders(id),
user_id BIGINT REFERENCES users(id),
status VARCHAR(30) DEFAULT 'pending',
reason VARCHAR(50) NOT NULL,
comment TEXT,
resolution VARCHAR(20),
refund_amount NUMERIC(12,2),
created_at TIMESTAMP,
resolved_at TIMESTAMP
);
CREATE TABLE return_items (
id BIGSERIAL PRIMARY KEY,
return_id BIGINT REFERENCES returns(id) ON DELETE CASCADE,
order_item_id BIGINT REFERENCES order_items(id),
quantity INT NOT NULL,
condition VARCHAR(30),
photos JSONB DEFAULT '[]'
);
Return Request Form
Buyer fills form in cabinet. Steps:
- Select order → select items and qty
- Specify reason per item
- Upload photos (if required)
- Choose compensation type
- Confirm and get RMA number
Photo Upload
Photos confirm condition, needed for defect/damage returns. Upload via S3:
public function uploadPhoto(Request $request): JsonResponse {
$request->validate([
'photo' => 'required|image|mimes:jpeg,png,webp|max:5120',
]);
$path = $request->file('photo')->store('returns/photos', 's3');
$url = Storage::disk('s3')->url($path);
return response()->json(['url' => $url]);
}
Max 5 photos, each to 5MB. Preview displays after upload.
Admin Processing
Manager sees queue with filters. Per request actions:
- Approve — send return instructions
- Reject — required comment
- Mark received — start quality check
- Process refund — via payment provider, status resolved
Auto-Approval Rules
Lower support load — auto-approve rules:
public function shouldAutoApprove(Return $return): bool {
return $return->order->total <= 2000
&& $return->reason === 'not_suitable'
&& $return->created_at->diffInDays($return->order->delivered_at) <= 14;
}
Refund Processing
Via payment provider API. For ЮKassa:
$client = new \YooKassa\Client();
$client->setAuth($shopId, $secretKey);
$refund = $client->createRefund([
'payment_id' => $order->payment->yookassa_payment_id,
'amount' => ['value' => $return->refund_amount, 'currency' => 'RUB'],
'description' => "Return RMA #{$return->rma_number}",
]);
Partial return (subset of items) supported — refund_amount calculated with proportional discount.
Return Analytics
Admin report: return % by category, top reasons, avg processing time, total refunded. Identifies systemic issues: if product returns > 15% — check description/photos/quality.







