Skip to content

Complete E-Commerce Store Cookbook ​

This recipe shows a complete, production-ready integration in a standard Laravel e-commerce store with Product variants, Order checkout with torob_clid attribution, and TorobPay gateway handling.


1. Product Model with Variants & Specs ​

In Torob, variants (different colors, sizes, warranties) share the same product_group_id, while having distinct page_unique identifiers and prices.

php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Torob\Laravel\Contracts\TorobProductContract;
use Torob\Laravel\Traits\HasTorobProduct;

class ProductVariant extends Model implements TorobProductContract
{
    use HasTorobProduct;

    protected $guarded = [];

    protected $casts = [
        'specs' => 'array',
        'gallery' => 'array',
        'is_active' => 'boolean',
    ];

    public function product(): BelongsTo
    {
        return $this->belongsTo(Product::class);
    }

    // Unique identifier for this variant: e.g. "105_red_64gb"
    public function getTorobPageUnique(): string
    {
        return (string) $this->sku;
    }

    // URL direct to product with variant pre-selected
    public function getTorobPageUrl(): string
    {
        return url("/product/{$this->product->slug}?variant={$this->id}");
    }

    // Title includes base title + variant attributes
    public function getTorobTitle(): string
    {
        return "{$this->product->title} {$this->color} {$this->storage}";
    }

    // Group ID connects all variants of the same product
    public function getTorobProductGroupId(): ?string
    {
        return (string) $this->product_id;
    }

    // Selling price in Tomans (use 0 if out of stock)
    public function getTorobCurrentPrice(): int
    {
        return $this->stock > 0 ? (int) $this->price_toman : 0;
    }

    public function getTorobAvailability(): bool
    {
        return $this->is_active && $this->stock > 0;
    }

    public function getTorobOldPrice(): ?int
    {
        return $this->compare_at_price_toman ? (int) $this->compare_at_price_toman : null;
    }

    // Image links: main image first
    public function getTorobImageLinks(): array
    {
        $main = $this->image_url ?? $this->product->main_image_url;
        $gallery = $this->gallery ?? [];
        return array_values(array_filter([$main, ...$gallery]));
    }

    public function getTorobSpec(): array
    {
        return array_merge($this->product->specs ?? [], [
            'color' => $this->color,
            'storage' => $this->storage,
            'warranty' => $this->warranty_title,
        ]);
    }

    public function getTorobDateAdded(): string
    {
        return $this->created_at->toIso8601String();
    }
}

Point the configuration to your variant model in .env:

bash
TOROB_PRODUCT_MODEL="App\Models\ProductVariant"

2. Order & Line Item Models ​

Implement TorobOrderContract on your Order model and TorobOrderItemContract on your line item model:

php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Torob\Laravel\Contracts\TorobOrderContract;
use Torob\Laravel\Traits\HasTorobOrder;

class Order extends Model implements TorobOrderContract
{
    use HasTorobOrder;

    protected $guarded = [];

    public function items(): HasMany
    {
        return $this->hasMany(OrderItem::class);
    }

    public function getTorobPurchaseTimestamp(): string
    {
        return $this->created_at->toIso8601ZuluString();
    }

    public function getTorobLastUpdatedTimestamp(): string
    {
        return $this->updated_at->toIso8601ZuluString();
    }

    public function getTorobClid(): string
    {
        return (string) $this->torob_clid;
    }

    public function getTorobStatus(): string
    {
        return in_array($this->status, ['cancelled', 'refunded'], true) ? 'cancelled' : 'completed';
    }

    // Required by Torob: must be 'torobpay' for TorobPay gateway orders
    public function getTorobPsp(): ?string
    {
        return $this->payment_gateway === 'torobpay' ? 'torobpay' : $this->payment_gateway;
    }

    public function getTorobOrderValue(): ?int
    {
        return (int) $this->items_total_toman;
    }

    public function getTorobShippingAmount(): ?int
    {
        return (int) $this->shipping_cost_toman;
    }

    public function getTorobPhoneNumber(): ?string
    {
        return $this->shipping_mobile;
    }

    public function getTorobItems(): ?array
    {
        return $this->items->all();
    }
}
php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Torob\Laravel\Contracts\TorobOrderItemContract;
use Torob\Laravel\Traits\HasTorobOrderItem;

class OrderItem extends Model implements TorobOrderItemContract
{
    use HasTorobOrderItem;

    public function variant(): BelongsTo
    {
        return $this->belongsTo(ProductVariant::class, 'product_variant_id');
    }

    public function getTorobProductUrl(): string
    {
        return $this->variant->getTorobPageUrl();
    }

    public function getTorobProductPrice(): int
    {
        return (int) $this->unit_price_toman;
    }

    public function getTorobQuantity(): int
    {
        return (int) $this->quantity;
    }
}

Register model in .env:

bash
TOROB_ORDER_MODEL="App\Models\Order"

3. Checkout Controller ​

Capture the visitor's torob_clid and attach it to the created order:

php
namespace App\Http\Controllers;

use App\Models\Order;
use Illuminate\Http\Request;
use Torob\Laravel\Facades\Torob;

class CheckoutController extends Controller
{
    public function process(Request $request)
    {
        $validated = $request->validate([
            'address' => 'required|string',
            'mobile' => 'required|string',
            'gateway' => 'required|string',
        ]);

        $order = new Order();
        $order->user_id = auth()->id();
        $order->shipping_mobile = $validated['mobile'];
        $order->payment_gateway = $validated['gateway'];

        // Automatically captures click ID from active request or 7-day cookie:
        $order->torob_clid = Torob::getClid($request);

        $order->save();

        // Attach line items...
        return redirect()->route('payment.redirect', $order);
    }
}

Released under the MIT License.