orderBy('name')->get(); } public function settings(User $actor, ?int $tenantId): array { $accounts = PaymentGatewayAccount::with('provider:id,code,name,scope,capabilities,credential_schema') ->when(! $actor->isMasterAdmin(), fn ($q) => $q->where(fn ($x) => $x->where('tenant_id', $tenantId)->orWhere('scope', 'platform'))) ->when($actor->isMasterAdmin() && $tenantId, fn ($q) => $q->where(fn ($x) => $x->where('tenant_id', $tenantId)->orWhere('scope', 'platform'))) ->orderBy('scope')->orderBy('name')->get() ->map(fn ($account) => $this->maskedAccount($account)); $setting = $tenantId ? TenantPaymentSetting::with(['tenantGateway.provider', 'platformGateway.provider'])->firstOrCreate( ['tenant_id' => $tenantId], ['payment_mode' => 'disabled', 'fee_bearer' => 'customer', 'default_expiry_minutes' => 1440] ) : null; return compact('accounts', 'setting'); } public function saveAccount(array $data, User $actor, ?int $tenantId, ?PaymentGatewayAccount $account = null): PaymentGatewayAccount { $scope = $data['scope']; if ($scope === 'platform' && ! $actor->isMasterAdmin()) { abort(403, 'Hanya Master Admin yang dapat mengelola gateway platform.'); } $effectiveTenant = $scope === 'platform' ? null : ($actor->isMasterAdmin() ? ($data['tenant_id'] ?? $tenantId) : $tenantId); if ($scope === 'tenant' && ! $effectiveTenant) { throw ValidationException::withMessages(['tenant_id' => 'Tenant wajib dipilih.']); } if ($account && ($account->scope !== $scope || ($scope === 'tenant' && (int) $account->tenant_id !== (int) $effectiveTenant))) { abort(403); } return DB::transaction(function () use ($data, $account, $scope, $effectiveTenant) { if (($data['enabled'] ?? false) && $scope === 'tenant') { PaymentGatewayAccount::where('tenant_id', $effectiveTenant)->where('scope', 'tenant') ->when($account, fn ($q) => $q->whereKeyNot($account->id))->update(['enabled' => false, 'is_default' => false]); } if (($data['is_default'] ?? false) && $scope === 'platform') { PaymentGatewayAccount::where('scope', 'platform') ->when($account, fn ($q) => $q->whereKeyNot($account->id))->update(['is_default' => false]); } $payload = [ 'uuid' => $account?->uuid ?? Str::uuid(), 'payment_provider_id' => $data['payment_provider_id'], 'tenant_id' => $effectiveTenant, 'scope' => $scope, 'name' => $data['name'], 'environment' => $data['environment'], 'settings' => $data['settings'] ?? [], 'enabled' => $data['enabled'] ?? false, 'is_default' => $data['is_default'] ?? false, ]; if (! empty($data['credentials'])) { $payload['credentials'] = $data['credentials']; } if (filled($data['webhook_secret'] ?? null)) { $payload['webhook_secret'] = $data['webhook_secret']; } $model = $account ?: new PaymentGatewayAccount; $model->fill($payload)->save(); return $model->load('provider'); }); } public function saveTenantSetting(array $data, User $actor, ?int $tenantId): TenantPaymentSetting { $effectiveTenant = $actor->isMasterAdmin() ? ($data['tenant_id'] ?? $tenantId) : $tenantId; if (! $effectiveTenant) { throw ValidationException::withMessages(['tenant_id' => 'Tenant wajib dipilih.']); } if ($data['payment_mode'] === 'tenant_gateway') { $valid = PaymentGatewayAccount::whereKey($data['tenant_gateway_account_id'] ?? 0) ->where('tenant_id', $effectiveTenant)->where('scope', 'tenant')->where('enabled', true)->exists(); if (! $valid) { throw ValidationException::withMessages(['tenant_gateway_account_id' => 'Gateway tenant aktif tidak valid.']); } } if ($data['payment_mode'] === 'platform_gateway') { $valid = PaymentGatewayAccount::whereKey($data['platform_gateway_account_id'] ?? 0) ->whereNull('tenant_id')->where('scope', 'platform')->where('enabled', true)->exists(); if (! $valid) { throw ValidationException::withMessages(['platform_gateway_account_id' => 'Gateway platform aktif tidak valid.']); } } return TenantPaymentSetting::updateOrCreate(['tenant_id' => $effectiveTenant], [ ...$data, 'tenant_id' => $effectiveTenant, 'tenant_gateway_account_id' => $data['payment_mode'] === 'tenant_gateway' ? $data['tenant_gateway_account_id'] : null, 'platform_gateway_account_id' => $data['payment_mode'] === 'platform_gateway' ? $data['platform_gateway_account_id'] : null, ])->load(['tenantGateway.provider', 'platformGateway.provider']); } public function transactions(array $filters, User $actor, ?int $tenantId) { return PaymentTransaction::with(['tenant:id,tenant_name', 'user:id,name', 'customer:id,name,customer_code', 'invoice:id,invoice_number', 'account.provider:id,code,name']) ->when(! $actor->isMasterAdmin(), fn ($q) => $q->where('tenant_id', $tenantId)) ->when($actor->isMasterAdmin() && $tenantId, fn ($q) => $q->where('tenant_id', $tenantId)) ->when($filters['search'] ?? null, fn ($q, $search) => $q->where(fn ($x) => $x->where('transaction_number', 'ilike', "%{$search}%")->orWhere('provider_reference', 'ilike', "%{$search}%"))) ->when($filters['status'] ?? null, fn ($q, $value) => $q->where('status', $value)) ->when($filters['provider_code'] ?? null, fn ($q, $value) => $q->where('provider_code', $value)) ->when($filters['purpose'] ?? null, fn ($q, $value) => $q->where('purpose', $value)) ->when($filters['date_from'] ?? null, fn ($q, $value) => $q->whereDate('created_at', '>=', $value)) ->when($filters['date_to'] ?? null, fn ($q, $value) => $q->whereDate('created_at', '<=', $value)) ->orderBy($filters['sort_by'] ?? 'id', $filters['sort_direction'] ?? 'desc') ->paginate($filters['per_page'] ?? 10); } private function maskedAccount(PaymentGatewayAccount $account): PaymentGatewayAccount { $account->setAttribute('has_credentials', ! empty($account->getRawOriginal('credentials'))); $account->setAttribute('has_webhook_secret', ! empty($account->getRawOriginal('webhook_secret'))); return $account; } }