market

0.4.4 2024-01-16 03:12 UTC

README

SierraTecnologia Market integration services market and providers for users required by various SierraTecnologia packages. Validator functionality, and basic controller included out-of-the-box.

Packagist Scrutinizer Code Quality Travis StyleCI License

📚 Índice

🚀 Introdução

O que é o Market?

Market é uma solução modular completa de e-commerce e marketplace desenvolvida pela SierraTecnologia / Rica Soluções, projetada para integração nativa com aplicações Laravel. O pacote oferece uma camada robusta e escalável para gerenciamento de produtos, carrinho de compras, processamento de pagamentos, gestão de pedidos e assinaturas recorrentes.

Objetivo e Contexto de Uso

O Market integra o ecossistema SierraTecnologia, servindo como módulo central para operações comerciais em aplicações corporativas e startups que necessitam de:

  • Catálogo de produtos com variantes e gestão de estoque
  • Carrinho de compras persistente e seguro
  • Checkout completo com múltiplos métodos de pagamento
  • Gestão de pedidos e transações financeiras
  • Assinaturas e planos recorrentes
  • Sistema de cupons e descontos
  • Analytics e relatórios comerciais

Principais Benefícios e Diferenciais

Modularidade: Arquitetura desacoplada seguindo princípios SOLID ✅ Escalabilidade: Suporte a múltiplos marketplaces e multi-tenancy ✅ Integração Nativa: Comunicação fluida com Payment-Laravel, Pedreiro, Media-Manager ✅ Flexibilidade: Sistema de variantes dinâmicas com modificadores de preço/peso ✅ Segurança: Criptografia de IDs sensíveis, validação robusta, proteção CSRF ✅ Analytics Avançado: Dashboard com métricas de vendas, refundos e assinaturas ✅ Multi-canal: APIs REST para integração com mobile e SPAs

Relação com o Ecossistema Rica Soluções

O Market se comunica com outros pacotes da SierraTecnologia através de contratos e interfaces bem definidas:

Market (Commerce Core)
    ├── Payment-Laravel (Gateway de Pagamento)
    ├── Pedreiro (Admin Scaffolding)
    ├── Media-Manager (Gestão de Imagens/Arquivos)
    ├── Crypto (Criptografia e UUID)
    ├── Bancario (Integrações Bancárias)
    ├── Muleta (Utilitários Base)
    └── Locaravel (Localização e URLs)

💿 Instalação

Requisitos Mínimos

  • PHP: ^7.2 | ^8.0 | ^8.1 | ^8.2
  • Laravel: ^7.0 | ^8.0 | ^9.0 | ^10.0
  • Extensões PHP: mbstring, openssl, pdo, tokenizer, json, curl
  • Banco de Dados: MySQL 5.7+ / PostgreSQL 9.6+ / MariaDB 10.2+

Instalação via Composer

composer require sierratecnologia/market

Publicação de Configurações e Assets

# Publicar arquivo de configuração
php artisan vendor:publish --tag=sitec-config

# Publicar views (opcional, para customização)
php artisan vendor:publish --tag=sitec-views

# Publicar traduções
php artisan vendor:publish --tag=sitec-lang

# Publicar assets públicos (CSS/JS)
php artisan vendor:publish --tag=sitec-public

Executar Migrações

php artisan migrate

Isso criará as tabelas:

  • products - Catálogo de produtos
  • product_variants - Variantes (tamanho, cor, etc.)
  • cart - Carrinho de compras
  • orders - Pedidos
  • order_items - Itens dos pedidos
  • transactions - Transações financeiras
  • refunds - Reembolsos
  • plans - Planos de assinatura
  • coupons - Cupons de desconto
  • favorites - Lista de desejos

Registro de Service Providers e Aliases

Registro Automático (Laravel 5.5+)

O MarketProvider é registrado automaticamente via Package Discovery.

Registro Manual (Laravel 5.4 ou inferior)

Adicione ao config/app.php:

'providers' => [
    // ...
    Market\MarketProvider::class,
],

'aliases' => [
    // ...
    'Market' => Market\Facades\Market::class,
    'StoreHelper' => Market\Facades\StoreHelper::class,
],

Configuração Inicial

Edite config/sitec/market.php:

return [
    // Habilitar suporte a múltiplos marketplaces
    'multi-markets' => true,

    // Habilitar sistema de planos/assinaturas
    'have-plans' => true,

    // Habilitar sistema de cupons
    'have-coupons' => true,

    // Prefixo de URL da loja
    'store_url_prefix' => 'commerce',

    // Moeda padrão (ISO 4217)
    'currency' => 'BRL',

    // Incluir frete no cálculo de impostos
    'taxes_include_shipping' => true,
];

Configure suas credenciais de pagamento no .env:

SITECPAYMENT_SECRET=sk_test_xxxxxxxxxxxxx
SITECPAYMENT_PUBLIC=pk_test_xxxxxxxxxxxxx

🏗️ Arquitetura e Estrutura Interna

Organização dos Diretórios e Namespaces

src/
├── Cacheable/              # Utilitários de cache para queries Eloquent
├── Console/                # Comandos Artisan e Kernel customizado
├── Contracts/              # Contratos abstratos e interfaces
├── Exceptions/             # Exceções customizadas do domínio
├── Facades/                # Facades (Market, StoreHelper)
├── Http/
│   ├── Controllers/        # Controladores MVC
│   │   ├── Admin/         # Painel administrativo (CRUD)
│   │   ├── Api/           # Endpoints REST (cart, favorites)
│   │   └── Master/        # Dashboard de analytics
│   ├── Policies/          # Políticas de autorização
│   └── Requests/          # Form Requests (validação)
├── Interfaces/            # Contratos de serviços e repositórios
├── Models/                # Modelos Eloquent (14 models)
├── Observers/             # Observadores de eventos de modelo
├── Repositories/          # Camada de acesso a dados (10 repos)
├── Resources/             # API Resources (serialização JSON)
├── Scopes/                # Query Scopes globais/locais
├── Services/              # Lógica de negócio (19 services)
├── Traits/                # Funcionalidades reutilizáveis
├── Market.php             # Classe principal do pacote
└── MarketProvider.php     # Service Provider Laravel

Padrões Arquiteturais

O Market implementa uma arquitetura em camadas inspirada em Domain-Driven Design (DDD) e Clean Architecture:

1. Camada de Domínio (Domain Layer)

Contém as regras de negócio puras e entidades do domínio:

// Models (Entidades)
Market\Models\Product        # Produtos do catálogo
Market\Models\Order          # Pedidos (Aggregate Root)
Market\Models\OrderItem      # Itens do pedido
Market\Models\Transaction    # Transações financeiras
Market\Models\Cart           # Carrinho de compras
Market\Models\Variant        # Variantes de produtos

// Value Objects
Market\Models\Currency       # Objeto de valor monetário

Exemplo de Aggregate Root (Order):

namespace Market\Models;

class Order extends MarketModel
{
    // Aggregate que coordena OrderItems e Transaction
    public function items()
    {
        return $this->hasMany(OrderItem::class);
    }

    public function transaction()
    {
        return $this->belongsTo(Transaction::class);
    }

    // Regra de negócio: cancelamento cascata
    public function cancel()
    {
        foreach ($this->items as $item) {
            $item->refund();
        }
        $this->transaction->refund();
        $this->update(['status' => 'cancelled']);
    }
}

2. Camada de Aplicação (Application Layer)

Orquestra casos de uso através de Services:

// Serviços (Casos de Uso)
Market\Services\CartService           # Gerenciamento do carrinho
Market\Services\PaymentService        # Processamento de pagamentos
Market\Services\OrderService          # Gestão de pedidos
Market\Services\ProductService        # CRUD de produtos
Market\Services\LogisticService       # Cálculo de frete e impostos
Market\Services\AnalyticsService      # Métricas e relatórios

Exemplo de Service (PaymentService):

namespace Market\Services;

class PaymentService
{
    protected $transactionService;
    protected $orderService;
    protected $logisticService;

    public function __construct(
        TransactionService $transactionService,
        OrderService $orderService,
        LogisticService $logisticService
    ) {
        $this->transactionService = $transactionService;
        $this->orderService = $orderService;
        $this->logisticService = $logisticService;
    }

    public function purchase($user, $cardToken, $cart)
    {
        DB::beginTransaction();

        try {
            // 1. Processar pagamento via gateway
            $charge = $user->meta->charge(
                $cart->total(),
                $cardToken
            );

            // 2. Registrar transação
            $transaction = $this->transactionService->create([
                'user_id' => $user->id,
                'provider' => 'sitecpayment',
                'provider_id' => $charge->id,
                'amount' => $cart->total(),
            ]);

            // 3. Criar pedido
            $order = $this->createOrder($user, $transaction, $cart);

            // 4. Processar logística
            $this->logisticService->afterPurchase($order);

            // 5. Limpar carrinho
            $cart->empty();

            DB::commit();
            return $order;

        } catch (\Exception $e) {
            DB::rollBack();
            throw new PaymentException($e->getMessage());
        }
    }
}

3. Camada de Infraestrutura (Infrastructure Layer)

Implementa persistência e integrações externas:

// Repositórios (Data Access)
Market\Repositories\ProductRepository
Market\Repositories\OrderRepository
Market\Repositories\CartRepository
Market\Repositories\TransactionRepository

// Service Provider (Dependency Injection)
Market\MarketProvider

// Migrations (Schema Management)
database/migrations/

Exemplo de Repository Pattern:

namespace Market\Repositories;

class ProductRepository
{
    protected $model;

    public function __construct(Product $model)
    {
        $this->model = $model;
    }

    public function paginated($perPage = 25)
    {
        return $this->model
            ->where('is_published', true)
            ->where('is_available', true)
            ->orderBy('created_at', 'desc')
            ->paginate($perPage);
    }

    public function findByURL($url)
    {
        return $this->model
            ->where('url', $url)
            ->firstOrFail();
    }
}

Convenções da SierraTecnologia para Modularidade e Escalabilidade

  1. Injeção de Dependência: Todos os services usam constructor injection
  2. Interface Segregation: Contratos específicos em Interfaces/
  3. Single Responsibility: Cada service tem uma responsabilidade única
  4. Repository Pattern: Abstração de acesso a dados
  5. Facades Opcionais: Acesso via DI ou Facades conforme preferência
  6. Event Observers: Callbacks de modelo centralizados
  7. Configuração Centralizada: config/sitec/market.php
  8. Logging Dedicado: Canal sitec-market separado

🎯 Principais Funcionalidades

1. Gestão de Produtos

Estrutura do Modelo Product

namespace Market\Models;

class Product extends MarketModel
{
    protected $fillable = [
        'name',              // Nome do produto
        'url',               // Slug URL-friendly
        'code',              // SKU/Código interno
        'price',             // Preço em centavos (int)
        'weight',            // Peso em gramas (int)
        'width',             // Largura em cm
        'height',            // Altura em cm
        'depth',             // Profundidade em cm
        'discount',          // Desconto em centavos
        'stock',             // Quantidade em estoque
        'is_published',      // Publicado?
        'is_available',      // Disponível?
        'is_featured',       // Produto destaque?
        'hero_image',        // Imagem principal (path)
        'file',              // Arquivo digital (downloads)
        'details',           // JSON: descrição, SEO, etc.
    ];

    // Accessors para conversão automática
    public function getPriceAttribute($value)
    {
        return $value / 100; // centavos → reais
    }

    public function setPriceAttribute($value)
    {
        $this->attributes['price'] = $value * 100; // reais → centavos
    }

    // Relacionamentos
    public function variants()
    {
        return $this->hasMany(Variant::class);
    }

    public function images()
    {
        return $this->hasMany(Image::class, 'entity_id')
                    ->where('entity_type', self::class);
    }
}

Criando Produtos via Service

use Market\Services\ProductService;

$productService = app(ProductService::class);

$product = $productService->create([
    'name' => 'Notebook Dell XPS 15',
    'url' => 'notebook-dell-xps-15',
    'code' => 'DELL-XPS15-2024',
    'price' => 8999.90,  // Será armazenado como 899990 centavos
    'weight' => 1800,    // 1.8kg em gramas
    'stock' => 50,
    'is_published' => true,
    'is_available' => true,
    'details' => [
        'description' => 'Notebook de alta performance...',
        'specs' => [
            'processor' => 'Intel Core i7',
            'ram' => '16GB DDR4',
            'storage' => 'SSD 512GB NVMe'
        ],
        'seo' => [
            'meta_description' => 'Compre o melhor notebook...',
            'keywords' => 'notebook, dell, xps'
        ]
    ]
]);

2. Sistema de Variantes Avançado

O Market suporta variantes com modificadores de preço e peso:

// Criando variantes
$variant1 = $product->variants()->create([
    'key' => 'cor',
    'value' => 'Prata(+$0)[+0g]'  // Sem modificadores
]);

$variant2 = $product->variants()->create([
    'key' => 'cor',
    'value' => 'Dourado(+$299.90)[+50g]'  // +R$299,90 e +50g
]);

$variant3 = $product->variants()->create([
    'key' => 'memoria',
    'value' => '32GB(+$899.00)[+0g]'  // Upgrade de RAM
]);

Sintaxe de Modificadores:

  • (+$valor) ou (-$valor): Ajuste de preço
  • [+peso] ou [-peso]: Ajuste de peso em gramas
  • Múltiplas variantes: "Cor|Tamanho|Material"

Cálculo Automático no Carrinho:

// Adicionar ao carrinho com variantes
$cart->add([
    'product_id' => $product->id,
    'quantity' => 1,
    'variants' => 'Dourado|32GB',  // +R$1.198,90
]);

// Preço calculado:
// Base: R$8.999,90
// + Cor Dourado: R$299,90
// + 32GB RAM: R$899,00
// = R$10.198,80

3. Carrinho de Compras

Dual Storage: Sessão (Guest) + Database (User)

use Market\Services\CartService;

$cartService = app(CartService::class);

// Adicionar item ao carrinho
$cartService->addToCart([
    'product_id' => 1,
    'quantity' => 2,
    'variants' => 'Vermelho|Grande',
]);

// Obter conteúdo do carrinho
$items = $cartService->contents();

// Totais
$subtotal = $cartService->getCartSubTotal();  // Soma dos produtos
$tax = $cartService->getCartTax();            // Impostos
$shipping = $cartService->getCartShipping();  // Frete
$total = $cartService->getCartTotal();        // Total final

// Aplicar cupom
$cartService->addCoupon('DESCONTO10');

// Remover item
$cartService->removeFromCart($itemId);

// Esvaziar carrinho
$cartService->emptyCart();

Sincronização Automática (Guest → User)

Quando um usuário não autenticado faz login, o carrinho da sessão é automaticamente mesclado com o carrinho do banco de dados:

// Em CartRepository@syncronize()
public function syncronize($userId)
{
    $sessionCart = session()->get('cart', []);

    foreach ($sessionCart as $item) {
        $this->model->create([
            'user_id' => $userId,
            'product_id' => $item['product_id'],
            'quantity' => $item['quantity'],
            'variants' => $item['variants'],
        ]);
    }

    session()->forget('cart');
}

4. Checkout e Processamento de Pagamentos

Fluxo Completo de Checkout

// 1. Página de Confirmação (/checkout)
// Usuário revisa itens, endereço, frete

// 2. Página de Pagamento (/payment)
// Usuário insere dados do cartão

// 3. Processar Pagamento (POST /process)
use Market\Services\PaymentService;

$paymentService = app(PaymentService::class);

$order = $paymentService->purchase(
    auth()->user(),
    $request->input('card_token'),  // Token do gateway
    $cartService->getCart()
);

// 4. Página de Sucesso (/complete)
// Exibir número do pedido e detalhes

Integração com Payment-Laravel (SierraTecnologia Gateway)

// O usuário precisa ter um customer_id no gateway
if (!auth()->user()->meta->hasCustomer()) {
    auth()->user()->meta->createAsSierraTecnologiaCustomer(
        $request->input('card_token')
    );
}

// Cobrar do cartão
$charge = auth()->user()->meta->charge(
    $cart->total(),
    $request->input('card_token')
);

// Cobrar do último cartão salvo
$charge = auth()->user()->meta->chargeWithLastCard($amount);

5. Gestão de Pedidos e Transações

Estrutura de Pedido (Order)

namespace Market\Models;

class Order extends MarketModel
{
    protected $fillable = [
        'uuid',                  // UUID único do pedido
        'user_id',              // Cliente
        'transaction_id',       // Transação associada
        'shipping_address',     // JSON: endereço de entrega
        'billing_address',      // JSON: endereço de cobrança
        'is_shipped',           // Enviado?
        'tracking_number',      // Código de rastreio
        'status',               // pending|complete|cancelled
        'notes',                // Observações
        'details',              // JSON: metadados adicionais
    ];

    protected $casts = [
        'shipping_address' => 'array',
        'billing_address' => 'array',
        'details' => 'array',
    ];
}

Criando e Gerenciando Pedidos

use Market\Services\OrderService;

$orderService = app(OrderService::class);

// Criar pedido
$order = $orderService->create([
    'user_id' => $user->id,
    'transaction_id' => $transaction->id,
    'shipping_address' => [
        'street' => 'Rua das Flores, 123',
        'city' => 'Rio de Janeiro',
        'state' => 'RJ',
        'zipcode' => '20000-000',
    ],
    'items' => [
        ['product_id' => 1, 'quantity' => 2, 'variants' => 'Azul|M'],
        ['product_id' => 5, 'quantity' => 1, 'variants' => null],
    ],
]);

// Buscar pedidos do cliente
$orders = $orderService->getByCustomer($user->id);

// Cancelar pedido (refund automático)
$orderService->cancel($order->id);

// Atualizar status de envio
$orderService->update($order->id, [
    'is_shipped' => true,
    'tracking_number' => 'BR123456789XY',
]);

6. Sistema de Assinaturas (Plans)

Criando Planos

use Market\Services\PlanService;

$planService = app(PlanService::class);

$plan = $planService->create([
    'name' => 'Premium Mensal',
    'amount' => 49.90,           // R$49,90/mês
    'interval' => 'month',       // week|month|year
    'currency' => 'BRL',
    'trial_days' => 7,           // 7 dias grátis
    'is_featured' => true,
    'enabled' => true,
    'descriptors' => [
        'features' => [
            'Acesso ilimitado',
            'Suporte prioritário',
            'Downloads sem limite'
        ]
    ]
]);

Assinando um Plano

use Market\Services\SubscriptionService;

$subscriptionService = app(SubscriptionService::class);

// Criar assinatura
$subscription = auth()->user()->meta->newSubscription(
    'Premium Mensal',
    $plan->sitecpayment_id
)->create($cardToken);

// Cancelar assinatura
auth()->user()->meta->subscription('Premium Mensal')->cancel();

// Verificar se está assinado
if (auth()->user()->meta->subscribed('Premium Mensal')) {
    // Usuário tem acesso premium
}

7. Cupons e Descontos

use Market\Services\CouponService;

$couponService = app(CouponService::class);

// Criar cupom de 10% de desconto
$coupon = $couponService->create([
    'code' => 'DESCONTO10',
    'discount_type' => 'percentage',  // ou 'dollar'
    'amount' => 10,                   // 10%
    'limit' => 100,                   // Máximo 100 usos
    'start_date' => now(),
    'end_date' => now()->addDays(30),
    'for_subscriptions' => false,
]);

// Criar cupom de R$50 off
$coupon = $couponService->create([
    'code' => '50REAIS',
    'discount_type' => 'dollar',
    'amount' => 50.00,
    'limit' => 50,
]);

// Aplicar cupom no checkout
$cartService->addCoupon('DESCONTO10');

8. Analytics e Relatórios

use Market\Services\AnalyticsService;

$analyticsService = app(AnalyticsService::class);

// Obter transações dos últimos 30 dias
$transactions = $analyticsService->getTransactionsByDays(30);

// Calcular balanço (receita - reembolsos)
$balance = $analyticsService->balanceValues($transactions);

// Receita mensal
$monthlyRevenue = $analyticsService->overMonths(12);

// Exibir no dashboard
return view('market::master.analytics', [
    'revenue' => $balance['income'],
    'refunds' => $balance['refunds'],
    'profit' => $balance['income'] - $balance['refunds'],
    'chart_data' => $monthlyRevenue,
]);

🔧 Uso Prático

Integração em Aplicação Laravel Existente

1. Adicionar Rotas Personalizadas

// routes/web.php

use Market\Http\Controllers\ProductController;
use Market\Http\Controllers\CartController;
use Market\Http\Controllers\CheckoutController;

// Vitrine pública
Route::get('/loja', [ProductController::class, 'index']);
Route::get('/loja/produto/{url}', [ProductController::class, 'show']);

// Carrinho
Route::middleware('auth')->group(function () {
    Route::get('/carrinho', [CartController::class, 'index']);
    Route::post('/carrinho/adicionar', [CartController::class, 'add']);

    // Checkout
    Route::get('/finalizar', [CheckoutController::class, 'confirm']);
    Route::post('/processar-pagamento', [CheckoutController::class, 'process']);
});

2. Customizar Views

// Publicar views
php artisan vendor:publish --tag=sitec-views

// Customizar: resources/views/vendor/market/products/show.blade.php
@extends('layouts.app')

@section('content')
<div class="product-detail">
    <h1>{{ $product->name }}</h1>
    <p class="price">R$ {{ number_format($product->price, 2, ',', '.') }}</p>

    @if($product->variants->count())
        <div class="variants">
            @foreach($product->variants as $variant)
                <label>
                    <input type="radio" name="variant" value="{{ $variant->value }}">
                    {{ $variant->value }}
                </label>
            @endforeach
        </div>
    @endif

    <button onclick="addToCart({{ $product->id }})">
        Adicionar ao Carrinho
    </button>
</div>
@endsection

3. Criar Middleware Personalizado

// app/Http/Middleware/CheckProductAvailability.php

namespace App\Http\Middleware;

use Closure;
use Market\Models\Product;

class CheckProductAvailability
{
    public function handle($request, Closure $next)
    {
        $product = Product::find($request->route('id'));

        if (!$product || !$product->is_available) {
            return redirect('/loja')->with('error', 'Produto indisponível');
        }

        return $next($request);
    }
}

Exemplo: Integração com Gateway de Pagamento

// app/Services/CustomPaymentService.php

namespace App\Services;

use Market\Services\PaymentService as BasePaymentService;
use Market\Services\OrderService;
use Market\Services\TransactionService;

class CustomPaymentService extends BasePaymentService
{
    public function processWithPix($user, $cart)
    {
        // 1. Gerar QR Code PIX
        $pixData = $this->pixGateway->generateQRCode([
            'amount' => $cart->total(),
            'description' => 'Pedido #' . uniqid(),
        ]);

        // 2. Criar transação pendente
        $transaction = $this->transactionService->create([
            'user_id' => $user->id,
            'provider' => 'pix',
            'provider_id' => $pixData['id'],
            'state' => 'pending',
            'amount' => $cart->total(),
        ]);

        // 3. Criar pedido aguardando pagamento
        $order = $this->createOrder($user, $transaction, $cart);

        return [
            'order' => $order,
            'pix_qrcode' => $pixData['qrcode'],
            'pix_string' => $pixData['emv'],
        ];
    }
}

Exemplo: Webhook para Confirmação de Pagamento

// routes/api.php
Route::post('/webhook/payment-confirmation', [WebhookController::class, 'handle']);

// app/Http/Controllers/WebhookController.php
namespace App\Http\Controllers;

use Market\Services\OrderService;
use Market\Services\TransactionService;
use Market\Services\LogisticService;

class WebhookController extends Controller
{
    public function handle(Request $request)
    {
        $signature = $request->header('X-Webhook-Signature');

        // Validar assinatura
        if (!$this->validateSignature($signature, $request->getContent())) {
            return response()->json(['error' => 'Invalid signature'], 401);
        }

        $data = $request->json()->all();

        if ($data['event'] === 'payment.approved') {
            $transaction = app(TransactionService::class)
                ->findByProviderId($data['payment_id']);

            // Atualizar transação
            $transaction->update(['state' => 'approved']);

            // Atualizar pedido
            $order = $transaction->order;
            $order->update(['status' => 'complete']);

            // Processar logística
            app(LogisticService::class)->afterPurchase($order);

            return response()->json(['status' => 'processed']);
        }

        return response()->json(['status' => 'ignored']);
    }
}

🔗 Integração com o Ecossistema SierraTecnologia

Comunicação entre Pacotes

1. Payment-Laravel (Gateway de Pagamento)

// O Market depende do Payment-Laravel para processar pagamentos

// Criar customer no gateway
$user->meta->createAsSierraTecnologiaCustomer($cardToken);

// Cobrar valor
$charge = $user->meta->charge($amount, $cardToken);

// Cobrar com último cartão salvo
$charge = $user->meta->chargeWithLastCard($amount);

// Reembolsar
$refund = $user->meta->refund($chargeId, $amount);

// Criar assinatura
$subscription = $user->meta->newSubscription('plan-name', $planId)
    ->trialDays(7)
    ->create($cardToken);

2. Media-Manager (Gestão de Imagens)

use SierraTecnologia\MediaManager\Models\Image;

// Associar imagens a produtos
$product->images()->create([
    'path' => '/storage/products/notebook-1.jpg',
    'alt' => 'Notebook Dell XPS 15',
    'title' => 'Imagem principal',
]);

// Recuperar imagens
$images = $product->images;

// Hero image (imagem principal)
$product->update([
    'hero_image' => '/storage/products/hero.jpg'
]);

3. Pedreiro (Admin Scaffolding)

O Market registra automaticamente itens de menu no painel admin:

// Em MarketProvider@boot()
$menuItens = [
    'Commerce' => [
        'Produtos' => [
            'icon' => 'pe-7s-cart',
            'route' => 'admin.market.products.index',
        ],
        'Pedidos' => [
            'icon' => 'pe-7s-note2',
            'route' => 'master.market.orders.index',
        ],
        'Transações' => [
            'icon' => 'pe-7s-cash',
            'route' => 'master.market.transactions.index',
        ],
    ],
];

event(new \Pedreiro\Events\MenuRegistered($menuItens));

4. Crypto (Criptografia e UUID)

use function SierraTecnologia\Crypto\crypto_encrypt;
use function SierraTecnologia\Crypto\crypto_decrypt;

// Gerar UUID para transações
$transaction->uuid = \Ramsey\Uuid\Uuid::uuid4()->toString();

// Criptografar IDs sensíveis em URLs
$encryptedId = crypto_encrypt($order->id);
$url = route('order.show', $encryptedId);

// Descriptografar
$orderId = crypto_decrypt($request->route('id'));

5. Locaravel (Localização)

use function SierraTecnologia\Locaravel\RiCaService;

// Converter nome de produto para URL slug
$product->url = RiCaService::convertToURL($product->name);
// "Notebook Dell XPS 15" → "notebook-dell-xps-15"

Padrões de Versionamento, Testes e CI/CD

Versionamento Semântico

O Market segue Semantic Versioning 2.0:

  • MAJOR (0.x.x → 1.0.0): Breaking changes
  • MINOR (0.4.x → 0.5.0): Novas funcionalidades (backward-compatible)
  • PATCH (0.4.4 → 0.4.5): Bug fixes

Estratégia de Branches (Git Flow)

main (production)
  └── develop (integration)
       ├── feature/cart-improvements
       ├── feature/pix-integration
       └── bugfix/checkout-validation

Aplicação em Ambientes de Microserviços

O Market pode ser usado em arquitetura de microserviços:

┌─────────────────────────────────────────┐
│   API Gateway (Laravel + Market)        │
│   - Endpoints REST                      │
│   - Autenticação JWT                    │
└─────────────────┬───────────────────────┘
                  │
      ┌───────────┴───────────┐
      │                       │
┌─────▼──────┐       ┌────────▼────────┐
│  Product   │       │   Order Service │
│  Service   │       │   (Market Core) │
│  (Market)  │       └─────────────────┘
└────────────┘

Exemplo de API REST:

// routes/api.php
Route::prefix('v1')->middleware('auth:sanctum')->group(function () {

    // Produtos
    Route::get('/products', [Api\ProductController::class, 'index']);
    Route::get('/products/{id}', [Api\ProductController::class, 'show']);

    // Carrinho
    Route::get('/cart', [Api\CartController::class, 'index']);
    Route::post('/cart', [Api\CartController::class, 'add']);
    Route::delete('/cart/{id}', [Api\CartController::class, 'remove']);

    // Pedidos
    Route::get('/orders', [Api\OrderController::class, 'index']);
    Route::get('/orders/{uuid}', [Api\OrderController::class, 'show']);
    Route::post('/orders', [Api\OrderController::class, 'create']);
});

🎨 Extensão e Customização

Criar Novos Módulos (Ex: Promoções)

1. Criar Model

// app/Models/Promotion.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Market\Models\Product;

class Promotion extends Model
{
    protected $fillable = [
        'name',
        'discount_percentage',
        'start_date',
        'end_date',
        'active',
    ];

    protected $casts = [
        'start_date' => 'datetime',
        'end_date' => 'datetime',
    ];

    public function products()
    {
        return $this->belongsToMany(Product::class);
    }

    public function isActive()
    {
        return $this->active
            && now()->between($this->start_date, $this->end_date);
    }
}

2. Criar Migration

// database/migrations/2024_xx_xx_create_promotions_table.php
public function up()
{
    Schema::create('promotions', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->integer('discount_percentage');
        $table->timestamp('start_date');
        $table->timestamp('end_date');
        $table->boolean('active')->default(true);
        $table->timestamps();
    });

    Schema::create('product_promotion', function (Blueprint $table) {
        $table->foreignId('product_id')->constrained()->onDelete('cascade');
        $table->foreignId('promotion_id')->constrained()->onDelete('cascade');
        $table->primary(['product_id', 'promotion_id']);
    });
}

3. Estender ProductService

// app/Services/PromotionService.php
namespace App\Services;

use Market\Services\ProductService;
use App\Models\Promotion;

class PromotionService
{
    protected $productService;

    public function __construct(ProductService $productService)
    {
        $this->productService = $productService;
    }

    public function applyPromotion(Promotion $promotion, $products)
    {
        foreach ($products as $product) {
            $promotion->products()->attach($product->id);
        }
    }

    public function getPromotionalPrice($product)
    {
        $promotion = $product->promotions()
            ->where('active', true)
            ->where('start_date', '<=', now())
            ->where('end_date', '>=', now())
            ->first();

        if ($promotion) {
            return $product->price * (1 - $promotion->discount_percentage / 100);
        }

        return $product->price;
    }
}

Substituir Classes Padrão via Injeção de Dependência

// app/Providers/AppServiceProvider.php

use Market\Services\PaymentService;
use App\Services\CustomPaymentService;

public function register()
{
    // Substituir PaymentService por implementação customizada
    $this->app->bind(PaymentService::class, CustomPaymentService::class);
}

Boas Práticas para Evitar Breaking Changes

  1. Nunca modifique arquivos do vendor/: Use inheritance ou DI
  2. Publique e customize views: vendor:publish --tag=sitec-views
  3. Use eventos e listeners: Para adicionar lógica sem alterar código core
  4. Crie observers customizados: Para estender comportamento de models
// app/Observers/ProductObserver.php
namespace App\Observers;

use Market\Models\Product;

class ProductObserver
{
    public function created(Product $product)
    {
        // Enviar email para admin
        \Mail::to('admin@loja.com')->send(new ProductCreatedMail($product));

        // Invalidar cache
        \Cache::forget('featured-products');
    }
}

// Registrar em AppServiceProvider
use Market\Models\Product;
use App\Observers\ProductObserver;

public function boot()
{
    Product::observe(ProductObserver::class);
}

💼 Exemplos Reais

Caso de Uso: Marketplace Multi-Vendor

// 1. Adicionar campo vendor_id aos produtos
Schema::table('products', function (Blueprint $table) {
    $table->foreignId('vendor_id')->nullable()->constrained('users');
});

// 2. Criar filtro por vendor
$vendorProducts = app(ProductService::class)->paginated([
    'vendor_id' => $vendorId
]);

// 3. Comissão automática no checkout
public function afterPurchase($order)
{
    foreach ($order->items as $item) {
        $product = $item->product;
        $vendor = $product->vendor;

        $commission = $item->total * 0.85; // 85% para o vendor

        VendorPayout::create([
            'vendor_id' => $vendor->id,
            'order_item_id' => $item->id,
            'amount' => $commission,
        ]);
    }
}

Caso de Uso: Produtos Digitais (Downloads)

// 1. Criar rota de download protegida
Route::get('/download/{order}/{item}', [DownloadController::class, 'download'])
    ->middleware('auth');

// 2. Controller de download
public function download(Order $order, OrderItem $item)
{
    // Verificar se o pedido pertence ao usuário
    if ($order->user_id !== auth()->id()) {
        abort(403);
    }

    // Verificar se o item tem arquivo digital
    if (!$item->product->file) {
        abort(404, 'Arquivo não encontrado');
    }

    // Registrar download
    DownloadLog::create([
        'user_id' => auth()->id(),
        'product_id' => $item->product_id,
        'ip' => request()->ip(),
    ]);

    // Forçar download
    return Storage::download($item->product->file);
}

Caso de Uso: Assinatura com Conteúdo Exclusivo

// Middleware para verificar assinatura
public function handle($request, Closure $next)
{
    if (!auth()->user()->meta->subscribed('Premium')) {
        return redirect('/planos')->with('error', 'Assine o plano Premium para acessar');
    }

    return $next($request);
}

// Usar em rotas
Route::middleware(['auth', 'subscription:Premium'])->group(function () {
    Route::get('/cursos-exclusivos', [CourseController::class, 'index']);
});

Demonstração: Antes e Depois

ANTES (sem Market):

// Código espaguete misturado no controller
public function checkout(Request $request)
{
    $cart = Session::get('cart');
    $total = 0;
    foreach ($cart as $item) {
        $total += $item['price'] * $item['qty'];
    }

    $charge = \Stripe\Charge::create([
        'amount' => $total * 100,
        'currency' => 'brl',
        'source' => $request->token,
    ]);

    DB::table('orders')->insert([
        'user_id' => auth()->id(),
        'total' => $total,
        'created_at' => now(),
    ]);

    Session::forget('cart');

    return redirect('/success');
}

DEPOIS (com Market):

// Código limpo, testável e reutilizável
public function checkout(Request $request)
{
    $order = app(PaymentService::class)->purchase(
        auth()->user(),
        $request->input('card_token'),
        app(CartService::class)->getCart()
    );

    return redirect()->route('order.complete', $order->uuid);
}

Benefícios:

  • ✅ 90% menos código
  • ✅ Transações atômicas (rollback automático)
  • ✅ Validação centralizada
  • ✅ Logs automáticos
  • ✅ Facilidade de teste unitário
  • ✅ Reutilizável em múltiplos projetos

🧪 Testes e Qualidade

Executar Testes Localmente

# Testes unitários e de integração
composer test
# ou
vendor/bin/phpunit

# Testes com cobertura
composer test-coverage

Análise Estática

# PHPStan (nível 8)
vendor/bin/phpstan analyse src/

# Psalm
composer psalm
# ou
vendor/bin/psalm

Verificação de Código (Code Style)

# PHPCS (PSR-12)
vendor/bin/phpcs --standard=PSR12 src/

# PHP CS Fixer (correção automática)
composer format
# ou
vendor/bin/php-cs-fixer fix --allow-risky=yes

PHPMD (Mess Detector)

vendor/bin/phpmd src/ text cleancode,codesize,controversial,design,naming,unusedcode

🤝 Guia de Contribuição

Como Contribuir

  1. Fork o repositório
  2. Crie uma branch para sua feature (git checkout -b feature/nova-funcionalidade)
  3. Commit suas mudanças (git commit -m 'feat: adiciona nova funcionalidade')
  4. Push para a branch (git push origin feature/nova-funcionalidade)
  5. Abra um Pull Request

Padrões de Commits

Seguimos o Conventional Commits:

feat: adiciona suporte a pagamento via PIX
fix: corrige cálculo de frete para produtos pesados
docs: atualiza README com exemplos de uso
style: formata código conforme PSR-12
refactor: reorganiza services em subdiretórios
test: adiciona testes para ProductService
chore: atualiza dependências do composer

Padrões de Branches

  • main: Branch de produção (somente merges via PR)
  • develop: Branch de desenvolvimento (integração)
  • feature/nome-da-feature: Novas funcionalidades
  • bugfix/nome-do-bug: Correções de bugs
  • hotfix/nome-do-hotfix: Correções urgentes em produção

Política de Licença e Contato

📝 Changelog

Consulte o CHANGELOG.md para histórico completo de versões.

💬 Suporte

Os seguintes canais de suporte estão disponíveis:

🔐 Vulnerabilidades de Segurança

Se você descobrir uma vulnerabilidade de segurança neste projeto, envie um e-mail para help@sierratecnologia.com.br. Todas as vulnerabilidades serão prontamente tratadas.

About SierraTecnologia

SierraTecnologia is a software solutions startup, specialized in integrated enterprise solutions for SMEs established in Rio de Janeiro, Brazil since June 2008. We believe that our drive The Value, The Reach, and The Impact is what differentiates us and unleash the endless possibilities of our philosophy through the power of software. We like to call it Innovation At The Speed Of Life. That’s how we do our share of advancing humanity.

License

This software is released under The MIT License (MIT).

(c) 2008-2020 SierraTecnologia, Some rights reserved.