/** * Takes a heading and normalizes it based on the current importer type * * @since 1.0.0 * @param string $heading * @return string */ public static function normalize_heading($heading) { $s_heading = trim($heading); // lowercase and replace space with underscores if not a custom meta value if (!SV_WC_Helper::str_starts_with($s_heading, 'meta:')) { $s_heading = strtolower($heading); $s_heading = str_replace(' ', '_', $s_heading); } return $s_heading; }
/** * Convert the request data into XML. * * @since 4.3.0 * @return string */ protected function to_xml() { if (!empty($this->request_xml)) { return $this->request_xml; } $this->xml = new XMLWriter(); // Create XML document in memory $this->xml->openMemory(); // Set XML version & encoding $this->xml->startDocument('1.0', 'UTF-8'); $request_data = $this->get_request_data(); SV_WC_Helper::array_to_xml($this->xml, $this->get_root_element(), $request_data[$this->get_root_element()]); $this->xml->endDocument(); return $this->request_xml = $this->xml->outputMemory(); }
/** * Constructor * * @access public * @since 1.0 * @param string : $account_sid : required * @param string $auth_token : required * @param string $from_number : required - number to send SMS messages from * @param array $options optional : API options * @return \WC_Twilio_SMS_API */ public function __construct($account_sid, $auth_token, $from_number, $options = array()) { $this->account_sid = $account_sid; $this->auth_token = $auth_token; // don't format to e.164, as the country code is not available $this->from_number = preg_replace('[\\D]', '', $from_number); // Format the Alphanumeric Sender ID if set if (isset($options['asid'])) { $asid = preg_replace('/[^a-zA-Z0-9 ]/', '', $options['asid']); $asid = SV_WC_Helper::str_truncate($asid, 11); $this->asid = $asid; } // Set Basic HTTP Auth $this->wp_remote_http_args['headers']['Authorization'] = sprintf('Basic %s', base64_encode($this->account_sid . ':' . $this->auth_token)); }
/** * Add free trial status to membership statuses * * @since 1.0.0 * @param array $statuses Array of statuses * @return array Modified array of statuses */ public function add_free_trial_status($statuses) { $statuses = SV_WC_Helper::array_insert_after($statuses, 'wcm-active', array('wcm-free_trial' => array('label' => _x('Free Trial', 'Membership Status', WC_Memberships::TEXT_DOMAIN), 'label_count' => _n_noop('Free Trial <span class="count">(%s)</span>', 'Free Trial <span class="count">(%s)</span>', WC_Memberships::TEXT_DOMAIN)))); return $statuses; }
/** * Adds debug messages to the page as a WC message/error, and / or to the WC Error log * * @since 2.0 * @param string $message to add * @param string $display how to display message, defaults to `message`, options are: * + `message` - display with WC message style, no formatting * + `request` - display with WC message style and <pre> formatting * + `response` - display with WC message style and XML/<pre> formatting * + `error` - display with WC error style, no formatting */ protected function log($message, $display = 'message') { // do nothing when debug mode is off or blank message if ('off' == $this->debug_mode || !$message) { return; } // add message to checkout page if ('checkout' === $this->debug_mode || 'both' === $this->debug_mode) { switch ($display) { case 'message': SV_WC_Helper::wc_add_notice($message); break; case 'request': SV_WC_Helper::wc_add_notice(__('API Request: ', WC_Amazon_FPS::TEXT_DOMAIN) . '<br/><pre>' . $message . '</pre>'); break; case 'response': $dom = new DOMDocument(); $dom->loadXML($message); $dom->formatOutput = true; SV_WC_Helper::wc_add_notice(__('API Response: ', WC_Amazon_FPS::TEXT_DOMAIN) . '<br/><pre>' . htmlspecialchars($dom->saveXML()) . '</pre>'); break; case 'error': SV_WC_Helper::wc_add_notice($message, 'error'); break; } } // add message to WC log if ('log' === $this->debug_mode || 'both' === $this->debug_mode) { $GLOBALS['wc_amazon_fps']->log($message); } }
/** * Handle any actions from the 'My Payment Methods' section on the * 'My Account' page * * @since 1.0.0 */ public function handle_my_payment_methods_actions() { assert($this->supports_tokenization()); // pre-conditions if (!$this->is_available() || !$this->tokenization_enabled()) { return; } $token = isset($_GET['wc-' . $this->get_id_dasherized() . '-token']) ? trim($_GET['wc-' . $this->get_id_dasherized() . '-token']) : ''; $action = isset($_GET['wc-' . $this->get_id_dasherized() . '-action']) ? $_GET['wc-' . $this->get_id_dasherized() . '-action'] : ''; // process payment method actions if ($token && $action && !empty($_GET['_wpnonce']) && is_user_logged_in()) { // security check if (false === wp_verify_nonce($_GET['_wpnonce'], 'wc-' . $this->get_id_dasherized() . '-token-action')) { SV_WC_Helper::wc_add_notice(_x('There was an error with your request, please try again.', 'Supports direct payment method tokenization', $this->text_domain), 'error'); wp_redirect(get_permalink(wc_get_page_id('myaccount'))); exit; } // current logged in user $user_id = get_current_user_id(); // handle deletion if ('delete' === $action) { if (!$this->remove_payment_token($user_id, $token)) { SV_WC_Helper::wc_add_notice(_x('Error removing payment method', 'Supports direct payment method tokenization', $this->text_domain), 'error'); } else { SV_WC_Helper::wc_add_notice(_x('Payment method deleted.', 'Supports direct payment method tokenization', $this->text_domain)); } } // handle default change if ('make-default' === $action) { $this->set_default_payment_token($user_id, $token); } // remove the query params wp_redirect(get_permalink(wc_get_page_id('myaccount'))); exit; } }
/** * Handle payment methods actions, e.g. deleting a payment method or setting * one as default * * @since 4.0.0 */ public function handle_payment_method_actions() { if (!$this->has_tokens) { return; } $token = isset($_GET['wc-' . $this->get_plugin()->get_id_dasherized() . '-token']) ? trim($_GET['wc-' . $this->get_plugin()->get_id_dasherized() . '-token']) : ''; $action = isset($_GET['wc-' . $this->get_plugin()->get_id_dasherized() . '-action']) ? $_GET['wc-' . $this->get_plugin()->get_id_dasherized() . '-action'] : ''; // process payment method actions if ($token && $action && !empty($_GET['_wpnonce']) && is_user_logged_in()) { // security check if (false === wp_verify_nonce($_GET['_wpnonce'], 'wc-' . $this->get_plugin()->get_id_dasherized() . '-token-action')) { SV_WC_Helper::wc_add_notice(esc_html__('Oops, you took too long, please try again.', 'woocommerce-plugin-framework'), 'error'); $this->redirect_to_my_account(); } // current logged in user $user_id = get_current_user_id(); $gateway = $this->get_plugin()->get_gateway_from_token($user_id, $token); // couldn't find an associated gateway for that token if (!is_object($gateway)) { SV_WC_Helper::wc_add_notice(esc_html__('There was an error with your request, please try again.', 'woocommerce-plugin-framework'), 'error'); $this->redirect_to_my_account(); } switch ($action) { // handle deletion case 'delete': if (!$gateway->remove_payment_token($user_id, $token)) { /* translators: Payment method as in a specific credit card, e-check or bank account */ SV_WC_Helper::wc_add_notice(esc_html__('Error removing payment method', 'woocommerce-plugin-framework'), 'error'); } else { /* translators: Payment method as in a specific credit card, e-check or bank account */ SV_WC_Helper::wc_add_notice(esc_html__('Payment method deleted.', 'woocommerce-plugin-framework')); } break; // set default payment method // set default payment method case 'make-default': $gateway->set_default_payment_token($user_id, $token); /* translators: Payment method as in a specific credit card, e-check or bank account */ SV_WC_Helper::wc_add_notice(esc_html__('Default payment method updated.', 'woocommerce-plugin-framework')); break; // custom actions // custom actions default: /** * My Payment Methods Custom Action. * * Fired when a custom action is requested for a payment method (e.g. other than delete/make default) * * @since 4.0.0 * @param \SV_WC_Payment_Gateway_My_Payment_Methods $this instance */ do_action('wc_' . $this->get_plugin()->get_id() . '_my_payment_methods_action_' . sanitize_title($action), $this); break; } $this->redirect_to_my_account(); } }
/** * Adds debug messages to the page as a WC message/error, and/or to the WC Error log * * @since 1.0.0 * @param string $message message to add * @param string $type how to add the message, options are: * 'message' (styled as WC message), 'error' (styled as WC Error) */ public function add_debug_message($message, $type = 'message') { // do nothing when debug mode is off or no message if ('off' == $this->debug_off() || !$message) { return; } // add log message to WC logger if log/both is enabled if ($this->debug_log()) { $this->get_plugin()->log($message, $this->get_id()); } // avoid adding notices when performing refunds, these occur in the admin as an Ajax call, so checking the current filter // is the only reliably way to do so if (in_array('wp_ajax_woocommerce_refund_line_items', $GLOBALS['wp_current_filter'])) { return; } // add debug message to woocommerce->errors/messages if checkout or both is enabled, the admin/Ajax check ensures capture charge transactions aren't logged as notices to the front end if (($this->debug_checkout() || 'error' === $type && $this->is_test_environment()) && (!is_admin() || is_ajax())) { if ('message' === $type) { SV_WC_Helper::wc_add_notice(str_replace("\n", "<br/>", htmlspecialchars($message)), 'notice'); } else { // defaults to error message SV_WC_Helper::wc_add_notice(str_replace("\n", "<br/>", htmlspecialchars($message)), 'error'); } } }
/** * Entry method for the Add Payment Method feature flow. Note this is *not* * stubbed in the WC_Payment_Gateway abstract class, but is called if the * gateway declares support for it. * * @since 4.0.0 */ public function add_payment_method() { assert($this->supports_add_payment_method()); $order = $this->get_order_for_add_payment_method(); try { $result = $this->do_add_payment_method_transaction($order); } catch (SV_WC_Plugin_Exception $e) { $result = array('message' => sprintf(esc_html__('Oops, adding your new payment method failed: %s', 'woocommerce-plugin-framework'), $e->getMessage()), 'success' => false); } SV_WC_Helper::wc_add_notice($result['message'], $result['success'] ? 'success' : 'error'); // if successful, redirect to the newly added method if ($result['success']) { // if this is WooCommerce 2.5.5 or older, redirect to the My Account page if (SV_WC_Plugin_Compatibility::is_wc_version_lt_2_6()) { $redirect_url = wc_get_page_permalink('myaccount'); // otherwise, redirect to the Payment Methods page (WC 2.6+) } else { $redirect_url = wc_get_account_endpoint_url('payment-methods'); } // otherwise, back to the Add Payment Method page } else { $redirect_url = wc_get_endpoint_url('add-payment-method'); } wp_safe_redirect($redirect_url); exit; }
/** * Write the given row to the CSV * * This is abstracted so the provided data can be matched to the CSV headers set and the CSV delimiter and * enclosure can be controlled from a single method * * @since 3.0 * @param array $row */ private function write($row) { $data = array(); foreach ($this->headers as $header_key => $_) { if (!isset($row[$header_key])) { $row[$header_key] = ''; } // strict string comparison, as values like '0' are valid $value = '' !== $row[$header_key] ? $row[$header_key] : ''; // escape leading equals sign character with a single quote to prevent CSV injections, see http://www.contextis.com/resources/blog/comma-separated-vulnerabilities/ if (SV_WC_Helper::str_starts_with($value, '=')) { $value = "'" . $value; } $data[] = $value; } fputcsv($this->stream, $data, $this->delimiter, $this->enclosure); }
/** * Helper method to return the item description, which is composed of item * meta flattened into a comma-separated string, if available. Otherwise the * product SKU is included. * * The description is automatically truncated to the 127 char limit. * * @since 3.0.0 * @param array $item cart or order item * @param \WC_Product $product product data * @return string */ private function get_item_description($item, $product) { if (empty($item['item_meta'])) { // cart item $item_desc = WC()->cart->get_item_data($item, true); $item_desc = str_replace("\n", ', ', rtrim($item_desc)); } else { // order item $item_meta = new WC_Order_Item_Meta($item['item_meta']); $item_meta = SV_WC_Plugin_Compatibility::get_formatted_item_meta($item_meta); if (!empty($item_meta)) { $item_desc = array(); foreach ($item_meta as $meta) { $item_desc[] = sprintf('%s: %s', $meta['label'], $meta['value']); } $item_desc = implode(', ', $item_desc); } else { $item_desc = is_callable(array($product, 'get_sku')) && $product->get_sku() ? sprintf(__('SKU: %s', WC_PayPal_Express::TEXT_DOMAIN), $product->get_sku()) : null; } } return SV_WC_Helper::str_truncate($item_desc, 127); }
/** * Add any Intuit QBMS specific payment and transaction information as * class members of WC_Order instance. Added members can include: * * $order->trans_request_id - an application-supplied value that identifies the transaction * $order->intuit_qbms_test_condition - a convenience for testing error conditions while in test mode * * @since 1.0 * @see WC_Gateway_Intuit_QBMS::get_order() * @param int $order_id order ID being processed * @return WC_Order object with payment and transaction information attached */ public function get_order($order_id) { // add common order members $order = parent::get_order($order_id); // add intuit credit card-specific order members // a convenience for testing error conditions while in test mode, this is passed as the NameOnCard if ($this->is_environment('test') && SV_WC_Helper::get_post('wc-intuit-qbms-test-condition')) { $order->intuit_qbms_test_condition = SV_WC_Helper::get_post('wc-intuit-qbms-test-condition'); } return $order; }
/** * Add custom contribution columns * * @since 1.0.0 * @param array $columns * @return array */ public function add_custom_contributions_columns(array $columns) { $columns = SV_WC_Helper::array_insert_after($columns, 'cb', array('type' => __('Type', WC_Product_Reviews_Pro::TEXT_DOMAIN))); $columns = SV_WC_Helper::array_insert_after($columns, 'comment', array('votes' => __('Votes', WC_Product_Reviews_Pro::TEXT_DOMAIN))); $columns = SV_WC_Helper::array_insert_after($columns, 'votes', array('flags' => _x('Flags', 'number of times contribution has been flagged', WC_Product_Reviews_Pro::TEXT_DOMAIN))); return $columns; }
/** * Save the Admin User Edit screen payment token fields, if any * * @see SV_WC_Payment_Gateway_Plugin::maybe_add_user_profile_tokenization_fields() * @param SV_WC_Payment_Gateway $gateway the gateway instance * @param int $user_id identifies the user to save the settings for */ protected function save_user_profile_tokenization_fields($gateway, $user_id) { foreach (array_keys($gateway->get_environments()) as $environment_id) { // deleting any payment tokens? $payment_tokens_deleted_name = 'wc_' . $gateway->get_id() . '_payment_tokens_deleted_' . $environment_id; $delete_payment_tokens = SV_WC_Helper::get_post($payment_tokens_deleted_name) ? explode(',', trim(SV_WC_Helper::get_post($payment_tokens_deleted_name), ',')) : array(); // see whether we're deleting any foreach ($delete_payment_tokens as $token) { $gateway->remove_payment_token($user_id, $token, $environment_id); } // adding a new payment token? $payment_token_name = 'wc_' . $gateway->get_id() . '_payment_token_' . $environment_id; if (SV_WC_Helper::get_post($payment_token_name)) { $exp_date = explode('/', SV_WC_Helper::get_post('wc_' . $gateway->get_id() . '_payment_token_exp_date_' . $environment_id)); // add the new payment token, making it active if this is the first card $gateway->add_payment_token($user_id, $gateway->build_payment_token(SV_WC_Helper::get_post($payment_token_name), array('type' => $gateway->is_credit_card_gateway() ? 'credit_card' : 'check', 'card_type' => SV_WC_Helper::get_post('wc_' . $gateway->get_id() . '_payment_token_type_' . $environment_id), 'last_four' => SV_WC_Helper::get_post('wc_' . $gateway->get_id() . '_payment_token_last_four_' . $environment_id), 'exp_month' => count($exp_date) > 1 ? sprintf('%02s', $exp_date[0]) : null, 'exp_year' => count($exp_date) > 1 ? $exp_date[1] : null))); } } }
/** * Add payment data to the order. * * @since 2.4.0 * @param int $order_id the order ID * @return \WC_Order */ public function get_order($order_id) { $order = parent::get_order($order_id); if ($this->is_accept_js_enabled() && ($nonce = SV_WC_Helper::get_post('wc-' . $this->get_id_dasherized() . '-payment-nonce'))) { // expiry month/year list($order->payment->exp_month, $order->payment->exp_year) = array_map('trim', explode('/', SV_WC_Helper::get_post('wc-' . $this->get_id_dasherized() . '-expiry'))); // card data $order->payment->card_type = SV_WC_Helper::get_post('wc-' . $this->get_id_dasherized() . '-card-type'); $order->payment->account_number = $order->payment->last_four = SV_WC_Helper::get_post('wc-' . $this->get_id_dasherized() . '-last-four'); // nonce data $order->payment->descriptor = SV_WC_Helper::get_post('wc-' . $this->get_id_dasherized() . '-payment-descriptor'); $order->payment->nonce = $nonce; } return $order; }
/** * Sanitize address fields by removing invalid UTF-8, direct response delimiter, * and truncate to field length limits * * @since 2.0.5 * @param string $field_name address field name * @param array $field field data * @return string sanitized field */ protected function sanitize_address_field($field_name, $field) { if ('phone' === $field_name) { $value = preg_replace('/\\D/', '', $field['value']); } else { // authorize.net claims to support unicode, but not all code points yet. // Unrecognized code points will display in their control panel with question marks $value = SV_WC_Helper::str_to_sane_utf8($field['value']); } // remove any usages of our hybrid direct response delimiter so as to not break response parsing // see WC_Authorize_Net_CIM_API_Profile_Response::parse_direct_response() $value = str_replace(':|:', '', $value); // truncate to field limits return $value ? SV_WC_Helper::str_truncate($value, $field['limit']) : null; }
/** * Search for taxonomy terms and echo json * * @since 1.0.0 */ public function json_search_terms() { check_ajax_referer('search-terms', 'security'); $term = (string) wc_clean(stripslashes(SV_WC_Helper::get_request('term'))); $taxonomy = (string) wc_clean(SV_WC_Helper::get_request('taxonomy')); if (empty($term) || empty($taxonomy)) { die; } if (is_numeric($term)) { $args = array('hide_empty' => false, 'include' => array(0, $term)); } else { $args = array('hide_empty' => false, 'search' => $term); } $terms = get_terms(array($taxonomy), $args); $found_terms = array(); if ($terms) { foreach ($terms as $term) { $found_terms[$term->term_id] = $term->name; } } /** * Filter terms found for JSON (AJAX) search * * @since 1.0.0 * @param array $found_terms Array of the found terms */ $found_terms = apply_filters('wc_memberships_json_search_found_terms', $found_terms); wp_send_json($found_terms); }
/** Create a void for the given $order * * @since 2.0.0 * @param WC_Order $order order object */ public function create_void(WC_Order $order) { $this->order = $order; $this->request_data = array('refId' => $order->id, 'transactionRequest' => array('transactionType' => self::VOID, 'refTransId' => $order->refund->trans_id, 'order' => array('invoiceNumber' => ltrim($this->order->get_order_number(), _x('#', 'hash before the order number', 'woocommerce-gateway-authorize-net-cim')), 'description' => SV_WC_Helper::str_truncate($this->order->refund->reason, 255)))); }
/** * Locates the WooCommerce template files from our templates directory * * @since 1.0.0 * @param string $template Already found template * @param string $template_name Searchable template name * @param string $template_path Template path * @return string Search result for the template */ public function locate_template($template, $template_name, $template_path) { // Only keep looking if no custom theme template was found or if // a default WooCommerce template was found. if (!$template || SV_WC_Helper::str_starts_with($template, WC()->plugin_path())) { // Set the path to our templates directory $plugin_path = $this->get_plugin_path() . '/templates/'; // If a template is found, make it so if (is_readable($plugin_path . $template_name)) { $template = $plugin_path . $template_name; } } return $template; }
/** * Process subscription renewal * * @since 4.1.0 * @param float $amount_to_charge subscription amount to charge, could include multiple renewals if they've previously failed and the admin has enabled it * @param WC_Order $order original order containing the subscription * @param int $product_id the subscription product id */ public function process_renewal_payment_1_5($amount_to_charge, $order, $product_id) { try { // set order defaults $order = $this->get_gateway()->get_order($order->id); // zero-dollar subscription renewal. weird, but apparently it happens if (0 == $amount_to_charge) { // add order note $order->add_order_note(sprintf(_x('%s0 Subscription Renewal Approved', 'Supports direct credit card subscriptions', $this->get_gateway()->get_text_domain()), get_woocommerce_currency_symbol())); // update subscription WC_Subscriptions_Manager::process_subscription_payments_on_order($order, $product_id); return; } // set the amount to charge, ensuring that we have a decimal point, even if it's 1.00 $order->payment_total = SV_WC_Helper::number_format($amount_to_charge); // required if (!$order->payment->token || !$order->get_user_id()) { throw new SV_WC_Payment_Gateway_Exception('Subscription Renewal: Payment Token or User ID is missing/invalid.'); } // get the token, we've already verified it's good $token = $this->get_gateway()->get_payment_token($order->get_user_id(), $order->payment->token); // perform the transaction if ($this->get_gateway()->is_credit_card_gateway()) { if ($this->get_gateway()->perform_credit_card_charge()) { $response = $this->get_gateway()->get_api()->credit_card_charge($order); } else { $response = $this->get_gateway()->get_api()->credit_card_authorization($order); } } elseif ($this->get_gateway()->is_echeck_gateway()) { $response = $this->get_gateway()->get_api()->check_debit($order); } // check for success if ($response->transaction_approved()) { // order note based on gateway type if ($this->get_gateway()->is_credit_card_gateway()) { $message = sprintf(_x('%s %s Subscription Renewal Payment Approved: %s ending in %s (expires %s)', 'Supports direct credit card subscriptions', $this->get_gateway()->get_text_domain()), $this->get_gateway()->get_method_title(), $this->get_gateway()->perform_credit_card_authorization() ? 'Authorization' : 'Charge', $token->get_card_type() ? $token->get_type_full() : 'card', $token->get_last_four(), $token->get_exp_month() . '/' . $token->get_exp_year()); } elseif ($this->get_gateway()->is_echeck_gateway()) { // there may or may not be an account type (checking/savings) available, which is fine $message = sprintf(_x('%s Check Subscription Renewal Payment Approved: %s account ending in %s', 'Supports direct cheque subscriptions', $this->get_gateway()->get_text_domain()), $this->get_gateway()->get_method_title(), $token->get_account_type(), $token->get_last_four()); } // add order note $order->add_order_note($message); // set transaction ID manually, WCS 1.5.x calls WC_Order::payment_complete() internally if ($response->get_transaction_id()) { update_post_meta($order->id, '_transaction_id', $response->get_transaction_id()); } // update subscription WC_Subscriptions_Manager::process_subscription_payments_on_order($order, $product_id); } else { // failure throw new SV_WC_Payment_Gateway_Exception(sprintf('%s: %s', $response->get_status_code(), $response->get_status_message())); } } catch (SV_WC_Plugin_Exception $e) { // don't mark the order as failed, Subscriptions will handle marking the renewal order as failed $order->add_order_note(sprintf(_x('%s Renewal Payment Failed (%s)', $this->get_gateway()->get_text_domain()), $this->get_gateway()->get_method_title(), $e->getMessage())); // update subscription WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } }
/** * Returns true if the current page is the admin configuration page for the * gateway with class name $gateway_class_name * * Temporary home for this function, until all payment gateways are brought into the frameworked fold * * @since 3.0.0 * @param string $gateway_class_name the gateway class name * @return boolean true if the current page is the admin configuration page for the gateway */ public static function is_payment_gateway_configuration_page($gateway_class_name) { return 'wc-settings' == SV_WC_Helper::get_request('page') && 'checkout' == SV_WC_Helper::get_request('tab') && strtolower($gateway_class_name) == SV_WC_Helper::get_request('section'); }
/** * Maybe force TLS v1.2 requests. * * @since 4.4.0 */ public function set_tls_1_2_request($handle, $r, $url) { if (!SV_WC_Helper::str_starts_with($url, 'https://')) { return; } $versions = curl_version(); $curl_version = $versions['version']; // Get the SSL details list($ssl_type, $ssl_version) = explode('/', $versions['ssl_version']); $ssl_version = substr($ssl_version, 0, -1); // If cURL and/or OpenSSL aren't up to the challenge, bail if (!version_compare($curl_version, '7.34.0', '>=') || 'OpenSSL' === $ssl_type && !version_compare($ssl_version, '1.0.1', '>=')) { return; } curl_setopt($handle, CURLOPT_SSLVERSION, 6); }
/** * Render dropdowns for any filterable checkout add-ons * * @since 1.0 */ public function restrict_orders() { global $typenow; if ('shop_order' != $typenow) { return; } $javascript = ''; foreach (wc_checkout_add_ons()->get_add_ons() as $add_on) { // if the add-on is filterable if ($add_on->is_filterable()) { if ($add_on->has_options()) { // filterable multi item add-on field (select, multiselect, radio, checkbox), provide a dropdown ?> <select name="<?php echo esc_attr($add_on->get_key()); ?> " id="<?php echo esc_attr($add_on->get_key()); ?> " class="wc-enhanced-select" data-placeholder="<?php echo __("Show all ", WC_Checkout_Add_Ons::TEXT_DOMAIN) . $add_on->name; ?> " data-allow_clear="true" style="min-width:200px;"> <option value=""></option> <?php foreach ($add_on->get_options() as $option) { if ('' === $option['value'] && '' === $option['label']) { continue; } echo '<option value="' . $option['value'] . '" ' . (isset($_GET[$add_on->get_key()]) ? selected($option['value'], $_GET[$add_on->get_key()]) : '') . '>' . __($option['label'], WC_Checkout_Add_Ons::TEXT_DOMAIN) . '</option>'; } ?> </select> <?php $javascript .= SV_WC_Plugin_Compatibility::is_wc_version_lt_2_3() ? "if ( \$().chosen ) { \$('select#" . $add_on->get_key() . "').chosen( { allow_single_deselect: true } ); }" : ""; } elseif ($add_on->type == 'text') { if (SV_WC_Plugin_Compatibility::is_wc_version_gte_2_3()) { ?> <input type="hidden" class="sv-wc-enhanced-search" name="<?php echo esc_attr($add_on->get_key()); ?> " style="min-width:200px;" data-placeholder="<?php echo __("Show all ", WC_Checkout_Add_Ons::TEXT_DOMAIN) . $add_on->label; ?> " data-selected="<?php echo empty($_GET[$add_on->get_key()]) ? '' : $_GET[$add_on->get_key()]; ?> " value="<?php echo empty($_GET[$add_on->get_key()]) ? '' : esc_attr($_GET[$add_on->get_key()]); ?> " data-allow_clear="true" data-action="wc_checkout_add_ons_json_search_field" data-nonce="<?php echo wp_create_nonce('search-field'); ?> " data-request_data = "<?php echo esc_attr(json_encode(array('add_on_id' => $add_on->id, 'default' => addslashes(__('Show all ', WC_Checkout_Add_Ons::TEXT_DOMAIN) . $add_on->name)))); ?> " /> <?php SV_WC_Helper::render_select2_ajax(); } else { // search box dropdown ?> <select id="<?php echo esc_attr($add_on->get_key()); ?> " name="<?php echo esc_attr($add_on->get_key()); ?> " data-placeholder="<?php echo __("Show all ", WC_Checkout_Add_Ons::TEXT_DOMAIN) . $add_on->name; ?> " data-allow_clear="true" style="min-width:200px;"> <option value=""></option> <?php if (!empty($_GET[$add_on->get_key()])) { echo '<option value="' . esc_attr($_GET[$add_on->get_key()]) . '" '; selected(1, 1); echo '>' . $_GET[$add_on->get_key()] . '</option>'; } ?> </select> <?php $javascript .= "\n\t\t\t\t\t\t\t\$( 'select#" . $add_on->get_key() . "' ).ajaxChosen( {\n\t\t\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\t\t\turl: '" . admin_url('admin-ajax.php') . "',\n\t\t\t\t\t\t\t\tdataType: 'json',\n\t\t\t\t\t\t\t\tafterTypeDelay: '100',\n\t\t\t\t\t\t\t\tminTermLength: 1,\n\t\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t\taction: 'wc_checkout_add_ons_json_search_field',\n\t\t\t\t\t\t\t\t\tsecurity: '" . wp_create_nonce("search-field") . "',\n\t\t\t\t\t\t\t\t\trequest_data: { 'add_on_id' : '" . $add_on->id . "', 'default' : '" . addslashes(__('Show all ', WC_Checkout_Add_Ons::TEXT_DOMAIN) . $add_on->name) . "' },\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}, function ( data ) {\n\n\t\t\t\t\t\t\t\tvar terms = {};\n\n\t\t\t\t\t\t\t\t\$.each( data, function ( i, val ) {\n\t\t\t\t\t\t\t\t\tterms[ i ] = val;\n\t\t\t\t\t\t\t\t} );\n\n\t\t\t\t\t\t\t\treturn terms;\n\t\t\t\t\t\t\t}, { allow_single_deselect: true } );"; } } elseif ($add_on->type == 'checkbox' || $add_on->type == 'file') { $checked = isset($_GET[$add_on->get_key()]) && $_GET[$add_on->get_key()]; ?> <label class="wc-checkout-add-on-checkbox-filter"> <input type="checkbox" id="<?php echo esc_attr($add_on->get_key()); ?> " name="<?php echo esc_attr($add_on->get_key()); ?> " value="1" <?php checked($checked); ?> > <?php echo $add_on->name; ?> </label> <?php } } } // filterable dropdown javascript wc_enqueue_js($javascript); }
/** * Process subscription renewal * * @since 1.4 * @param float $amount_to_charge subscription amount to charge, could include * multiple renewals if they've previously failed and the admin * has enabled it * @param WC_Order $order original order containing the subscription * @param int $product_id the ID of the subscription product */ public function process_renewal_payment($amount_to_charge, $order, $product_id = null) { require_once 'class-wc-realex-api.php'; $realex_subscription_count = 0; if (is_numeric($order->realex_subscription_count) && $order->realex_subscription_count) { $realex_subscription_count = $order->realex_subscription_count; } // increment the subscription count so we don't get order number clashes $realex_subscription_count++; update_post_meta($order->id, '_realex_subscription_count', $realex_subscription_count); // set custom class member used by the realex gateway $order->payment_total = SV_WC_Helper::number_format($amount_to_charge); // zero-dollar subscription renewal. weird, but apparently it happens -- only applicable to Subs 1.5.x if (!SV_WC_Plugin_Compatibility::is_wc_subscriptions_version_gte_2_0()) { if (0 == $order->payment_total) { // add order note $order->add_order_note(sprintf(__('%s0 Subscription Renewal Approved', 'woocommerce-gateway-realex'), get_woocommerce_currency_symbol())); // update subscription WC_Subscriptions_Manager::process_subscription_payments_on_order($order, $product_id); return; } } // This order is missing a tokenized card, lets see whether there's one available for the customer if (!get_post_meta($order->id, '_realex_cardref', true)) { $credit_cards = get_user_meta($order->get_user_id(), 'woocommerce_realex_cc', true); if (is_array($credit_cards)) { $card_ref = (object) current($credit_cards); $card_ref = $card_ref->ref; update_post_meta($order->id, '_realex_cardref', $card_ref); if (SV_WC_Plugin_Compatibility::is_wc_subscriptions_version_gte_2_0()) { foreach (wcs_get_subscriptions_for_renewal_order($order) as $subscription) { update_post_meta($subscription->id, '_realex_cardref', $card_ref); } } } } // create the realex api client $realex_client = new Realex_API($this->get_endpoint_url(), $this->get_realvault_endpoint_url(), $this->get_shared_secret()); // create the customer/cc tokens, and authorize the initial payment amount, if any $response = $this->authorize($realex_client, $order); if ($response && '00' == $response->result) { // add order note $order->add_order_note(sprintf(__('Credit Card Subscription Renewal Payment Approved (Payment Reference: %s) ', 'woocommerce-gateway-realex'), $response->pasref)); // update subscription if (SV_WC_Plugin_Compatibility::is_wc_subscriptions_version_gte_2_0()) { $order->payment_complete((string) $response->pasref); } else { WC_Subscriptions_Manager::process_subscription_payments_on_order($order, $product_id); } } else { // generate the result message $message = __('Credit Card Subscription Renewal Payment Failed', 'woocommerce-gateway-realex'); /* translators: Placeholders: %1$s - result, %2$s - result message */ if ($response) { $message .= sprintf(__(' (Result: %1$s - "%2$s").', 'woocommerce-gateway-realex'), $response->result, $response->message); } $order->add_order_note($message); // update subscription if (!SV_WC_Plugin_Compatibility::is_wc_subscriptions_version_gte_2_0()) { WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order, $product_id); } } }
/** * Handle payment methods actions, e.g. deleting a payment method or setting * one as default * * @since 4.0.0 */ public function handle_payment_method_actions() { if (!$this->has_tokens) { return; } $token = isset($_GET['wc-' . $this->get_plugin()->get_id_dasherized() . '-token']) ? trim($_GET['wc-' . $this->get_plugin()->get_id_dasherized() . '-token']) : ''; $action = isset($_GET['wc-' . $this->get_plugin()->get_id_dasherized() . '-action']) ? $_GET['wc-' . $this->get_plugin()->get_id_dasherized() . '-action'] : ''; // process payment method actions if ($token && $action && !empty($_GET['_wpnonce']) && is_user_logged_in()) { // security check if (false === wp_verify_nonce($_GET['_wpnonce'], 'wc-' . $this->get_plugin()->get_id_dasherized() . '-token-action')) { SV_WC_Helper::wc_add_notice(_x('Oops, you took too long, please try again.', 'Supports my payment method feature', $this->get_plugin()->get_text_domain()), 'error'); $this->redirect_to_my_account(); } // current logged in user $user_id = get_current_user_id(); $gateway = $this->get_plugin()->get_gateway_from_token($user_id, $token); // couldn't find an associated gateway for that token if (!is_object($gateway)) { SV_WC_Helper::wc_add_notice(_x('There was an error with your request, please try again.', 'Supports my payment method feature', $this->get_plugin()->get_text_domain()), 'error'); $this->redirect_to_my_account(); } switch ($action) { // handle deletion case 'delete': if (!$gateway->remove_payment_token($user_id, $token)) { SV_WC_Helper::wc_add_notice(_x('Error removing payment method', 'Supports my payment method feature', $this->get_plugin()->get_text_domain()), 'error'); } else { SV_WC_Helper::wc_add_notice(_x('Payment method deleted.', 'Supports my payment method feature', $this->get_plugin()->get_text_domain())); } break; // set default payment method // set default payment method case 'make-default': $gateway->set_default_payment_token($user_id, $token); SV_WC_Helper::wc_add_notice(_x('Default payment method updated.', 'Supports my payment method feature', $this->get_plugin()->get_text_domain())); break; // custom actions // custom actions default: do_action('wc_' . $this->get_plugin()->get_id() . '_my_payment_methods_action_' . sanitize_title($action), $this); break; } $this->redirect_to_my_account(); } }
/** * Add 'Social Profiles' column to the Users admin table * * @since 1.3.0 * @param array $columns user admin table columns * @return array $columns columns array with 'Social Profiles' */ public function add_user_columns($columns) { return SV_WC_Helper::array_insert_after($columns, 'email', array('wc_social_login_profiles' => __('Social Profiles', WC_Social_Login::TEXT_DOMAIN))); }
/** * Entry method for the Add Payment Method feature flow. Note this is *not* * stubbed in the WC_Payment_Gateway abstract class, but is called if the * gateway declares support for it. * * @since 4.0.0 */ public function add_payment_method() { assert($this->supports_add_payment_method()); $order = $this->get_order_for_add_payment_method(); try { $result = $this->do_add_payment_method_transaction($order); } catch (SV_WC_Plugin_Exception $e) { $result = array('message' => sprintf(__('Oops, adding your new payment method failed: %s', $this->text_domain), $e->getMessage()), 'success' => false); } SV_WC_Helper::wc_add_notice($result['message'], $result['success'] ? 'success' : 'error'); // redirect to my account on success, or back to Add Payment Method screen on failure so user can try again wp_safe_redirect($result['success'] ? SV_WC_Plugin_Compatibility::wc_get_page_permalink('myaccount') : wc_get_endpoint_url('add-payment-method')); exit; }
/** * * @dataProvider provider_test_str_starts_with_false */ public function test_str_starts_with_false($haystack, $needle) { $this->assertFalse(\SV_WC_Helper::str_starts_with($haystack, $needle)); }
/** * Helper function to determine whether a plugin is active * * @since 2.0.0 * @param string $plugin_name plugin name, as the plugin-filename.php * @return boolean true if the named plugin is installed and active */ public function is_plugin_active($plugin_name) { $active_plugins = (array) get_option('active_plugins', array()); if (is_multisite()) { $active_plugins = array_merge($active_plugins, array_keys(get_site_option('active_sitewide_plugins', array()))); } $plugin_filenames = array(); foreach ($active_plugins as $plugin) { if (SV_WC_Helper::str_exists($plugin, '/')) { // normal plugin name (plugin-dir/plugin-filename.php) list(, $filename) = explode('/', $plugin); } else { // no directory, just plugin file $filename = $plugin; } $plugin_filenames[] = $filename; } return in_array($plugin_name, $plugin_filenames); }
/** * Upgrade to the currently installed version * * @since 2.0.0 * @param string $installed_version currently installed version */ public function upgrade($installed_version) { // upgrade to 2.0.0 if (version_compare($installed_version, '2.0.0', '<')) { $this->log('Starting upgrade to 2.0.0'); /** Upgrade settings */ $old_cc_settings = get_option('woocommerce_authorize_net_cim_settings'); $old_echeck_settings = get_option('woocommerce_authorize_net_cim_echeck_settings'); if ($old_cc_settings) { // prior to 2.0.0, there was no settings for tokenization (always on) and enable_customer_decline_messages. // eCheck settings were inherited from the credit card gateway by default // credit card $new_cc_settings = array('enabled' => isset($old_cc_settings['enabled']) && 'yes' === $old_cc_settings['enabled'] ? 'yes' : 'no', 'title' => !empty($old_cc_settings['title']) ? $old_cc_settings['title'] : 'Credit Card', 'description' => !empty($old_cc_settings['description']) ? $old_cc_settings['description'] : 'Pay securely using your credit card.', 'enable_csc' => isset($old_cc_settings['require_cvv']) && 'yes' === $old_cc_settings['require_cvv'] ? 'yes' : 'no', 'transaction_type' => isset($old_cc_settings['transaction_type']) && 'auth_capture' === $old_cc_settings['transaction_type'] ? 'charge' : 'authorization', 'card_types' => !empty($old_cc_settings['card_types']) ? $old_cc_settings['card_types'] : array('VISA', 'MC', 'AMEX', 'DISC'), 'tokenization' => 'yes', 'environment' => isset($old_cc_settings['test_mode']) && 'yes' === $old_cc_settings['test_mode'] ? 'test' : 'production', 'inherit_settings' => 'no', 'api_login_id' => !empty($old_cc_settings['api_login_id']) ? $old_cc_settings['api_login_id'] : '', 'api_transaction_key' => !empty($old_cc_settings['api_transaction_key']) ? $old_cc_settings['api_transaction_key'] : '', 'test_api_login_id' => !empty($old_cc_settings['test_api_login_id']) ? $old_cc_settings['test_api_login_id'] : '', 'test_api_transaction_key' => !empty($old_cc_settings['test_api_transaction_key']) ? $old_cc_settings['test_api_transaction_key'] : '', 'enable_customer_decline_messages' => 'no', 'debug_mode' => !empty($old_cc_settings['debug_mode']) ? $old_cc_settings['debug_mode'] : 'off'); // eCheck $new_echeck_settings = array('enabled' => isset($old_echeck_settings['enabled']) && 'yes' === $old_echeck_settings['enabled'] ? 'yes' : 'no', 'title' => !empty($old_echeck_settings['title']) ? $old_echeck_settings['title'] : 'eCheck', 'description' => !empty($old_echeck_settings['description']) ? $old_echeck_settings['description'] : 'Pay securely using your checking account.', 'tokenization' => 'yes', 'environment' => $new_cc_settings['environment'], 'inherit_settings' => 'yes', 'api_login_id' => '', 'api_transaction_key' => '', 'test_api_login_id' => '', 'test_api_transaction_key' => '', 'enable_customer_decline_messages' => 'no', 'debug_mode' => $new_cc_settings['debug_mode']); // save new settings, remove old ones update_option('woocommerce_authorize_net_cim_credit_card_settings', $new_cc_settings); update_option('woocommerce_authorize_net_cim_echeck_settings', $new_echeck_settings); delete_option('woocommerce_authorize_net_cim_settings'); $this->log('Settings upgraded.'); } /** Update meta key for customer profile ID and shipping profile ID */ global $wpdb; // old key: _wc_authorize_net_cim_profile_id // new key: wc_authorize_net_cim_customer_profile_id // note that we don't know on a per-user basis what environment the customer ID was set in, so we assume production, just to be safe $rows = $wpdb->update($wpdb->usermeta, array('meta_key' => 'wc_authorize_net_cim_customer_profile_id'), array('meta_key' => '_wc_authorize_net_cim_profile_id')); $this->log(sprintf('%d users updated for customer profile ID.', $rows)); // old key: _wc_authorize_net_cim_shipping_profile_id // new key: wc_authorize_net_cim_shipping_address_id $rows = $wpdb->update($wpdb->usermeta, array('meta_key' => 'wc_authorize_net_cim_shipping_address_id'), array('meta_key' => '_wc_authorize_net_cim_shipping_profile_id')); $this->log(sprintf('%d users updated for shipping address ID', $rows)); /** Update meta values for order payment method & recurring payment method */ // meta key: _payment_method // old value: authorize_net_cim // new value: authorize_net_cim_credit_card // note that the eCheck method has not changed from 1.x to 2.x $rows = $wpdb->update($wpdb->postmeta, array('meta_value' => 'authorize_net_cim_credit_card'), array('meta_key' => '_payment_method', 'meta_value' => 'authorize_net_cim')); $this->log(sprintf('%d orders updated for payment method meta', $rows)); // meta key: _recurring_payment_method // old value: authorize_net_cim // new value: authorize_net_cim_credit_card $rows = $wpdb->update($wpdb->postmeta, array('meta_value' => 'authorize_net_cim_credit_card'), array('meta_key' => '_recurring_payment_method', 'meta_value' => 'authorize_net_cim')); $this->log(sprintf('%d orders updated for recurring payment method meta', $rows)); /** Convert payment profiles stored in legacy format to framework payment token format */ $this->log('Starting payment profile upgrade.'); $user_ids = $wpdb->get_col("SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = '_wc_authorize_net_cim_payment_profiles'"); if ($user_ids) { // iterate through each user with a payment profile foreach ($user_ids as $user_id) { $customer_profile_id = get_user_meta($user_id, 'wc_authorize_net_cim_customer_profile_id', true); $payment_profiles = get_user_meta($user_id, '_wc_authorize_net_cim_payment_profiles', true); $cc_tokens = $echeck_tokens = array(); // iterate through each payment profile foreach ($payment_profiles as $profile_id => $profile) { // bail if corrupted if (!$profile_id || empty($profile['type'])) { continue; } // parse expiry date if (!empty($profile['exp_date']) && SV_WC_Helper::str_exists($profile['exp_date'], '/')) { list($exp_month, $exp_year) = explode('/', $profile['exp_date']); } else { $exp_month = $exp_year = ''; } if ('Bank Account' === $profile['type']) { // eCheck tokens $echeck_tokens[$profile_id] = array('type' => 'echeck', 'last_four' => !empty($profile['last_four']) ? $profile['last_four'] : '', 'customer_profile_id' => $customer_profile_id, 'billing_hash' => '', 'payment_hash' => '', 'default' => !empty($profile['active']) && $profile['active'], 'exp_month' => $exp_month, 'exp_year' => $exp_year); } else { // parse card type switch ($profile['type']) { case 'Visa': $card_type = 'visa'; break; case 'American Express': $card_type = 'amex'; break; case 'MasterCard': $card_type = 'mc'; break; case 'Discover': $card_type = 'disc'; break; case 'Diners Club': $card_type = 'diners'; break; case 'JCB': $card_type = 'jcb'; break; default: $card_type = ''; } // credit card tokens $cc_tokens[$profile_id] = array('type' => 'credit_card', 'last_four' => !empty($profile['last_four']) ? $profile['last_four'] : '', 'customer_profile_id' => $customer_profile_id, 'billing_hash' => '', 'payment_hash' => '', 'default' => !empty($profile['active']) && $profile['active'], 'card_type' => $card_type, 'exp_month' => $exp_month, 'exp_year' => $exp_year); } } // update credit card tokens if (!empty($cc_tokens)) { update_user_meta($user_id, '_wc_authorize_net_cim_credit_card_payment_tokens', $cc_tokens); } // update eCheck tokens if (!empty($echeck_tokens)) { update_user_meta($user_id, '_wc_authorize_net_cim_echeck_payment_tokens', $echeck_tokens); } // save the legacy payment profiles in case we need them later update_user_meta($user_id, '_wc_authorize_net_cim_legacy_tokens', $payment_profiles); delete_user_meta($user_id, '_wc_authorize_net_cim_payment_profiles'); $this->log(sprintf('Converted payment profile for user ID: %s', $user_id)); } } $this->log('Completed payment profile upgrade.'); $this->log('Completed upgrade for 2.0.0'); } // TODO: remove _wc_authorize_net_cim_legacy_tokens meta in a future version @MR 2015-07 }