cacheRemember('system:marketplace', 86400, function () { $settings = app(MarketplaceSettings::class); return [ 'tiktok_shop_platform_commission' => $this->presentFeeRule($settings->tiktok_shop_platform_commission), 'tiktok_shop_logistics_service_fee' => $this->presentFeeRule($settings->tiktok_shop_logistics_service_fee), 'tiktok_shop_dynamic_commission' => $this->presentFeeRule($settings->tiktok_shop_dynamic_commission), 'tiktok_shop_order_processing_fee' => $this->presentFeeRule($settings->tiktok_shop_order_processing_fee), 'tiktok_shop_affiliate' => $this->presentFeeRule($settings->tiktok_shop_affiliate), 'tiktok_shop_pre_order_service_fee' => $this->presentFeeRule($settings->tiktok_shop_pre_order_service_fee), 'shopee_admin_fee' => $this->presentFeeRule($settings->shopee_admin_fee), 'shopee_program_fee' => $this->presentFeeRule($settings->shopee_program_fee), 'shopee_shipping_savings' => $this->presentFeeRule($settings->shopee_shipping_savings), 'shopee_premium' => $this->presentFeeRule($settings->shopee_premium), 'shopee_service_fee' => $this->presentFeeRule($settings->shopee_service_fee), 'shopee_order_processing_fee' => $this->presentFeeRule($settings->shopee_order_processing_fee), 'shopee_ams_commission_fee' => $this->presentFeeRule($settings->shopee_ams_commission_fee), 'shopee_pre_order' => $this->presentFeeRule($settings->shopee_pre_order), 'shopee_live_extra' => $this->presentFeeRule($settings->shopee_live_extra), ]; }); } public function updateMarketplace(array $validated, User $user): void { $this->runInTransaction( function () use ($validated, $user): void { if ($user->can(Permission::OWNER_VERIFICATIONS_VERIFY->value)) { $this->saveSettings($validated); return; } $hasPending = OwnerVerificationRequest::query() ->where('subject_type', MarketplaceSettings::class) ->pending() ->exists(); if ($hasPending) { throw ValidationException::withMessages([ 'system' => 'Perubahan pengaturan marketplace sedang menunggu verifikasi owner.', ]); } $settings = app(MarketplaceSettings::class); $oldPayload = []; $newPayload = []; foreach ($this->tiktokFeeKeys() as $key) { $oldPayload[$key] = $this->presentFeeRule($settings->{$key}); $newPayload[$key] = $this->normalizeFeeRule($validated[$key]); } foreach ($this->shopeeFeeKeys() as $key) { $oldPayload[$key] = $this->presentFeeRule($settings->{$key}); $newPayload[$key] = $this->normalizeFeeRule($validated[$key]); } OwnerVerificationRequest::create([ 'action' => OwnerVerificationAction::UPDATE, 'status' => OwnerVerificationStatus::PENDING, 'subject_type' => MarketplaceSettings::class, 'subject_id' => null, 'submitted_by_id' => $user->id, 'payload' => [ 'old' => $oldPayload, 'new' => $newPayload, ], ]); $this->pushNotificationService->sendToRoles( '⚙️ Pengaturan Marketplace Menunggu Persetujuan Owner', "Pengajuan ubah pengaturan marketplace oleh '{$user->username}' menunggu verifikasi owner.", ['owner', 'developer', 'direktur'], route('admin.system.settings.index'), ); $this->pushNotificationService->sendToUser( '📤 Pengajuan Terkirim', "Pengajuan ubah pengaturan marketplace oleh {$user->profile?->full_name} menunggu verifikasi owner.", $user->id, route('admin.system.settings.index'), ); }, 'Gagal memperbarui pengaturan marketplace', ); } public function saveSettings(array $validated): void { $settings = app(MarketplaceSettings::class); foreach ($this->tiktokFeeKeys() as $key) { $settings->{$key} = $this->normalizeFeeRule($validated[$key]); } foreach ($this->shopeeFeeKeys() as $key) { $settings->{$key} = $this->normalizeFeeRule($validated[$key]); } $settings->save(); $this->cacheForget('system:marketplace'); } public function applyVerificationRequest(OwnerVerificationRequest $request): void { $this->runInTransaction( function () use ($request): void { $newPayload = $request->payload['new'] ?? []; $this->saveSettings($newPayload); $this->cacheForget('system:marketplace'); }, 'Gagal menerapkan pengajuan verifikasi owner', ); } public function buildOrderSnapshot(OrderChannel $channel, int $totalAmount, array $lineItems, bool $isAffiliate = false): ?array { $feeSnapshot = $this->feeRulesForChannel($channel); if ($feeSnapshot === null) { return null; } if (! $isAffiliate) { $feeSnapshot['fees'] = $this->excludeAffiliateFee($feeSnapshot['fees'], $channel); } $calculation = app(MarketplaceFeeCalculator::class)->calculate( $feeSnapshot['fees'], $totalAmount, $lineItems, ); return [ 'platform' => $feeSnapshot['platform'], 'is_affiliate' => $isAffiliate, 'fees' => $feeSnapshot['fees'], 'base_amount' => $totalAmount, 'base_amount_formatted' => 'Rp '.number_format($totalAmount, 0, ',', '.'), 'results' => $calculation['results'], 'total_fee_amount' => $calculation['total_fee_amount'], 'total_fee_amount_formatted' => 'Rp '.number_format($calculation['total_fee_amount'], 0, ',', '.'), 'net_amount' => $calculation['net_amount'], 'net_amount_formatted' => 'Rp '.number_format($calculation['net_amount'], 0, ',', '.'), ]; } private function excludeAffiliateFee(array $fees, OrderChannel $channel): array { $affiliateKey = match ($channel) { OrderChannel::TIKTOK => 'tiktok_shop_affiliate', OrderChannel::SHOPEE => 'ams_commission_fee', default => null, }; if ($affiliateKey !== null && isset($fees[$affiliateKey])) { $fees[$affiliateKey] = [ 'scope' => $fees[$affiliateKey]['scope'], 'value_type' => $fees[$affiliateKey]['value_type'], 'value' => 0, ]; } return $fees; } private function feeRulesForChannel(OrderChannel $channel): ?array { $settings = app(MarketplaceSettings::class); return match ($channel) { OrderChannel::TIKTOK => [ 'platform' => 'tiktok', 'fees' => [ 'platform_commission' => $this->presentFeeRule($settings->tiktok_shop_platform_commission), 'logistics_service_fee' => $this->presentFeeRule($settings->tiktok_shop_logistics_service_fee), 'dynamic_commission' => $this->presentFeeRule($settings->tiktok_shop_dynamic_commission), 'order_processing_fee' => $this->presentFeeRule($settings->tiktok_shop_order_processing_fee), 'affiliate' => $this->presentFeeRule($settings->tiktok_shop_affiliate), 'pre_order_service_fee' => $this->presentFeeRule($settings->tiktok_shop_pre_order_service_fee), ], ], OrderChannel::SHOPEE => [ 'platform' => 'shopee', 'fees' => [ 'admin_fee' => $this->presentFeeRule($settings->shopee_admin_fee), 'program_fee' => $this->presentFeeRule($settings->shopee_program_fee), 'shipping_savings' => $this->presentFeeRule($settings->shopee_shipping_savings), 'premium' => $this->presentFeeRule($settings->shopee_premium), 'service_fee' => $this->presentFeeRule($settings->shopee_service_fee), 'order_processing_fee' => $this->presentFeeRule($settings->shopee_order_processing_fee), 'ams_commission_fee' => $this->presentFeeRule($settings->shopee_ams_commission_fee), 'pre_order' => $this->presentFeeRule($settings->shopee_pre_order), 'live_extra' => $this->presentFeeRule($settings->shopee_live_extra), ], ], OrderChannel::STORE => null, }; } private function presentFeeRule(array $data): array { return MarketplaceFeeRule::fromArray($data)->toArray(); } private function normalizeFeeRule(array $data): array { return MarketplaceFeeRule::fromArray($data)->toArray(); } }