Search by

devaspid / safi-laravel-client

aldisetiapambudi

Laravel Client SDK for SAFI by devASPid

Package info

github.com/dev-asp-id/safi-laravel-client

pkg:composer/devaspid/safi-laravel-client

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2026-09-24 15:30 UTC

This package is auto-updated.

Last update: 2026-09-24 15:36:20 UTC


README

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.