settings = $settings; $this->methodSlug = $this->getMeta('slug'); if ($subscriptions) { $this->supportedFeatures[] = 'subscriptions'; } $this->subscriptions = $subscriptions; // register global hooks $this->init(); } public function init(): void { add_filter('fluent_cart/transaction/url_' . $this->methodSlug, [$this, 'getTransactionUrl'], 10, 2); add_filter('fluent_cart/subscription/url_' . $this->methodSlug, [$this, 'getSubscriptionUrl'], 10, 2); } public function has(string $feature): bool { return in_array($feature, $this->supportedFeatures); } public function getMeta($key = '') { $meta = $this->meta(); $gatewaySettings = $this->settings->get(); if (isset($gatewaySettings['checkout_label']) && !empty($gatewaySettings['checkout_label'])) { $meta['title'] = $gatewaySettings['checkout_label']; } if (isset($gatewaySettings['checkout_logo']) && !empty($gatewaySettings['checkout_logo'])) { $meta['logo'] = $gatewaySettings['checkout_logo']; } if (isset($gatewaySettings['checkout_instructions']) && !empty($gatewaySettings['checkout_instructions'])) { $meta['instructions'] = $gatewaySettings['checkout_instructions']; } if ($key !== '') { return Arr::get($meta, $key, ''); } return $meta; } public function isUpcoming(): bool { return $this->getMeta('upcoming'); } public function setStoreSettings(StoreSettings $settings): void { $this->storeSettings = $settings; } public function storeSettings(): StoreSettings { return $this->storeSettings; } public function isCurrencySupported(): bool { return true; } public function isEnabled(): bool { return $this->settings->get('is_active') === 'yes'; } public static function validateSettings($data): array { return $data; } public static function beforeSettingsUpdate($data, $oldSettings): array { return $data; } public function updateSettings($data) { if ($this->isUpcoming()) { wp_send_json([ 'status' => 'failed', 'message' => __('Payment method is upcoming! Not available for right now!', 'fluent-cart') ], 422); } $oldSettings = $this->settings->get(); $settings = wp_parse_args($data, $oldSettings); $settings = Helper::sanitize($settings, $this->fields()); $is_active = Arr::get($settings, 'is_active', 'no'); // validate if the settings/credentials are correct if ('yes' === $is_active) { $response = static::validateSettings($settings); if (isset($response['status']) && $response['status'] === 'failed') { wp_send_json( [ 'status' => 'failed', 'message' => $response['message'] ? $response['message'] : __('Invalid credentials!', 'fluent-cart'), 'data' => [] ], 422 ); } } $settings = static::beforeSettingsUpdate($settings, $oldSettings); // unset($settings['payment_mode']); unset($settings['provider']); fluent_cart_update_option($this->settings->methodHandler, $settings); return $settings; } public function getSuccessUrl($transaction, $args = []) { $paymentHelper = new PaymentHelper($this->getMeta('route')); return $paymentHelper->successUrl($transaction->uuid, $args); } public static function getCancelUrl(): string { $checkoutPage = (new StoreSettings())->getCheckoutPage(); // get cart hash from url $cartHash = App::request()->get('fct_cart_hash', ''); if ($cartHash) { return add_query_arg([ 'fct_cart_hash' => $cartHash ], $checkoutPage); } return $checkoutPage; } public function paymentFailedNote($content, $data) { $request = Arr::get($data, 'request'); $trx_hash = $request->getSafe('trx_hash', 'sanitize_text_field'); $transaction = OrderTransaction::query()->where('uuid', $trx_hash)->first(); if (!$transaction) { return __('Transaction not found!', 'fluent-cart'); } $order = (new Orders())->getById($transaction->order_id); if (!$order || $transaction->status === Status::TRANSACTION_SUCCEEDED) { return ''; } $failedtitle = __('Payment Failed', 'fluent-cart'); $hasLog = ActivityResource::getQuery()->where('module_id', $order->id) ->where('module_name', 'Order') ->where('status', 'error') ->where('title', $failedtitle) ->count(); if (!$hasLog) { $content = 'Payment Failed Reason: ' . $request->getSafe('reason', 'sanitize_text_field'); fluent_cart_error_log($failedtitle, $content, [ 'module_id' => $order->id, 'module_name' => 'Order' ]); } ob_start(); (new ReceiptRenderer())->renderConfirmationError([ 'order' => $order, 'failed_reason' => $request->getSafe('reason', 'sanitize_text_field'), 'custom_payment_url' => PaymentHelper::getCustomPaymentLink($order->uuid) ]); return ob_get_clean(); } protected function getListenerUrl($args = null) { return (new PaymentHelper($this->getMeta('route')))->listenerUrl($args); } public function getOrderByHash($orderHash) { return (new Orders())->getByHash($orderHash); } public function validateSubscriptions($items): bool { $hasSubscription = (new CartResource())->hasSubscriptionProduct($items); if ($hasSubscription && !$this->has('subscriptions')) { wp_send_json([ 'status' => 'failed', 'message' => __('Subscription payment is not avalable for this gateway. Please choose another payment method!', 'fluent-cart') ], 422); } return $hasSubscription; } public function validatePaymentMethod($data) { $isZeroPayment = Arr::get($data, 'isZeroPayment', false); if (!$this->isEnabled() && !$isZeroPayment) { return [ 'isValid' => false, 'reason' => sprintf( /* translators: %s is the payment method name */ __('Selected payment method %s is not active!', 'fluent-cart'), $this->getMeta('route') ) ]; } $hasSubscriptions = (CartCheckoutHelper::make())->hasSubscription(); if ($hasSubscriptions === 'yes' && !$this->has('subscriptions')) { return [ 'isValid' => false, 'reason' => sprintf( /* translators: %s is the payment method name */ __('Subscription is not active for Selected payment method %s!', 'fluent-cart'), $this->getMeta('route') ) ]; } return [ 'isValid' => true, ]; } public function updateOrderDataByOrder($order, $transactionData, $transaction) { if ($order == null) { return; } $transaction->fill($transactionData); $transaction->save(); $paymentStatus = Status::syncPaymentStatus(Arr::get($transactionData, 'status')); $orderStatus = !in_array($paymentStatus, [Status::TRANSACTION_SUCCEEDED, Status::PAYMENT_PAID]) ? $paymentStatus : Status::ORDER_PROCESSING; $statusHelper = (new StatusHelper())->setOrder($order); $statusHelper->updateTransactionData($transactionData, $transaction); if ($amount = Arr::get($transactionData, 'total')) { $statusHelper->updateTotalPaid($amount); } $statusHelper->changeOrderStatus($orderStatus, $paymentStatus, $this->getMeta('title'), $this->getMeta('slug')); //If product is digital and processing then trigger status to completed if ($order->fulfillment_type == 'digital' && $orderStatus === Status::ORDER_PROCESSING && $order->total_amount <= $order->total_paid) { $statusHelper->changeOrderStatus(Status::ORDER_COMPLETED, $paymentStatus, $this->getMeta('title'), $this->getMeta('slug')); } do_action('fluent_cart/payments/after_payment_' . $paymentStatus, [ 'order' => $order ]); } public function getCheckoutItems(): array { return (CartCheckoutHelper::make())->getItems(); } public function getSettings(): BaseGatewaySettings { return $this->settings; } public function renderStoreModeNotice(): string { if ((new StoreSettings())->get('order_mode') == 'test') { return '
' . __('Your Store is in test mode, change Store\'s \'Order Mode\' to \'Live\' and update related settings to enable live payment!', 'fluent-cart') . '
'; } return '
' . __('Your Store is in Live mode', 'fluent-cart') . '
'; } public function beforeRenderPaymentMethod($hasSubscription): void { $this->enqueue($hasSubscription); } public function getEnqueueVersion() { return FLUENTCART_VERSION; } public function getEnqueueScriptSrc($hasSubscription): array { return []; } public function getEnqueueStyleSrc(): array { return []; } public function getTransactionUrl($url, $data) { return $url; } public function getSubscriptionUrl($url, $data) { return $url; } public function processRefund($transaction, $amount, $args) { return new \WP_Error('not_implemented', __('Refund process is not implemented for this payment gateway.', 'fluent-cart')); } public function enqueue($hasSubscription): void { $styles = $this->getEnqueueStyleSrc(); $scripts = $this->getEnqueueScriptSrc($hasSubscription); foreach ($styles as $style) { wp_enqueue_style( Arr::get($style, 'handle'), Arr::get($style, 'src'), Arr::get($style, 'deps', null), Arr::get($style, 'version', $this->getEnqueueVersion()) ); } $handleToEnqueue = ''; $scriptHandles = []; foreach ($scripts as $script) { $handle = Arr::get($script, 'handle'); if (empty($handleToEnqueue)) { $handleToEnqueue = $handle; } $scriptHandles[] = $handle; wp_enqueue_script( $handle, Arr::get($script, 'src'), Arr::get($script, 'deps', null), Arr::get($script, 'version', $this->getEnqueueVersion()), Arr::get($script, 'in_footer', false), ); } // Add filter to prevent consent plugins from blocking payment gateway scripts if (!empty($scriptHandles) && is_array($scriptHandles)) { $gatewayInstance = $this; add_filter('script_loader_tag', function($tag, $handle, $src) use ($scriptHandles, $gatewayInstance) { if (!in_array($handle, $scriptHandles, true)) { return $tag; } // This makes payment scripts load as "necessary" cookies $attributes = [ 'data-category="necessary"', 'data-consent-category="necessary"', 'data-cookieconsent="ignore"', 'data-no-optimize="1"', 'data-cfasync="false"', ]; $serviceName = ''; if (is_object($gatewayInstance)) { if (method_exists($gatewayInstance, 'getConsentServiceName')) { $serviceName = (string) $gatewayInstance->getConsentServiceName(); } if (empty($serviceName) && method_exists($gatewayInstance, 'getMeta')) { $serviceName = (string) $gatewayInstance->getMeta('title'); } } if (!empty($serviceName)) { $serviceName = esc_attr($serviceName); $attributes[] = 'data-usercentrics="' . $serviceName . '"'; $attributes[] = 'data-service="' . $serviceName . '"'; } $tag = preg_replace( '/getLocalizeData() as $key => $val) { wp_localize_script($handleToEnqueue, $key, $val); } } } public function prepare($mode, $hasSubscription) { $this->beforeRenderPaymentMethod($hasSubscription); $this->render($mode); $route = $this->getMeta('route'); do_action_deprecated('fluent-cart/after_render_payment_method_' . $route, [], '1.3.16', 'fluent_cart/after_render_payment_method_' . $route, 'Use fluent_cart/after_render_payment_method_' . $route . ' instead of fluent-cart/after_render_payment_method_' . $route . '. It will be removed in v1.4.3.'); do_action('fluent_cart/after_render_payment_method_' . $route); } public function render($mode = 'logo') { $content = ''; if ($mode === 'logo') { $content .= '' . esc_attr($this->getMeta('title')) . ''; } elseif ($mode === 'radio') { $content .= '' . ''; } else { $content .= '' . $this->getMeta('title') . ''; } echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } public function getLocalizeData(): array { // $example = [ // 'var_name' => [ // 'key' => 'value' // ], // // 'var_name_two' => [ // 'key' => 'value' // ], // ]; return []; } /** * Get the consent service name for this payment gateway * Used by consent management plugins (Usercentrics, CookieYes, etc.) * Override in child classes to specify the service name * * @return string|null Service name or null to use gateway slug */ public function getConsentServiceName(): ?string { // By default, return null and let the system use gateway meta title return null; } }