--- File: D:\projects\digitalvocano\Modules\AuthorizeNetGateway\config\config.php --- 'AuthorizeNetGateway', ]; --- File: D:\projects\digitalvocano\Modules\AuthorizeNetGateway\database\seeders\AuthorizeNetGatewayDatabaseSeeder.php --- call([]); } } --- File: D:\projects\digitalvocano\Modules\AuthorizeNetGateway\Http\Controllers\Admin\AuthorizeNetConfigController.php --- validate([ 'authorizenet_enabled' => 'nullable|boolean', // Checkbox might not be present if unchecked 'authorizenet_mode' => 'required|in:sandbox,live', 'authorizenet_login_id' => 'nullable|string|max:255', 'authorizenet_transaction_key' => 'nullable|string|max:255', 'authorizenet_signature_key' => 'nullable|string|max:255', 'authorizenet_public_client_key' => 'nullable|string|max:255', ]); try { Setting::setValue('authorizenet_enabled', $request->input('authorizenet_enabled', '0'), 'Enable Authorize.Net Gateway', 'Payment Gateways', 'boolean'); Setting::setValue('authorizenet_mode', $request->input('authorizenet_mode', 'sandbox'), 'Authorize.Net Mode', 'Payment Gateways', 'select'); Setting::setValue('authorizenet_login_id', $request->input('authorizenet_login_id'), 'Authorize.Net API Login ID', 'Payment Gateways', 'text'); Setting::setValue('authorizenet_transaction_key', $request->input('authorizenet_transaction_key'), 'Authorize.Net Transaction Key', 'Payment Gateways', 'password'); Setting::setValue('authorizenet_signature_key', $request->input('authorizenet_signature_key'), 'Authorize.Net Signature Key (Webhooks)', 'Payment Gateways', 'password'); Setting::setValue('authorizenet_public_client_key', $request->input('authorizenet_public_client_key'), 'Authorize.Net Public Client Key', 'Payment Gateways', 'text'); return redirect()->route('admin.authorizenetgateway.settings.edit')->with('success', 'Authorize.Net settings updated successfully.'); } catch (\Exception $e) { Log::error('Error updating Authorize.Net settings: ' . $e->getMessage()); return redirect()->route('admin.authorizenetgateway.settings.edit')->with('error', 'Failed to update Authorize.Net settings. Please try again.'); } } } --- File: D:\projects\digitalvocano\Modules\AuthorizeNetGateway\Http\Controllers\AuthorizeNetGatewayController.php --- authorizeNetService = $authorizeNetService; $this->creditService = $creditService; $this->walletService = $walletService; } public function showCheckoutForm(Request $request, SubscriptionPlan $subscriptionPlan) { if (setting('authorizenet_enabled', '0') != '1') { return redirect()->route('subscription.plans')->with('error', 'Authorize.Net payments are currently disabled.'); } $apiLoginId = setting('authorizenet_login_id'); $publicClientKey = setting('authorizenet_public_client_key'); if (!$apiLoginId || !$publicClientKey) { Log::error("Authorize.Net checkout form: API Login ID or Public Client Key is not set."); return redirect()->route('subscription.plans')->with('error', 'Authorize.Net gateway is not configured correctly.'); } return view('authorizenetgateway::subscriptions.checkout', ['plan' => $subscriptionPlan, 'apiLoginId' => $apiLoginId, 'publicClientKey' => $publicClientKey]); } public function processSubscriptionWithAcceptJs(Request $request, SubscriptionPlan $plan) { if (setting('authorizenet_enabled', '0') != '1') { return redirect()->route('subscription.plans')->with('error', 'Authorize.Net payments are currently disabled.'); } $request->validate([ 'authorizenet_data_descriptor' => 'required|string', 'authorizenet_data_value' => 'required|string', // Payment nonce ]); /** @var User $user */ $user = Auth::user(); try { $opaqueData = [ 'dataDescriptor' => $request->input('authorizenet_data_descriptor'), 'dataValue' => $request->input('authorizenet_data_value'), ]; $customerDetails = ['email' => $user->email, 'firstName' => $user->name]; // Or more detailed if available $invoiceNumber = 'SUB-' . Str::random(6) . '-' . $user->id; $description = "Subscription to {$plan->name}"; $response = $this->authorizeNetService->chargeCreditCard($plan->price, $opaqueData, $customerDetails, $invoiceNumber, $description); if ($response != null && $response->getMessages()->getResultCode() == "Ok" && $response->getTransactionResponse() != null && $response->getTransactionResponse()->getMessages() != null) { $transactionId = $response->getTransactionResponse()->getTransId(); // Deactivate other existing subscriptions for this user $user->subscriptions() ->whereIn('status', ['active', 'trialing']) ->update(['status' => 'cancelled', 'ends_at' => now(), 'cancelled_at' => now()]); // Create new local subscription record $subscription = Subscription::create([ 'user_id' => $user->id, 'subscription_plan_id' => $plan->id, 'payment_gateway' => 'authorizenetgateway', 'gateway_transaction_id' => $transactionId, 'status' => $plan->trial_period_days > 0 ? 'trialing' : 'active', 'price_at_purchase' => $plan->price, 'currency_at_purchase' => $plan->currency, 'trial_ends_at' => $plan->trial_period_days > 0 ? now()->addDays($plan->trial_period_days) : null, 'starts_at' => now(), 'ends_at' => now()->add($plan->interval, $plan->interval_count), ]); // Award credits if (function_exists('setting') && setting('credits_system_enabled', '0') == '1' && $plan->credits_awarded_on_purchase > 0) { $this->creditService->awardCredits($user, $plan->credits_awarded_on_purchase, 'award_subscription_purchase', "Credits for {$plan->name} subscription", $subscription); } // Assign target role if (!empty($plan->target_role) && class_exists(Role::class) && Role::where('name', $plan->target_role)->where('guard_name', 'web')->exists()) { $user->syncRoles([$plan->target_role]); } return redirect()->route('dashboard')->with('success', "Successfully subscribed to {$plan->name} via Authorize.Net!"); } $errorMessage = $this->authorizeNetService->getErrorMessage($response); Log::error("Authorize.Net Subscription Error for user {$user->id}, plan {$plan->id}: " . $errorMessage, ['response' => $response]); return redirect()->route('subscription.authorizenet.checkout', ['subscriptionPlan' => $plan->slug])->with('error', 'Authorize.Net payment failed: ' . $errorMessage)->withInput(); } catch (\Exception $e) { Log::error("Authorize.Net Subscription Exception for user {$user->id}, plan {$plan->id}: " . $e->getMessage()); return redirect()->route('subscription.authorizenet.checkout', ['subscriptionPlan' => $plan->slug])->with('error', 'An error occurred while processing your payment with Authorize.Net.')->withInput(); } } // --- Wallet Deposit Methods --- public function showWalletDepositForm(Request $request) { if (setting('authorizenet_enabled', '0') != '1' || setting('allow_wallet_deposits', '0') != '1') { return redirect()->route('user.wallet.deposit.form')->with('error', 'Authorize.Net deposits are currently disabled.'); } $request->validate(['amount' => 'required|numeric|min:1']); $amount = (float) $request->input('amount'); $apiLoginId = setting('authorizenet_login_id'); $publicClientKey = setting('authorizenet_public_client_key'); if (!$apiLoginId || !$publicClientKey) { Log::error("Authorize.Net wallet deposit form: API Login ID or Public Client Key is not set."); return redirect()->route('user.wallet.deposit.form')->with('error', 'Authorize.Net gateway is not configured correctly.'); } return view('authorizenetgateway::wallet.deposit_form', compact('apiLoginId', 'publicClientKey', 'amount')); } public function processWalletDepositWithAcceptJs(Request $request) { if (setting('authorizenet_enabled', '0') != '1' || setting('allow_wallet_deposits', '0') != '1') { return redirect()->route('user.wallet.deposit.form')->with('error', 'Authorize.Net deposits are currently disabled.'); } $request->validate([ 'amount' => 'required|numeric|min:1', 'authorizenet_data_descriptor' => 'required|string', 'authorizenet_data_value' => 'required|string', // Payment nonce ]); /** @var User $user */ $user = Auth::user(); $amount = (float) $request->input('amount'); $currency = strtoupper(setting('currency_code', 'USD')); try { $opaqueData = [ 'dataDescriptor' => $request->input('authorizenet_data_descriptor'), 'dataValue' => $request->input('authorizenet_data_value'), ]; $customerDetails = ['email' => $user->email, 'firstName' => $user->name]; $invoiceNumber = 'DEPOSIT-' . Str::random(6) . '-' . $user->id; $description = "Wallet Deposit for user {$user->id}"; $response = $this->authorizeNetService->chargeCreditCard($amount, $opaqueData, $customerDetails, $invoiceNumber, $description); if ($response != null && $response->getMessages()->getResultCode() == "Ok" && $response->getTransactionResponse() != null && $response->getTransactionResponse()->getMessages() != null) { $transactionId = $response->getTransactionResponse()->getTransId(); $this->walletService->deposit($user, $amount, $currency, 'authorizenetgateway', $transactionId, "Wallet deposit via Authorize.Net"); return redirect()->route('user.wallet.history')->with('success', 'Successfully deposited ' . $currency . ' ' . number_format($amount, 2) . ' to your wallet.'); } $errorMessage = $this->authorizeNetService->getErrorMessage($response); Log::error("Authorize.Net Wallet Deposit Error for user {$user->id}: " . $errorMessage, ['response' => $response]); return redirect()->route('wallet.authorizenet.depositForm', ['amount' => $amount])->with('error', 'Authorize.Net payment failed: ' . $errorMessage)->withInput(); } catch (\Exception $e) { Log::error("Authorize.Net Wallet Deposit Exception for user {$user->id}: " . $e->getMessage()); return redirect()->route('wallet.authorizenet.depositForm', ['amount' => $amount])->with('error', 'An error occurred while processing your deposit with Authorize.Net.')->withInput(); } } } --- File: D:\projects\digitalvocano\Modules\AuthorizeNetGateway\Http\Controllers\AuthorizeNetWebhookController.php --- getContent(); $receivedSignature = $request->header('X-ANET-Signature'); if (!$signatureKey) { Log::error('Authorize.Net Webhook: Signature key not configured.'); return response()->json(['status' => 'error', 'message' => 'Configuration error'], 500); } // Verify signature $computedSignature = hash_hmac('sha512', $payload, hex2bin($signatureKey)); // Or just $signatureKey if it's not hex // Note: Authorize.Net's signature format might be "sha512=" prefix. Adjust as needed. // Example: $receivedSignature = str_replace('sha512=', '', $receivedSignature); if (!hash_equals(strtoupper($computedSignature), strtoupper($receivedSignature))) { Log::warning("Authorize.Net Webhook: Invalid signature. Received: {$receivedSignature}, Computed: {$computedSignature}"); return response()->json(['status' => 'error', 'message' => 'Invalid signature'], 403); } $event = json_decode($payload, true); Log::info('Authorize.Net Webhook Received:', $event); $eventType = $event['eventType'] ?? null; $payloadData = $event['payload'] ?? null; switch ($eventType) { case 'net.authorize.payment.authcapture.created': case 'net.authorize.payment.capture.created': // Handle successful one-time payment // $transactionId = $payloadData['id']; // Potentially update an order or subscription status break; case 'net.authorize.customer.subscription.created': case 'net.authorize.customer.subscription.updated': case 'net.authorize.customer.subscription.cancelled': case 'net.authorize.customer.subscription.expiring': case 'net.authorize.customer.subscription.suspended': case 'net.authorize.customer.subscription.terminated': // Handle subscription events // $subscriptionId = $payloadData['id']; (This is Authorize.Net's subscription ID) // Find your local subscription record and update its status, ends_at, etc. break; // Add more cases as needed default: Log::info("Authorize.Net Webhook: Unhandled event type '{$eventType}'"); } return response()->json(['status' => 'success'], 200); } } --- File: D:\projects\digitalvocano\Modules\AuthorizeNetGateway\Providers\AuthorizeNetGatewayServiceProvider.php --- registerCommands(); $this->registerCommandSchedules(); $this->registerTranslations(); $this->registerConfig(); $this->registerViews(); $this->loadMigrationsFrom(module_path($this->name, 'database/migrations')); } /** * Register the service provider. */ public function register(): void { $this->app->register(EventServiceProvider::class); $this->app->register(RouteServiceProvider::class); } /** * Register commands in the format of Command::class */ protected function registerCommands(): void { // $this->commands([]); } /** * Register command Schedules. */ protected function registerCommandSchedules(): void { // $this->app->booted(function () { // $schedule = $this->app->make(Schedule::class); // $schedule->command('inspire')->hourly(); // }); } /** * Register translations. */ public function registerTranslations(): void { $langPath = resource_path('lang/modules/'.$this->nameLower); if (is_dir($langPath)) { $this->loadTranslationsFrom($langPath, $this->nameLower); $this->loadJsonTranslationsFrom($langPath); } else { $this->loadTranslationsFrom(module_path($this->name, 'lang'), $this->nameLower); $this->loadJsonTranslationsFrom(module_path($this->name, 'lang')); } } /** * Register config. */ protected function registerConfig(): void { $configPath = module_path($this->name, config('modules.paths.generator.config.path')); if (is_dir($configPath)) { $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($configPath)); foreach ($iterator as $file) { if ($file->isFile() && $file->getExtension() === 'php') { $config = str_replace($configPath.DIRECTORY_SEPARATOR, '', $file->getPathname()); $config_key = str_replace([DIRECTORY_SEPARATOR, '.php'], ['.', ''], $config); $segments = explode('.', $this->nameLower.'.'.$config_key); // Remove duplicated adjacent segments $normalized = []; foreach ($segments as $segment) { if (end($normalized) !== $segment) { $normalized[] = $segment; } } $key = ($config === 'config.php') ? $this->nameLower : implode('.', $normalized); $this->publishes([$file->getPathname() => config_path($config)], 'config'); $this->merge_config_from($file->getPathname(), $key); } } } } /** * Merge config from the given path recursively. */ protected function merge_config_from(string $path, string $key): void { $existing = config($key, []); $module_config = require $path; config([$key => array_replace_recursive($existing, $module_config)]); } /** * Register views. */ public function registerViews(): void { $viewPath = resource_path('views/modules/'.$this->nameLower); $sourcePath = module_path($this->name, 'resources/views'); $this->publishes([$sourcePath => $viewPath], ['views', $this->nameLower.'-module-views']); $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->nameLower); Blade::componentNamespace(config('modules.namespace').'\\' . $this->name . '\\View\\Components', $this->nameLower); } /** * Get the services provided by the provider. */ public function provides(): array { return []; } private function getPublishableViewPaths(): array { $paths = []; foreach (config('view.paths') as $path) { if (is_dir($path.'/modules/'.$this->nameLower)) { $paths[] = $path.'/modules/'.$this->nameLower; } } return $paths; } } --- File: D:\projects\digitalvocano\Modules\AuthorizeNetGateway\Providers\EventServiceProvider.php --- > */ protected $listen = []; /** * Indicates if events should be discovered. * * @var bool */ protected static $shouldDiscoverEvents = true; /** * Configure the proper event listeners for email verification. */ protected function configureEmailVerification(): void {} } --- File: D:\projects\digitalvocano\Modules\AuthorizeNetGateway\Providers\RouteServiceProvider.php --- mapApiRoutes(); $this->mapWebRoutes(); } /** * Define the "web" routes for the application. * * These routes all receive session state, CSRF protection, etc. */ protected function mapWebRoutes(): void { Route::middleware('web')->group(module_path($this->name, '/routes/web.php')); } /** * Define the "api" routes for the application. * * These routes are typically stateless. */ protected function mapApiRoutes(): void { Route::middleware('api')->prefix('api')->name('api.')->group(module_path($this->name, '/routes/api.php')); } } --- File: D:\projects\digitalvocano\Modules\AuthorizeNetGateway\resources\views\admin\form\toggle-switch.blade.php --- @props([ 'id', 'name', 'label', 'value' => '1', 'checked' => false, 'helpText' => null, ])
{{ $helpText }}
@endif @error($name){{ $message }}
@enderrorPrice: ${{ number_format($plan->price, 2) }} / {{ $plan->interval }}
@if (session('error')){{ __('Amount to Deposit:') }} {{ setting('currency_symbol', '$') }}{{ number_format($amount, 2) }}
Module: {!! config('authorizenetgateway.name') !!}