Looking to hire Laravel developers? Try LaraJobs

safi-laravel-client maintained by devaspid

Description
Laravel Client SDK for SAFI by devASPid
Author
Last update
2026/09/24 17:35 (dev-main)
License
Links
Downloads
2

Comments
comments powered by Disqus

SAFI Laravel Client SDK

Latest Version on Packagist Total Downloads License

Laravel Client SDK untuk terhubung dengan SAFI by devASPid. Package ini mempermudah pengiriman data (agregasi) ke server SAFI.


📌 Fitur Utama

  • ⚡ Dua Mode Integrasi Flexible:
    • Pull Mode (Rekomendasi): Server SAFI yang menarik data transaksi & cabang secara otomatis dari aplikasi Anda.
    • Push Mode: Aplikasi Anda yang mengirim data transaksi (real-time atau background job) ke SAFI Server.
  • 🔄 Auto-Retry & Fault Tolerance: Proteksi otomatis saat koneksi jaringan tidak stabil.
  • 📊 Hourly Aggregator Helper: Mengelompokkan transaksi lokal secara otomatis per jam (0–23) dan per cabang.
  • 🤹 Multi-Platform Support: Siap digunakan untuk Retail POS, E-Commerce, dan Crowdfunding.
  • 🧪 Compatibility: Mendukung PHP 8.0 s/d 8.4+ dan Laravel 9.0 s/d 13.0+.

📦 Persyaratan Sistem

Komponen Versi yang Didukung
PHP Runtime ^8.0 | ^8.1 | ^8.2 | ^8.3 | ^8.4
Laravel Framework ^9.0 | ^10.0 | ^11.0 | ^12.0 | ^13.0

🚀 Instalasi

Jalankan perintah Composer berikut di terminal proyek Laravel Anda:

composer require devaspid/safi-laravel-client

Publish file konfigurasi safi.php ke proyek Anda:

php artisan vendor:publish --tag=safi-config

⚙️ Konfigurasi .env

Tambahkan variabel lingkungan berikut di file .env aplikasi Anda:

# URL Server SAFI Hub Anda
SAFI_BASE_URL=https://safi.domainanda.com

# Secret API Key Tenant yang didapatkan dari Admin Portal SAFI
SAFI_API_KEY=safi_live_xxxxxxxxxxxxxxxx

# Tipe Platform: 'pos', 'online_shop', atau 'crowdfunding'
SAFI_SOURCE_TYPE=pos

# Identitas Default Cabang/Channel Utama
SAFI_DEFAULT_CHANNEL_CODE=MAIN-01
SAFI_DEFAULT_CHANNEL_NAME="Cabang Utama"

# Pengaturan Timeout & Retry (Opsional)
SAFI_TIMEOUT=15
SAFI_RETRY_TIMES=3
SAFI_RETRY_SLEEP_MS=500

💻 Panduan Penggunaan & Integrasi

SAFI mendukung dua mode integrasi. Anda bisa memilih salah satu atau menggabungkan keduanya sesuai kebutuhan.


🟢 MODE 1: PULL PROVIDER (Rekomendasi Utama)

Dalam mode ini, server SAFI Hub yang akan melakukan request GET secara berkala ke aplikasi Anda untuk mengambil data transaksi atau daftar cabang.

1. Endpoint Transaksi Agregat (GET /api/safi/sync)

Daftarkan route di routes/api.php:

use App\Http\Controllers\Api\SafiSyncExportController;
use Illuminate\Support\Facades\Route;

Route::get('/safi/sync', [SafiSyncExportController::class, 'export']);

Buat controller app/Http/Controllers/Api/SafiSyncExportController.php:

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Branch;
use App\Models\Order;
use Devaspid\Safi\Aggregator\HourlyTransactionAggregator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class SafiSyncExportController extends Controller
{
    public function export(Request $request): JsonResponse
    {
        // 1. Verifikasi X-API-KEY dari SAFI Hub
        $apiKey = $request->header('X-API-KEY');
        if ($apiKey !== config('safi.api_key')) {
            return response()->json(['message' => 'Unauthorized: Invalid API Key'], 401);
        }

        $date = $request->query('date', now()->format('Y-m-d'));

        // 2. Query transaksi lokal pada tanggal bersangkutan
        $orders = Order::whereDate('created_at', $date)->get();

        // 3. Kembalikan format JSON sesuai standar SAFI
        return response()->json([
            'status' => 'success',
            'source_type' => config('safi.source_type', 'pos'),
            'date' => $date,
            'channels' => Branch::where('is_active', true)->get()->map(fn($b) => [
                'source_original_id' => $b->id,
                'code' => $b->code,
                'name' => $b->name,
            ])->values()->all(),
            'daily_summary' => [
                [
                    'channel_original_id' => 1,
                    'total_transactions' => $orders->count(),
                    'total_revenue' => (float) $orders->sum('grand_total'),
                    'total_cogs' => (float) $orders->sum('total_hpp'),
                    'total_profit' => (float) $orders->sum('net_profit'),
                    'total_discount' => (float) $orders->sum('discount_amount'),
                    'total_items_sold' => (int) $orders->sum('items_count'),
                    'member_count' => $orders->whereNotNull('customer_id')->count(),
                    'non_member_count' => $orders->whereNull('customer_id')->count(),
                ]
            ],
            'hourly_summary' => HourlyTransactionAggregator::aggregate(
                transactions: $orders,
                targetDate: $date,
                channelOriginalId: 1
            ),
        ]);
    }
}

2. Endpoint Tarik Cabang (GET /api/safi/channels)

Endpoint ini dipanggil saat tombol "Tarik Cabang dari API" ditekan pada Portal Admin SAFI.

Daftarkan route di routes/api.php:

use App\Http\Controllers\Api\SafiBranchExportController;
use Illuminate\Support\Facades\Route;

Route::get('/safi/channels', [SafiBranchExportController::class, 'index']);

Buat controller app/Http/Controllers/Api/SafiBranchExportController.php:

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Branch; // Sesuai entitas cabang/toko/mitra di sistem Anda
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class SafiBranchExportController extends Controller
{
    public function index(Request $request): JsonResponse
    {
        $providedKey = $request->header('X-API-KEY');
        if (empty($providedKey) || $providedKey !== config('safi.api_key')) {
            return response()->json(['success' => false, 'message' => 'Unauthorized'], 401);
        }

        $branches = Branch::where('is_active', true)
            ->get()
            ->map(fn($branch) => [
                'id' => $branch->id,
                'code' => $branch->code ?? 'USR-'.$branch->id,
                'name' => $branch->name,
                'is_active' => (bool) $branch->is_active,
            ]);

        return response()->json([
            'success' => true,
            'channels' => $branches,
        ]);
    }
}

🔵 MODE 2: PUSH CLIENT (Pengiriman Real-time)

Aplikasi Anda mengirimkan data transaksi secara aktif menggunakan Facade Safi::.

A. Pengiriman Langsung dari Controller / Webhook

use Devaspid\Safi\Facades\Safi;

public function checkoutSuccess(Order $order)
{
    Safi::ingestRaw([
        [
            'channel_original_id' => $order->branch_id ?? 1,
            'invoice_no' => $order->invoice_number,
            'transaction_time' => $order->created_at->toIso8601String(),
            'total_net' => (float) $order->grand_total,
            'total_gross' => (float) ($order->grand_total + $order->discount_amount),
            'total_cogs' => (float) $order->total_hpp,
            'total_profit' => (float) $order->net_profit,
            'total_discount' => (float) $order->discount_amount,
            'items_count' => (int) $order->items()->sum('qty'),
            'customer' => [
                'source_customer_id' => $order->customer_id,
                'code' => 'CUST-'.$order->customer_id,
                'name' => $order->customer_name,
                'phone' => $order->customer_phone,
                'email' => $order->customer_email,
            ],
        ]
    ], [
        'source_original_id' => $order->branch_id ?? 1,
        'code' => 'CABANG-01',
        'name' => 'Cabang Utama',
    ]);

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

B. Pengiriman Non-Blocking via Queue Job (Livewire / Background)

use Devaspid\Safi\Jobs\SyncToSafiJob;

public function processPayment()
{
    $trx = $this->saveLocalTransaction();

    // Jalankan pengiriman di background queue
    dispatch(new SyncToSafiJob(
        transactions: [$trx->toSafiArray()],
        channel: [
            'source_original_id' => auth()->user()->branch_id,
            'code' => auth()->user()->branch_code,
            'name' => auth()->user()->branch_name,
        ]
    ));
}

🗺️ Pemetaan Entitas per Model Bisnis

Gunakan tabel ini sebagai panduan saat memetakan kolom database Anda ke format DTO SAFI:

Model Bisnis Cabang / Channel (channels) Produk / Item (items) Transaksi (transactions)
Retail POS Cabang / Outlet Toko Menu / Barang Dagang Struk Belanja Kasir
E-Commerce Toko Online / Marketplace Produk / SKU Order Checkout
Crowdfunding User / Mitra Pengelola Campaign Program Campaign / Infaq Donasi Masuk

🧪 Verifikasi Koneksi

Lakukan pengujian koneksi ke server SAFI Hub dari php artisan tinker:

use Devaspid\Safi\Facades\Safi;

Safi::testConnection(); 
// Menghasilkan true jika API Key dan SAFI_BASE_URL terkonfigurasi dengan benar.

🧪 Running Tests

Untuk menjalankan unit test pada package ini:

composer test

Untuk mengeksekusi tes beserta code coverage:

composer test-coverage

📄 Lisensi

Package ini berlisensi open-source di bawah MIT License.