version, '3.0.0', '<'); } /** * Get a URL to the PayPlug gateway settings page. * * @return string */ public static function get_setting_link() { $use_id_as_section = function_exists('WC') ? version_compare(WC()->version, '2.6', '>=') : false; $section_slug = $use_id_as_section ? 'payplug' : strtolower('PayplugGateway'); return admin_url('admin.php?page=wc-settings&tab=checkout§ion=' . $section_slug); } /** * Get all country code supported by PayPlug. * * Those are ISO 3166-1 alpha-2. You can find more information on https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 * * @return array */ public static function get_supported_countries() { return [ 'AD', 'AO', 'AX', 'BG', 'BO', 'BY', 'CH', 'CR', 'DE', 'EE', 'FI', 'GB', 'GL', 'GT', 'HR', 'IN', 'JM', 'KM', 'KZ', 'LS', 'MD', 'MM', 'MT', 'NA', 'NO', 'PE', 'PN', 'RE', 'SC', 'SK', 'ST', 'TF', 'TN', 'UA', 'VC', 'WS', 'AE', 'AQ', 'AZ', 'BH', 'BQ', 'BZ', 'CI', 'CU', 'DJ', 'EG', 'FJ', 'GD', 'GM', 'GU', 'HT', 'IO', 'JO', 'KN', 'LA', 'LT', 'ME', 'MN', 'MU', 'NC', 'NP', 'PF', 'PR', 'RO', 'SD', 'SL', 'SV', 'TG', 'TO', 'UG', 'VE', 'YE', 'AM', 'AW', 'BF', 'BN', 'BW', 'CG', 'CO', 'AF', 'CZ', 'EC', 'EU', 'GA', 'GI', 'GS', 'HN', 'IM', 'JE', 'KI', 'KY', 'LR', 'MC', 'ML', 'MS', 'MZ', 'NL', 'PA', 'PM', 'QA', 'SB', 'SJ', 'SS', 'TD', 'TM', 'TZ', 'VA', 'WF', 'AR', 'BA', 'BI', 'BR', 'CA', 'CK', 'CV', 'DK', 'EH', 'FK', 'GE', 'GN', 'GW', 'HU', 'IQ', 'JP', 'KP', 'LB', 'LU', 'MF', 'MO', 'MV', 'NE', 'NR', 'PG', 'PS', 'RS', 'SE', 'SM', 'SX', 'TH', 'TR', 'UM', 'VG', 'YT', 'AL', 'AU', 'BE', 'BM', 'BV', 'CF', 'CN', 'CY', 'DZ', 'ET', 'FR', 'GH', 'GR', 'HM', 'IL', 'IT', 'KH', 'KW', 'LK', 'MA', 'MK', 'MR', 'MY', 'NI', 'OM', 'PL', 'PY', 'SA', 'SI', 'SR', 'TC', 'TL', 'TW', 'UZ', 'VU', 'ZW', 'AI', 'AT', 'BD', 'BL', 'BT', 'CD', 'CM', 'CX', 'DO', 'ES', 'FO', 'GG', 'GQ', 'HK', 'IE', 'IS', 'KG', 'KS', 'LI', 'LY', 'MH', 'MQ', 'MX', 'NG', 'NZ', 'PK', 'PW', 'RW', 'SH', 'SO', 'SZ', 'TK', 'TV', 'UY', 'VN', 'ZM', 'AG', 'AS', 'BB', 'BJ', 'BS', 'CC', 'CL', 'CW', 'DM', 'ER', 'FM', 'GF', 'GP', 'GY', 'ID', 'IR', 'KE', 'KR', 'LC', 'LV', 'MG', 'MP', 'MW', 'NF', 'NU', 'PH', 'PT', 'RU', 'SG', 'SN', 'SY', 'TJ', 'TT', 'US', 'VI', 'ZA', ]; } /** * Ensure country code is supported by PayPlug. * * @param string $country The ISO 3166-1 alpha-2 code for the country * * @return bool * * @author Clément Boirie */ public static function is_country_supported($country) { $country = trim($country); if (empty($country)) { return false; } return in_array(strtoupper($country), self::get_supported_countries()); } /** * Get default country. * * @return string */ public static function get_default_country() { $country = \WC()->countries->get_base_country(); return (self::is_country_supported($country)) ? strtoupper($country) : 'FR'; } /** * Get minimum amount allowed by PayPlug. * * This amount is in cents. * * @return int */ public static function get_minimum_amount() { return 99; } /** * Get maximum amount allowed by PayPlug. * * This amount is in cents. * * @return int */ public static function get_maximum_amount() { return 2000000; } /** * Convert amount in cents. * * @param float $amount * * @return int */ public static function get_payplug_amount($amount) { if (is_null($amount)) { return $amount; } return absint(wc_format_decimal(((float) $amount * 100), wc_get_price_decimals())); } /** * Extract useful metadata from PayPlug response. * * @param APIResource $resource * * @return array */ public static function extract_transaction_metadata($resource) { // For non-card payment methods (e.g. Oney), the API returns "card": null. // Accessing null->property throws \Error in PHP 8+, which is not caught by catch(\Exception). $card = (isset($resource->card) && $resource->card !== null) ? $resource->card : null; return [ 'transaction_id' => sanitize_text_field($resource->id), 'paid' => (bool) $resource->is_paid, 'refunded' => (bool) $resource->is_refunded, 'amount' => sanitize_text_field($resource->amount), 'amount_refunded' => sanitize_text_field($resource->amount_refunded), '3ds' => (bool) $resource->is_3ds, 'live' => (bool) $resource->is_live, 'paid_at' => isset($resource->hosted_payment->paid_at) ? sanitize_text_field($resource->hosted_payment->paid_at) : sanitize_text_field($resource->created_at), 'card_last4' => $card !== null ? sanitize_text_field($card->last4) : '', 'card_exp_month' => $card !== null ? sanitize_text_field($card->exp_month) : '', 'card_exp_year' => $card !== null ? sanitize_text_field($card->exp_year) : '', 'card_brand' => $card !== null ? sanitize_text_field($card->brand) : '', 'card_country' => $card !== null ? sanitize_text_field($card->country) : '', ]; } /** * @param \WC_Order $order * * @return array|bool * * @author Clément Boirie */ public static function get_transaction_metadata($order) { if (self::is_pre_30()) { return get_post_meta($order->id, '_payplug_metadata', true); } else { return $order->get_meta('_payplug_metadata', true); } } /** * Save transaction metadata extracted from PayPlug response. * * @param \WC_Order $order * @param array $metadata * * @return void */ public static function save_transaction_metadata($order, $metadata): void { if (self::is_pre_30()) { update_post_meta($order->id, '_payplug_metadata', $metadata); } else { $order->add_meta_data('_payplug_metadata', $metadata, true); $order->save_meta_data(); } } /** * Set flag ipn ( in progress / over ) on order * * @param \WC_Order $order * @param array $metadata * @param bool $flag * * @return void */ public static function set_flag_ipn_order($order, $metadata, $flag): void { $metadata['transaction_in_progress'] = $flag; self::save_transaction_metadata($order, $metadata); } /** * Get transient key from payplug option * * @return string */ public static function get_transient_key($options) { $transient_key = PayplugGateway::OPTION_NAME . (array_key_exists('mode', $options) && (bool) $options['mode'] ? '_live' : '_test'); return $transient_key; } /** * Get transient live key from payplug option * * @return string */ public static function get_live_transient_key() { return PayplugGateway::OPTION_NAME . '_live'; } /** * Set transient data for payplug account * * @return string */ public static function set_transient_data($data, $options = null) { $options = $options ? $options : self::get_payplug_options(); $transient_key = self::get_transient_key($options); set_transient($transient_key, isset($data['httpResponse']) ? $data['httpResponse'] : []); } /** * Get current option from payplug settings * * @return array */ public static function get_account_data_from_options() { $options = self::get_payplug_options(); $transient_key = self::get_transient_key($options); $account = get_transient($transient_key); if (empty($account) || !is_array($account)) { self::set_account_data_from_options(); $account = get_transient($transient_key); } return $account; } /** * Get current option from payplug settings *this should replace get_account_data_from_options * * @return array */ public static function generic_get_account_data_from_options($gateway_id) { $options = self::get_payplug_options(); $transient_key = self::get_transient_key($options); $account = get_transient($transient_key); //if transient is empty, it goes and get the permissions for the customer to populate it if (empty($account) || !is_array($account)) { self::set_account_data_from_options(); $account = get_transient($transient_key); } if (!empty($account)) { $helper = new self(); $configuration = $helper->get_service('configuration'); $account['permissions']['payplug'] = $configuration->get_option('payment_methods.configuration.payplug.active'); } return $account; } /** * Set current option from payplug settings and api call * * @return void */ public static function set_account_data_from_options() { $helper = new self(); $configuration = $helper->get_service('configuration'); $options = $configuration->get_options(); if (empty($options) || !isset($options['api_key']) || !isset($options['jwt'])) { return []; } // get_bearer_token() refreshes the JWT if it's close to expiry (OAuth2 accounts). $mode = (bool) $options['mode'] ? 'live' : 'test'; $key = (string) $helper->get_api()->get_bearer_token($mode); if (empty($key)) { return []; } try { $parameters_account = Authentication::getAccount(new Payplug($key)); self::set_transient_data($parameters_account, $options); } catch (\Payplug\Exception\UnauthorizedException $e) { self::exception_handler_400_logout($e->getCode(), __('payplug_enable_feature', 'payplug'), sprintf('Account request error from PayPlug API : %s
' . __('Successfully logged out.', 'payplug') . '', wc_print_r($e->getMessage(), true))); } catch (\Payplug\Exception\ConfigurationNotSetException $e) { } catch (\Payplug\Exception\ForbiddenException $e) { } catch (\Payplug\Exception\ForbiddenException $e) { return []; } } /** * Check if oney is available with current settings * * @return bool */ public static function is_oney_available() { $account = self::get_account_data_from_options(); if (!$account) { return false; } $options = self::get_payplug_options(); $oney_active = (bool) $options['payment_methods']['configuration']['oney']['active']; return $account && $account['permissions'][PayplugPermissions::USE_ONEY] == '1' && $oney_active; } /** * Hide popup for if country_code != payplug country * https://payplug-prod.atlassian.net/browse/WOOC-249 * * @return bool */ public static function show_oney_popup() { $account = self::get_account_data_from_options(); if ($account && $account['permissions'][PayplugPermissions::USE_ONEY] == true && $account['country'] == self::getISOCountryCode()) { return true; } return false; } /** * @return bool */ public static function check_order_max_amount($order_total) { if ($order_total < PayplugGatewayOney3x::MIN_AMOUNT || $order_total > PayplugGatewayOney3x::MAX_AMOUNT) { return false; } return true; } /** * Load translations from plugin languages folder. * * @param string $plugin_rel_path * * @return bool */ public static function load_plugin_textdomain($plugin_rel_path) { $domain = 'payplug'; $locale = apply_filters('plugin_locale', is_admin() ? get_user_locale() : get_locale(), $domain); $mofile = $domain . '-' . $locale . '.mo'; $path = WP_PLUGIN_DIR . '/' . trim($plugin_rel_path, '/'); return load_textdomain($domain, $path . '/' . $mofile); } public static function getISOCountryCode() { preg_match('([a-z-]+)', get_locale(), $country); return strtoupper($country[0]); } /** * Get country of the payplug merchant account and save it to the database * * @return string payplug merchant account country */ public static function get_payplug_merchant_country() { $data = self::get_payplug_options(); //in order to reduce the getAccount calls if (isset($data['payplug_live_key'])) { if (!isset($data['payplug_merchant_country'])) { return self::UpdateCountryOption($data); } return $data['payplug_merchant_country']; } $country = wc_get_base_location(); return $country['country']; } /** * Update payplug options and return the country * * @param $options * * @throws \Payplug\Exception\ConfigurationException * * @return string */ public static function UpdateCountryOption($options) { if (!isset($options['api_key'])) { $options['company_iso'] = 'FR'; return $options['company_iso']; } try { $api_key = json_decode($options['api_key'], true); //fail safe for non activated account if ((isset($options['mode'])) && $api_key['test']) { $key = $options['mode'] && !empty($api_key['live']) ? $api_key['live'] : $api_key['test']; } if (empty($api_key['live'])) { $options['mode'] = false; } if (isset($key) && !empty($key)) { $response = self::get_account_data_from_options(); } if (isset($response['httpResponse']['country'])) { $options['company_iso'] = $response['httpResponse']['country']; update_option('woocommerce_payplug_settings', apply_filters('woocommerce_settings_api_sanitized_fields_payplug', $options)); } else { //default value for merchant country $options['company_iso'] = 'FR'; } } catch (ForbiddenException $e) { PayplugGateway::log('Error while getting account : ' . $e->getMessage(), 'error'); \WC_Admin_Settings::add_error($e->getMessage()); $options['company_iso'] = 'FR'; } return $options['company_iso']; } public static function get_live_key() { // get_bearer_token() refreshes the JWT if it's close to expiry (OAuth2 accounts). return (new self())->get_api()->get_bearer_token('live'); } public static function get_test_key() { return (new self())->get_api()->get_bearer_token('test'); } public static function check_mode() { return self::get_payplug_options()['mode']; } /** * Domain the Integrated Payment SDK should submit card-tokenization requests to. Depends * on whether the connected merchant account is QA or production, which is a build-time * distinction (see SECURE_DOMAIN in payplug-config.php), not something this can derive * from the Test/Live mode toggle at runtime. * * @return string */ public static function get_secure_domain(): string { return SECURE_DOMAIN; } /** * URL of the official Oney widget loader script. Depends on whether the connected * merchant account is QA or production, which is a build-time distinction (see * ONEY_LOADER_URL in payplug-config.php), not something this can derive from the * Test/Live mode toggle at runtime. * * @return string */ public static function get_oney_loader_url(): string { return ONEY_LOADER_URL; } public static function payplug_logout(): void { $helper = new self(); $helper->get_service('configuration')->clean_option(); set_transient(self::get_transient_key(self::get_payplug_options()), null); } public static function plugin_deactivation(): void { $option_name = 'woocommerce_payplug_settings'; delete_option($option_name); // for site options in Multisite delete_site_option($option_name); \Payplug\PayplugWoocommerce\Model\Lock::delete_lock_table(); } public static function available_shipping_methods($carriers = []) { // Build enabled shipping methods based on active Zone $enabled_method_types = []; if (class_exists('\\WC_Shipping_Zones')) { // Regular zones $zones = \WC_Shipping_Zones::get_zones(); foreach ($zones as $zone) { $methods = isset($zone['shipping_methods']) ? $zone['shipping_methods'] : []; foreach ($methods as $m) { if (!empty($m->enabled)) { $enabled_method_types[$m->id] = true; } } } // Locations not covered by zones (zone 0) $defaultZone = new \WC_Shipping_Zone(0); foreach ($defaultZone->get_shipping_methods(true) as $m) { if (!empty($m->enabled)) { $enabled_method_types[$m->id] = true; } } } $shippings = WC()->shipping()->get_shipping_methods(); $shippings_methods = []; foreach ($shippings as $shipping) { // Only keep shipping method types that are currently enabled in at least one zone if (!isset($enabled_method_types[$shipping->id])) { continue; } $shippings_methods[] = [ 'id_carrier' => $shipping->id, 'name' => $shipping->method_title, 'checked' => in_array($shipping->id, $carriers, true), ]; } return $shippings_methods; } public static function get_payplug_options() { $helper = new self(); return $helper->get_service('configuration')->get_options(); } public static function get_applepay_options() { $options = self::get_payplug_options(); $applepay_configuration = $options['payment_methods']['configuration']['apple_pay']; $applepay_display = json_decode($applepay_configuration['display'], true); $applepay = [ 'enabled' => (bool) $applepay_configuration['active'], 'checkout' => (bool) $applepay_display['checkout'], 'cart' => (bool) $applepay_display['cart'], 'carriers' => json_decode($applepay_configuration['carriers'], true), ]; return $applepay; } public static function is_checkout_block() { return WC_Blocks_Utils::has_block_in_page(wc_get_page_id('checkout'), 'woocommerce/checkout'); } public static function is_cart_block() { return WC_Blocks_Utils::has_block_in_page(wc_get_page_id('cart'), 'woocommerce/cart'); } public static function is_product_block() { return WC_Blocks_Utils::has_block_in_page(wc_get_page_id('product'), 'woocommerce/product'); } /** * Checks if subscriptions are enabled on the site. * * @return bool Whether subscriptions is enabled or not. * * @since 5.6.0 */ public static function is_subscriptions_enabled() { return class_exists('WC_Subscriptions') && class_exists('WC_Subscription') && version_compare(WC_Subscriptions::$version, '2.2.0', '>='); } /** * Cart has any subscriptions * * @return bool */ public static function is_subscription() { if (is_null(WC()->cart)) { wc_load_cart(); WC()->cart->get_cart_from_session(); } if (empty(WC()->cart) || empty(WC()->cart->get_cart())) { return false; } foreach (WC()->cart->get_cart() as $prod) { if (empty($prod['product_id'])) { return false; } $pid = $prod['product_id']; $product = wc_get_product($pid); if ($product->is_type('subscription')) { return true; } } return false; } /** * Handling 400 errors from API (get_account, get_permissions) and return JSON rest response * * @param $title * @param $msg * * @return false */ public static function exception_handler_400_logout($error_code, $title, $msg) { if (!empty($error_code) && ($error_code === 401 || $error_code === 403)) { self::payplug_logout(); wp_send_json_error([ 'title' => $title, 'msg' => $msg, 'close' => __('payplug_ok', 'payplug'), ]); } return false; } }