# Design

Technical design for the Amaleena rebuild. Schema detail lives in database.md;
stack/theme in tech.md; module layout in structure.md.

## Architecture
Monolithic Laravel app, server-rendered Blade + Livewire storefront, Filament
admin, MySQL. Request lifecycle: route → thin controller/Livewire → Form Request
(validate) → policy (authorize) → Action/Service (business logic, DB transaction)
→ Eloquent → view/redirect. Slow work goes to the `database` queue, drained by
cron on shared hosting.

Layering:
- **Presentation:** Blade/Livewire (storefront), Filament (admin).
- **Application:** Actions (write use-cases), Services (calculations/reads).
- **Domain:** Eloquent models, enums, value objects (Money).
- **Infrastructure:** payment gateway + gold-price provider implementations, queue, mail.

## Key interfaces
```php
interface GoldPriceProvider {                 // App\Services
    public function current(Purity $purity): GoldPrice;   // cached "latest effective"
}
interface PaymentGateway {                     // App\Payments
    public function createPayment(Order $order): PaymentRedirect;
    public function verifyCallback(Request $request): CallbackResult; // signature-checked
}
```
Default gold provider reads the latest `gold_prices` row (manual admin entry); a
future API-backed provider swaps in without touching pricing. Default gateway =
ToyyibPay/Billplz impl (confirm at build).

## Pricing engine
Single source of truth: `PricingService`. Retail price is **computed, not stored**.
```php
// PricingService::priceFor(ProductVariation $v): int  // cents
$perGram = $this->gold->current($v->product->purity)->sell_per_gram;   // cents
$upah = $v->product->workmanship_type === WorkmanshipType::PerGram
    ? (int) round($v->product->workmanship_amount * $v->weight_grams)
    : $v->product->workmanship_amount;
return $v->price_override ?? (int) round($v->weight_grams * $perGram + $upah);
```
- Current gold price is cached; cache is busted when a new `gold_prices` row is added.
- Product price range = min/max over its variations.
- **Snapshot rule:** at order placement, each `order_item` freezes weight, purity,
  `gold_price_per_gram`, `workmanship`, `unit_price`, and `gold_price_id`. Placed
  orders are never recomputed (accounting + policy integrity).

## Enums (backed, string)
`Purity` (P916, P375) · `WorkmanshipType` (PerGram, Flat) · `ProductType` (Simple,
Variable) · `OrderStatus` (PendingPayment, Paid, Processing, Shipped, Delivered,
Cancelled, Refunded) · `PaymentStatus` (Pending, Paid, Failed) · `StoreCreditType`
(RefundVoucher, TradeInCredit, CancellationRefund) · `StoreCreditStatus` (Active,
Used, Expired, Void) · `LedgerType` (Issue, Redeem, Reverse) · `TradeInStatus`
(Submitted, UnderReview, Approved, Rejected, Completed) · `ReferralStatus` (Pending,
Approved, Paid) · `ReviewStatus` (Pending, Approved, Rejected) · `ShippingZone`
(Peninsular, EastMalaysia).

## Critical flows

### Checkout price-lock
1. Cart renders live prices via `PricingService`.
2. "Place order" recomputes server-side; shows final price + weight to confirm (R4.1).
3. On confirm (DB transaction): create order → snapshot each `order_item` → apply
   store credit (locked/decremented) → attach affiliate → compute shipping → set
   `pending_payment` → hand to gateway.

### Payment callback
Gateway → callback route → `verifyCallback` (signature) → find order by reference →
idempotent transition `pending_payment → paid` (skip if already paid) → fire
`OrderPaid` (queued: email, stock decrement, affiliate commission). Raw payload
logged (no secrets).

### Gold price update
Admin adds a `gold_prices` row (sell + buy per gram for a purity) → cache busted →
catalog reprices instantly. Old rows retained for audit.

### Trade-in
Submit request → estimate = Σ(weight × buy_per_gram) → admin review sets final
per-item values → approve issues store credit for the total → customer redeems at
checkout. Each transition audit-logged.

### Store-credit redemption
Validate (owner, active, unexpired, balance>0) → transaction + `lockForUpdate()` on
the credit row → decrement balance → write ledger row → attach to order. Prevents
double-spend under concurrency.

### Affiliate attribution
`?ref=CODE` → signed cookie (e.g. 30 days). At placement, resolve code → block if
buyer == affiliate → store `affiliate_id` on order. On `OrderPaid`, create
`affiliate_referral` (commission = rate × eligible subtotal), status `pending`.

## Security design
- **Price integrity:** never accept client price/weight; recompute; snapshot; overrides admin-only.
- **AuthZ:** policy on every owned resource (orders, credits, trade-ins, affiliate
  dashboard) — guards IDOR. Admin abilities via Filament policies/gates.
- **Vouchers/credit:** atomic row-locked decrement + ledger; random codes.
- **Payments:** signature-verified, idempotent; verify callback amount == order total.
- **Reviews:** verified-purchase check; moderation; output escaped.
- **Uploads:** validate mime+extension+size+dimensions; randomised names; public disk.
- **Rate limiting:** auth, password reset, OTP, checkout, review submission.
- **General:** Form Request validation, CSRF, guarded mass-assignment, secrets in `.env`,
  `APP_DEBUG=false`, secure cookies/sessions, forced HTTPS.
- **Audit log:** sensitive create/update/delete/approve/auth events (actor, action,
  subject, before/after where useful).

## Error handling & logging
- User-facing errors are actionable and in the interface's voice (BM/EN), never leak
  internals. Generic 500 page in production.
- Log with context (order id, action) but never PII/secrets/card data.
- External calls (gateway, optional gold API) wrapped with timeouts, retries (queued),
  and safe fallbacks.

## Localization design
- UI strings in `lang/ms` + `lang/en` via `__()`.
- Translatable model attributes (product/category/CMS) via spatie/laravel-translatable
  (locale-keyed JSON). Default `ms`; `SetLocale` middleware resolves from session.

## Frontend / theme
Purple `#720eec` + warm gold `#c4a35c` on white; Cinzel/Cormorant Garamond/Jost.
Animated SVG hero ring. Tokens in `resources/css/theme.css` → Tailwind. See tech.md.

## Testing strategy
- **Unit:** `PricingService` (per-gram vs flat, override, range), `ShippingCalculator`,
  commission calc.
- **Feature:** checkout price-lock/snapshot, payment callback idempotency, store-credit
  no-double-spend, trade-in approval → credit, affiliate attribution + self-referral
  block, authorization/IDOR, review moderation, locale switching.
