config = $config; $this->baseUrl = $config['sandbox'] ?? false ? 'https://api-sandbox.oxapay.com/v1' : 'https://api.oxapay.com/v1'; } public function getName(): string { return 'oxapay'; } public function isActive(): bool { return ! empty($this->config['merchant_api_key']); } public function supportsRecurring(): bool { return false; // OxaPay doesn't support recurring payments } public function supportsOneTime(): bool { return true; } public function getSupportedCurrencies(): array { return Cache::remember('oxapay_currencies', now()->addHour(), function () { try { $response = Http::withHeaders([ 'merchant_api_key' => $this->config['merchant_api_key'], ])->get("{$this->baseUrl}/info/currencies"); if ($response->successful()) { $data = $response->json(); return $data['data'] ?? []; } } catch (\Exception $e) { Log::error('Failed to fetch OxaPay currencies', [ 'error' => $e->getMessage(), ]); } return ['BTC', 'ETH', 'USDT', 'USDC', 'LTC', 'BCH']; // Default common cryptos }); } public function calculateFees(float $amount): array { // OxaPay fees vary by currency and network // Using average estimates - actual fees should be fetched from API $percentageFee = 0.5; // 0.5% average $fixedFee = 0.0; // No fixed fee for most cryptos $totalFee = ($amount * $percentageFee / 100) + $fixedFee; return [ 'fixed_fee' => $fixedFee, 'percentage_fee' => $percentageFee, 'total_fee' => $totalFee, 'net_amount' => $amount - $totalFee, ]; } public function createSubscription(User $user, Plan $plan, array $options = []): array { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function cancelSubscription(Subscription $subscription, string $reason = ''): bool { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function updateSubscription(Subscription $subscription, Plan $newPlan): array { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function pauseSubscription(Subscription $subscription): bool { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function resumeSubscription(Subscription $subscription): bool { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function getSubscriptionDetails(string $subscriptionId): array { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function createCheckoutSession(User $user, Plan $plan, array $options = []): array { try { $amount = $options['amount'] ?? $plan->price; $currency = $options['currency'] ?? 'USD'; $toCurrency = $options['to_currency'] ?? 'USDT'; $payload = [ 'amount' => $amount, 'currency' => $currency, 'to_currency' => $toCurrency, 'lifetime' => $options['lifetime'] ?? 60, // 60 minutes default 'fee_paid_by_payer' => $options['fee_paid_by_payer'] ?? 0, 'callback_url' => $this->config['webhook_url'] ?? route('webhooks.oxapay'), 'return_url' => $this->config['success_url'] ?? route('payment.success'), 'email' => $user->email, 'order_id' => $options['order_id'] ?? null, 'description' => $options['description'] ?? "Payment for {$plan->name}", 'sandbox' => $this->config['sandbox'] ?? false, ]; $response = Http::withHeaders([ 'merchant_api_key' => $this->config['merchant_api_key'], 'Content-Type' => 'application/json', ])->post("{$this->baseUrl}/payment/invoice", $payload); if (! $response->successful()) { throw new \Exception('Failed to create OxaPay invoice: '.$response->body()); } $data = $response->json(); return [ 'success' => true, 'checkout_url' => $data['data']['payment_url'] ?? null, 'payment_id' => $data['data']['track_id'] ?? null, 'expires_at' => $data['data']['expired_at'] ?? null, 'amount' => $amount, 'currency' => $currency, 'provider' => 'oxapay', ]; } catch (\Exception $e) { Log::error('OxaPay checkout session creation failed', [ 'error' => $e->getMessage(), 'user_id' => $user->id, 'plan_id' => $plan->id, ]); return [ 'success' => false, 'error' => $e->getMessage(), ]; } } public function createCustomerPortalSession(User $user): array { throw new \Exception('OxaPay does not provide customer portal functionality'); } public function processWebhook(Request $request): array { try { $payload = $request->getContent(); $signature = $request->header('HMAC'); if (! $this->validateWebhook($request)) { throw new \Exception('Invalid webhook signature'); } $data = $request->json()->all(); $status = $data['status'] ?? 'unknown'; $trackId = $data['track_id'] ?? null; $type = $data['type'] ?? 'payment'; return [ 'success' => true, 'event_type' => $status, 'provider_transaction_id' => $trackId, 'processed' => true, 'data' => $data, 'type' => $type, ]; } catch (\Exception $e) { Log::error('OxaPay webhook processing failed', [ 'error' => $e->getMessage(), 'payload' => $request->getContent(), ]); return [ 'success' => false, 'event_type' => 'error', 'provider_transaction_id' => null, 'processed' => false, 'error' => $e->getMessage(), ]; } } public function validateWebhook(Request $request): bool { try { $payload = $request->getContent(); $signature = $request->header('HMAC'); $apiSecret = $this->config['merchant_api_key']; if (empty($signature) || empty($apiSecret)) { return false; } $expectedSignature = hash_hmac('sha512', $payload, $apiSecret); return hash_equals($expectedSignature, $signature); } catch (\Exception $e) { Log::error('OxaPay webhook validation failed', [ 'error' => $e->getMessage(), ]); return false; } } public function getConfiguration(): array { return $this->config; } public function syncSubscriptionStatus(Subscription $subscription): array { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function getPaymentMethodDetails(string $paymentId): array { try { $response = Http::withHeaders([ 'merchant_api_key' => $this->config['merchant_api_key'], ])->get("{$this->baseUrl}/payment/info", [ 'track_id' => $paymentId, ]); if ($response->successful()) { $data = $response->json(); return [ 'success' => true, 'details' => $data['data'] ?? [], ]; } return [ 'success' => false, 'error' => 'Payment not found', ]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage(), ]; } } public function processRefund(string $paymentIntentId, float $amount, string $reason = ''): array { // OxaPay doesn't support traditional refunds in crypto // Would need manual payout process return [ 'success' => false, 'error' => 'OxaPay refunds must be processed manually via payouts', ]; } public function getTransactionHistory(User $user, array $filters = []): array { try { $response = Http::withHeaders([ 'merchant_api_key' => $this->config['merchant_api_key'], ])->get("{$this->baseUrl}/payment/history", array_merge([ 'email' => $user->email, ], $filters)); if ($response->successful()) { $data = $response->json(); return [ 'success' => true, 'transactions' => $data['data'] ?? [], ]; } return [ 'success' => false, 'error' => 'Failed to fetch transaction history', ]; } catch (\Exception $e) { return [ 'success' => false, 'error' => $e->getMessage(), ]; } } public function getSubscriptionMetadata(Subscription $subscription): array { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function updateSubscriptionMetadata(Subscription $subscription, array $metadata): bool { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function startTrial(Subscription $subscription, int $trialDays): bool { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function applyCoupon(Subscription $subscription, string $couponCode): array { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function removeCoupon(Subscription $subscription): bool { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function getUpcomingInvoice(Subscription $subscription): array { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function retryFailedPayment(Subscription $subscription): array { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function canModifySubscription(Subscription $subscription): bool { return false; // No recurring support } public function getCancellationTerms(Subscription $subscription): array { return [ 'immediate_cancellation' => true, 'refund_policy' => 'no_refunds_crypto', 'cancellation_effective' => 'immediately', 'billing_cycle_proration' => false, ]; } public function exportSubscriptionData(Subscription $subscription): array { throw new \Exception('OxaPay does not support recurring subscriptions'); } public function importSubscriptionData(User $user, array $subscriptionData): array { throw new \Exception('OxaPay does not support recurring subscriptions'); } }