| @@ -1,12 +1,13 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace Payplug\PayplugWoocommerce\Gateway; |
| 4 | 4 | |
| 5 | -use Payplug\PayplugWoocommerce\PayplugWoocommerceHelper; | |
| 6 | -use Payplug\Authentication; | |
| 7 | 5 | use libphonenumber\PhoneNumberType; |
| 8 | 6 | use libphonenumber\PhoneNumberUtil; |
| 7 | +use Payplug\PayplugWoocommerce\Controller\PayplugGenericGateway; | |
| 8 | +use Payplug\PayplugWoocommerce\Gateway\Oney\OneyAccountMapper; | |
| 9 | +use Payplug\PayplugWoocommerce\PayplugWoocommerceHelper; | |
| 9 | 10 | |
| 10 | 11 | // Exit if accessed directly |
| 11 | 12 | if (!defined('ABSPATH')) { |
| 12 | 13 | exit; |
| @@ -13,40 +14,79 @@ | ||
| 13 | 14 | } |
| 14 | 15 | |
| 15 | 16 | /** |
| 16 | 17 | * PayPlug WooCommerce Gateway. |
| 17 | - * | |
| 18 | - * @package Payplug\PayplugWoocommerce\Gateway | |
| 19 | 18 | */ |
| 20 | -class PayplugGatewayOney3x extends PayplugGateway | |
| 19 | +class PayplugGatewayOney3x extends PayplugGenericGateway | |
| 21 | 20 | { |
| 22 | - const OPTION_NAME = "payplug_oney_config"; | |
| 23 | - const ONEY_UNAVAILABLE_CODE_COUNTRY_NOT_ALLOWED = 2; | |
| 24 | - const ONEY_UNAVAILABLE_CODE_CART_SIZE_TOO_HIGH = 3; | |
| 25 | - const ONEY_PRODUCT_QUANTITY_MAXIMUM = 1000; | |
| 21 | + public const ONEY_UNAVAILABLE_CODE_COUNTRY_NOT_ALLOWED = 2; | |
| 22 | + public const ONEY_UNAVAILABLE_CODE_CART_SIZE_TOO_HIGH = 3; | |
| 23 | + public const ONEY_DISALBE_CHECKOUT_OPTIONS = 4; | |
| 26 | 24 | |
| 25 | + public const ONEY_PRODUCT_QUANTITY_MAXIMUM = 1000; | |
| 26 | + | |
| 27 | 27 | protected $oney_response; |
| 28 | - protected $min_oney_price; | |
| 29 | - protected $max_oney_price; | |
| 30 | - protected $allowed_country_codes; | |
| 28 | + public const ENABLE_ON_TEST_MODE = true; | |
| 31 | 29 | |
| 30 | + public $allowed_country_codes = []; | |
| 31 | + | |
| 32 | 32 | public function __construct() |
| 33 | 33 | { |
| 34 | 34 | parent::__construct(); |
| 35 | - $this->id = 'oney_x3_with_fees'; | |
| 36 | - $this->method_title = _x('PayPlug Oney 3x', 'Gateway method title', 'payplug'); | |
| 35 | + $this->id = 'oney_x3_with_fees'; | |
| 36 | + $this->method_title = _x('PayPlug Oney 3x', 'Gateway method title', 'payplug'); | |
| 37 | 37 | $this->method_description = __('Enable PayPlug Oney 3x for your customers.', 'payplug'); |
| 38 | - $this->title = __('Pay by card in 3x with Oney', 'payplug'); | |
| 38 | + $this->title = __('Pay by card in 3x with Oney', 'payplug'); | |
| 39 | + $this->has_fields = true; | |
| 39 | 40 | |
| 40 | - if (is_admin()) { | |
| 41 | - wp_register_style('payplug-admin-oney', PAYPLUG_GATEWAY_PLUGIN_URL . 'assets/css/payplug-admin-oney.css', [], PAYPLUG_GATEWAY_VERSION); | |
| 42 | - wp_enqueue_style('payplug-admin-oney'); | |
| 41 | + add_action('woocommerce_order_item_add_action_buttons', [$this, 'oney_refund_text']); | |
| 42 | + add_action('woocommerce_after_checkout_validation', [$this, 'validate_checkout'], 10); | |
| 43 | + | |
| 44 | + self::set_oney_configuration(); | |
| 45 | + | |
| 46 | + // Unlike the other sub-gateways (Bancontact, AmericanExpress, PPRO methods...), Oney never | |
| 47 | + // synced $this->enabled with the plugin's own "active" toggle, so disabling Oney from the | |
| 48 | + // Payplug config left it shown as enabled on WooCommerce's native Payments settings page. | |
| 49 | + if (!$this->checkGateway()) { | |
| 50 | + $this->enabled = 'no'; | |
| 43 | 51 | } |
| 44 | 52 | |
| 45 | - add_action('woocommerce_order_item_add_action_buttons', [$this, 'oney_refund_text']); | |
| 53 | + if (is_checkout()) { | |
| 54 | + if ($this->check_oney_is_available() === self::ONEY_DISALBE_CHECKOUT_OPTIONS) { | |
| 55 | + $this->enabled = 'no'; | |
| 56 | + } | |
| 57 | + } | |
| 58 | + } | |
| 46 | 59 | |
| 47 | - if (is_checkout()) { | |
| 48 | - $account = PayplugWoocommerceHelper::get_account_data_from_options(true); | |
| 60 | + public function validate_checkout(): void | |
| 61 | + { | |
| 62 | + $posted_data = $this->get_post_data(); | |
| 63 | + | |
| 64 | + // Was a single malformed string (missing quotes around each id) instead of a 4-element | |
| 65 | + // array, so this guard never matched and the whole method was a silent no-op (PRE-3457 | |
| 66 | + // review / PRE-3457 follow-up fix). | |
| 67 | + $oney_gateway_ids = ['oney_x3_with_fees', 'oney_x4_with_fees', 'oney_x3_without_fees', 'oney_x4_without_fees']; | |
| 68 | + | |
| 69 | + if (in_array($posted_data['payment_method'] ?? '', $oney_gateway_ids, true)) { | |
| 70 | + if ($this->check_oney_is_available() === self::ONEY_UNAVAILABLE_CODE_COUNTRY_NOT_ALLOWED) { | |
| 71 | + throw new \Exception(__('Unavailable for the specified country.')); | |
| 72 | + } elseif ($this->check_oney_is_available() === self::ONEY_UNAVAILABLE_CODE_CART_SIZE_TOO_HIGH) { | |
| 73 | + throw new \Exception(sprintf(__('The payment with Oney is unavailable because you have more than %s items in your cart.', 'payplug'), self::ONEY_PRODUCT_QUANTITY_MAXIMUM)); | |
| 74 | + } elseif (!$this->check_oney_is_available()) { | |
| 75 | + throw new \Exception(sprintf(__('The total amount of your order should be between %s€ and %s€ to pay with Oney.', 'payplug'), $this->oney_thresholds_min, $this->oney_thresholds_max)); | |
| 76 | + } | |
| 77 | + } | |
| 78 | + } | |
| 79 | + | |
| 80 | + /** | |
| 81 | + * Set oney settings | |
| 82 | + * | |
| 83 | + * @return void | |
| 84 | + */ | |
| 85 | + private function set_oney_configuration(): void | |
| 86 | + { | |
| 87 | + $account = PayplugWoocommerceHelper::get_account_data_from_options(); | |
| 88 | + if ($account) { | |
| 49 | 89 | $oney_configuration = $account['configuration']['oney']; |
| 50 | 90 | $this->min_oney_price = $oney_configuration['min_amounts']['EUR'] / 100; |
| 51 | 91 | $this->max_oney_price = $oney_configuration['max_amounts']['EUR'] / 100; |
| 52 | 92 | $this->allowed_country_codes = $oney_configuration['allowed_countries']; |
| @@ -59,79 +99,169 @@ | ||
| 59 | 99 | * @return string |
| 60 | 100 | */ |
| 61 | 101 | public function get_icon() |
| 62 | 102 | { |
| 103 | + $disable = $this->check_oney_is_available() === true ? '' : 'disable-checkout-icons'; | |
| 104 | + $available_img = 'x3_with_fees.svg'; | |
| 63 | 105 | |
| 64 | - if ($this->check_oney_is_available() === true) { | |
| 65 | - $total_price = floatval(WC()->cart->total); | |
| 66 | - $this->oney_response = $this->api->simulate_oney_payment($total_price); | |
| 67 | - $currency = get_woocommerce_currency_symbol(get_option('woocommerce_currency')); | |
| 68 | - $f = function ($fn) { | |
| 69 | - return $fn; | |
| 70 | - }; | |
| 71 | - $this->description = <<<HTML | |
| 72 | - <p> | |
| 73 | - <div class="payplug-oney-flex"> | |
| 74 | - <div>{$f(__('Bring', 'payplug'))} :</div> | |
| 75 | - <div>{$this->oney_response['x3_with_fees']['down_payment_amount']} {$currency}</div> | |
| 76 | - </div> | |
| 77 | - <div class="payplug-oney-flex"> | |
| 78 | - <div>{$f(__('1st monthly payment', 'payplug'))} :</div> | |
| 79 | - <div>{$this->oney_response['x3_with_fees']['installments'][0]['amount']} {$currency}</div> | |
| 80 | - </div> | |
| 81 | - <div class="payplug-oney-flex"> | |
| 82 | - <div>{$f(__('2nd monthly payment', 'payplug'))} :</div> | |
| 83 | - <div>{$this->oney_response['x3_with_fees']['installments'][1]['amount']} {$currency}</div> | |
| 84 | - </div> | |
| 85 | - </p> | |
| 86 | -HTML; | |
| 87 | - $available_img = 'lg-3xoney-checkout.png'; | |
| 88 | - } else { | |
| 89 | - $available_img = 'lg-3xoney-checkout-disabled.png'; | |
| 90 | - } | |
| 91 | - | |
| 92 | 106 | $icons = apply_filters('payplug_payment_icons', [ |
| 93 | - 'payplug' => sprintf('<img src="%s" alt="Oney 3x" class="payplug-payment-icon" />', esc_url(PAYPLUG_GATEWAY_PLUGIN_URL . '/assets/images/' . $available_img)), | |
| 107 | + 'payplug' => sprintf('<img src="%s" alt="Oney 3x" class="payplug-payment-icon ' . $disable . '" />', esc_url(PAYPLUG_GATEWAY_PLUGIN_URL . '/assets/images/checkout/' . $available_img)), | |
| 94 | 108 | ]); |
| 95 | 109 | $icons_str = ''; |
| 96 | 110 | foreach ($icons as $icon) { |
| 97 | 111 | $icons_str .= $icon; |
| 98 | 112 | } |
| 113 | + | |
| 99 | 114 | return $icons_str; |
| 100 | 115 | } |
| 101 | 116 | |
| 102 | 117 | /** |
| 118 | + * Render the placeholder + config the official Oney widget uses to display the inline | |
| 119 | + * payment-schedule section for this specific gateway (checkout obligation). | |
| 120 | + */ | |
| 121 | + public function payment_fields(): void | |
| 122 | + { | |
| 123 | + if ($this->check_oney_is_available() !== true) { | |
| 124 | + // check_oney_is_available() sets $this->description with the reason Oney can't be used. | |
| 125 | + $description = $this->get_description(); | |
| 126 | + | |
| 127 | + if (!empty($description)) { | |
| 128 | + echo wpautop(wptexturize($description)); | |
| 129 | + } | |
| 130 | + | |
| 131 | + return; | |
| 132 | + } | |
| 133 | + | |
| 134 | + $account = PayplugWoocommerceHelper::get_account_data_from_options(); | |
| 135 | + $oney_settings = $this->get_configuration()->get_option('payment_methods.configuration.oney'); | |
| 136 | + $country = PayplugWoocommerceHelper::getISOCountryCode(); | |
| 137 | + $mapper = OneyAccountMapper::map((array) $account, $oney_settings, $country); | |
| 138 | + $code = $mapper->business_transaction_code_for_gateway($this->id); | |
| 139 | + | |
| 140 | + if (!$mapper->is_enabled() || !$code) { | |
| 141 | + return; | |
| 142 | + } | |
| 143 | + | |
| 144 | + printf( | |
| 145 | + '<div id="oney-checkout-loading-%1$s" class="payplug-lds-roller"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><div id="oney-checkout-%1$s"></div><script type="application/json" id="oney-checkout-config-%1$s">%2$s</script>', | |
| 146 | + esc_attr($this->id), | |
| 147 | + wp_json_encode([ | |
| 148 | + 'merchant_guid' => $mapper->get_merchant_guid(), | |
| 149 | + 'business_transaction_code' => $code, | |
| 150 | + 'country' => $country, | |
| 151 | + 'language' => strtoupper(substr(get_locale(), 0, 2)), | |
| 152 | + 'payment_amount' => floatval(WC()->cart->total), | |
| 153 | + ]) | |
| 154 | + ); | |
| 155 | + } | |
| 156 | + | |
| 157 | + /** | |
| 103 | 158 | * Check if Oney is available |
| 104 | - * | |
| 105 | - * @return void | |
| 159 | + * | |
| 160 | + * @return bool|int true, false, or an ONEY_* status code | |
| 106 | 161 | */ |
| 107 | 162 | public function check_oney_is_available() |
| 108 | 163 | { |
| 164 | + static $is_running = false; | |
| 165 | + if ($is_running) { | |
| 166 | + return false; | |
| 167 | + } | |
| 168 | + $is_running = true; | |
| 169 | + try { | |
| 170 | + $result = $this->do_check_oney_is_available(); | |
| 171 | + } finally { | |
| 172 | + $is_running = false; | |
| 173 | + } | |
| 174 | + | |
| 175 | + return $result; | |
| 176 | + } | |
| 177 | + | |
| 178 | + private function do_check_oney_is_available() | |
| 179 | + { | |
| 109 | 180 | $cart = WC()->cart; |
| 181 | + | |
| 182 | + //for backend orders | |
| 183 | + if (!empty(get_query_var('order-pay'))) { | |
| 184 | + $order = wc_get_order(get_query_var('order-pay')); | |
| 185 | + | |
| 186 | + //wc_get_order @return bool|WC_Order|WC_Order_Refund | |
| 187 | + if ($order === false) { | |
| 188 | + return false; | |
| 189 | + } | |
| 190 | + | |
| 191 | + $items = $order->get_items(); | |
| 192 | + | |
| 193 | + $country_code_shipping = $order->get_shipping_country(); | |
| 194 | + $country_code_billing = $order->get_billing_country(); | |
| 195 | + | |
| 196 | + // Skip cart population for subscription renewals: WC Subscriptions manages the cart | |
| 197 | + // itself and adding items here would cause a double entry in the order summary. | |
| 198 | + $is_renewal = function_exists('wcs_order_contains_renewal') && wcs_order_contains_renewal($order); | |
| 199 | + if (!$is_renewal) { | |
| 200 | + $this->order_items_to_cart($cart, $items); | |
| 201 | + } | |
| 202 | + } | |
| 203 | + | |
| 204 | + $oney_cfg = $this->get_configuration()->get_option('payment_methods.configuration.oney'); | |
| 205 | + $account = PayplugWoocommerceHelper::get_account_data_from_options(); | |
| 206 | + $oney_account_cfg = ((array) $account)['configuration']['oney'] ?? []; | |
| 207 | + // Same clamping OneyAccountMapper::map() uses for the badge/widget display, so | |
| 208 | + // checkout validation and display never silently drift apart (PRE-3457 review). | |
| 209 | + [$min_cents, $max_cents] = OneyAccountMapper::effective_bounds($oney_account_cfg, $oney_cfg); | |
| 210 | + $oney_amount = [ | |
| 211 | + 'min' => $min_cents / 100, | |
| 212 | + 'max' => $max_cents / 100, | |
| 213 | + ]; | |
| 214 | + // validate_checkout() reads these to build its exception message - populate them | |
| 215 | + // unconditionally (before any early return below) so they're never stale/unset there. | |
| 216 | + $this->oney_thresholds_min = $oney_amount['min']; | |
| 217 | + $this->oney_thresholds_max = $oney_amount['max']; | |
| 218 | + | |
| 219 | + if (empty($cart->total)) { | |
| 220 | + return false; | |
| 221 | + } | |
| 222 | + | |
| 223 | + if (empty($cart->cart_contents_count)) { | |
| 224 | + return false; | |
| 225 | + } | |
| 226 | + | |
| 110 | 227 | $total_price = floatval($cart->total); |
| 111 | - $products_qty = (int) $cart->cart_contents_count; | |
| 112 | - | |
| 113 | - // Min and max | |
| 114 | - if ($total_price < $this->min_oney_price || $total_price > $this->max_oney_price) { | |
| 115 | - $this->description = '<div class="payment_method_oney_x3_with_fees_disabled">'.sprintf(__('The total amount of your order should be between %s€ and %s€ to pay with Oney.', 'payplug'), $this->min_oney_price, $this->max_oney_price).'</div>'; | |
| 228 | + $products_qty = (int) $cart->cart_contents_count; | |
| 229 | + | |
| 230 | + // Min and max | |
| 231 | + if ($total_price < $oney_amount['min'] || $total_price > $oney_amount['max']) { | |
| 232 | + $this->description = '<div class="payment_method_oney_x3_with_fees_disabled">' . sprintf(__('The total amount of your order should be between %s€ and %s€ to pay with Oney.', 'payplug'), $oney_amount['min'], $oney_amount['max']) . '</div>'; | |
| 233 | + | |
| 116 | 234 | return false; |
| 117 | 235 | } |
| 118 | 236 | |
| 119 | 237 | // Cart check |
| 120 | 238 | if ($products_qty >= self::ONEY_PRODUCT_QUANTITY_MAXIMUM) { |
| 121 | - $this->description = '<div class="payment_method_oney_x3_with_fees_disabled">'.sprintf(__('The payment with Oney is unavailable because you have more than %s items in your cart.', 'payplug'), self::ONEY_PRODUCT_QUANTITY_MAXIMUM).'</div>'; | |
| 239 | + $this->description = '<div class="payment_method_oney_x3_with_fees_disabled">' . sprintf(__('The payment with Oney is unavailable because you have more than %s items in your cart.', 'payplug'), self::ONEY_PRODUCT_QUANTITY_MAXIMUM) . '</div>'; | |
| 240 | + | |
| 122 | 241 | return self::ONEY_UNAVAILABLE_CODE_CART_SIZE_TOO_HIGH; |
| 123 | 242 | } |
| 124 | 243 | |
| 125 | 244 | // Country check |
| 126 | - $country_code_shipping = WC()->customer->get_shipping_country(); | |
| 127 | - $country_code_billing = WC()->customer->get_billing_country(); | |
| 128 | - | |
| 129 | - if (!in_array($country_code_billing, $this->allowed_country_codes) || !in_array($country_code_shipping, $this->allowed_country_codes)) { | |
| 130 | - $this->description = '<div class="payment_method_oney_x3_with_fees_disabled">'.__('Unavailable for the specified country.', 'payplug').'</div>'; | |
| 131 | - return self::ONEY_UNAVAILABLE_CODE_COUNTRY_NOT_ALLOWED; | |
| 245 | + if (empty($country_code_shipping) || empty($country_code_shipping)) { | |
| 246 | + $country_code_shipping = WC()->customer->get_shipping_country(); | |
| 247 | + $country_code_billing = WC()->customer->get_billing_country(); | |
| 132 | 248 | } |
| 133 | - | |
| 249 | + | |
| 250 | + //WOOC-663 exception for the description to be visible | |
| 251 | + //billing allowed? | |
| 252 | + if ($this->allowed_country($country_code_billing, $this->allowed_country_codes)) { | |
| 253 | + | |
| 254 | + //if shipping is different from billing and billing is accepted | |
| 255 | + if (!$this->validate_shipping_billing_country($country_code_shipping, $country_code_billing)) { | |
| 256 | + $this->description = '<div class="payment_method_oney_x3_with_fees_disabled">' . __('Unavailable for the specified country.', 'payplug') . '</div>'; | |
| 257 | + | |
| 258 | + return self::ONEY_UNAVAILABLE_CODE_COUNTRY_NOT_ALLOWED; | |
| 259 | + } | |
| 260 | + } else { | |
| 261 | + return self::ONEY_DISALBE_CHECKOUT_OPTIONS; | |
| 262 | + } | |
| 263 | + | |
| 134 | 264 | return true; |
| 135 | 265 | } |
| 136 | 266 | |
| 137 | 267 | /** |
| @@ -157,10 +287,11 @@ | ||
| 157 | 287 | * @param \WC_Order $order |
| 158 | 288 | * @param int $amount |
| 159 | 289 | * @param int $customer_id |
| 160 | 290 | * |
| 291 | + * @throws \Exception | |
| 292 | + * | |
| 161 | 293 | * @return array |
| 162 | - * @throws \Exception | |
| 163 | 294 | */ |
| 164 | 295 | public function process_standard_payment($order, $amount, $customer_id) |
| 165 | 296 | { |
| 166 | 297 | $order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id(); |
| @@ -174,25 +305,31 @@ | ||
| 174 | 305 | throw new \Exception(sprintf(__('The payment with Oney is unavailable because you have more than %s items in your cart.', 'payplug'), self::ONEY_PRODUCT_QUANTITY_MAXIMUM)); |
| 175 | 306 | } |
| 176 | 307 | |
| 177 | 308 | $country = PayplugWoocommerceHelper::is_pre_30() ? $order->billing_country : $order->get_billing_country(); |
| 178 | - $phone = PayplugWoocommerceHelper::is_pre_30() ? $order->billing_phone : $order->get_billing_phone(); | |
| 179 | - $billing_email = PayplugWoocommerceHelper::is_pre_30() ? $order->billing_email : $order->get_billing_email(); | |
| 309 | + $phone = PayplugWoocommerceHelper::is_pre_30() ? $order->billing_phone : $order->get_billing_phone(); | |
| 310 | + $billing_email = PayplugWoocommerceHelper::is_pre_30() ? $order->billing_email : $order->get_billing_email(); | |
| 180 | 311 | $phone_number_util = PhoneNumberUtil::getInstance(); |
| 181 | - $phone_number = $phone_number_util->parse( $phone, $country ); | |
| 182 | - if ( PhoneNumberType::MOBILE !== $phone_number_util->getNumberType( $phone_number ) ) { | |
| 312 | + $phone_number = $phone_number_util->parse($phone, $country); | |
| 313 | + if (PhoneNumberType::MOBILE !== $phone_number_util->getNumberType($phone_number)) { | |
| 183 | 314 | throw new \Exception(__('Mobile phone number fullfilled is invalid. Please retry.', 'payplug')); |
| 184 | 315 | } |
| 185 | - | |
| 186 | - if (!filter_var($billing_email, FILTER_VALIDATE_EMAIL) || strpos($billing_email,'+') !== false) { | |
| 187 | - throw new \Exception(__("Your email address is too long and the + character is not valid, please change it to another address (max 100 characters).", 'payplug')); | |
| 316 | + | |
| 317 | + if (!filter_var($billing_email, FILTER_VALIDATE_EMAIL) || strpos($billing_email, '+') !== false) { | |
| 318 | + throw new \Exception(__('Your email address is too long and the + character is not valid, please change it to another address (max 100 characters).', 'payplug')); | |
| 188 | 319 | } |
| 189 | 320 | |
| 190 | 321 | $address_data = PayplugAddressData::from_order($order); |
| 191 | 322 | |
| 323 | + $return_url = esc_url_raw($order->get_checkout_order_received_url()); | |
| 324 | + | |
| 325 | + if (!(substr($return_url, 0, 4) === 'http')) { | |
| 326 | + $return_url = get_site_url() . $return_url; | |
| 327 | + } | |
| 328 | + | |
| 192 | 329 | $cart_items = []; |
| 193 | 330 | $items = $order->get_items(); |
| 194 | - foreach($items as $item) { | |
| 331 | + foreach ($items as $item) { | |
| 195 | 332 | $data = $item->get_data(); |
| 196 | 333 | $total = floatval(round($data['total'], 2)) * 100; |
| 197 | 334 | $cart_items[] = [ |
| 198 | 335 | 'delivery_label' => 'storepickup', |
| @@ -197,39 +334,44 @@ | ||
| 197 | 334 | $cart_items[] = [ |
| 198 | 335 | 'delivery_label' => 'storepickup', |
| 199 | 336 | 'delivery_type' => 'storepickup', |
| 200 | 337 | 'brand' => 'Woocommerce', |
| 201 | - 'merchant_item_id' => 'cart-'.$data['id'].'-'.$data['product_id'], | |
| 338 | + 'merchant_item_id' => 'cart-' . $data['id'] . '-' . $data['product_id'], | |
| 202 | 339 | 'name' => $data['name'], |
| 203 | 340 | 'expected_delivery_date' => date('Y-m-d'), |
| 204 | - 'total_amount' => $total, | |
| 205 | - 'price' => round($total / $data['quantity']), | |
| 206 | - 'quantity' => $data['quantity'] | |
| 341 | + 'total_amount' => (int) $total, | |
| 342 | + 'price' => round($total / $data['quantity']), | |
| 343 | + 'quantity' => $data['quantity'], | |
| 207 | 344 | ]; |
| 208 | 345 | } |
| 209 | 346 | |
| 210 | 347 | $payment_data = [ |
| 211 | 348 | 'authorized_amount' => $amount, |
| 212 | - 'auto_capture' => true, | |
| 213 | - 'currency' => get_woocommerce_currency(), | |
| 214 | - 'payment_method' => $this->id, | |
| 215 | - 'billing' => $address_data->get_billing(), | |
| 216 | - 'shipping' => $address_data->get_shipping(), | |
| 217 | - 'payment_context' => [ | |
| 218 | - "cart" => $cart_items | |
| 349 | + 'auto_capture' => true, | |
| 350 | + 'currency' => get_woocommerce_currency(), | |
| 351 | + 'payment_method' => $this->id, | |
| 352 | + 'billing' => $address_data->get_billing(), | |
| 353 | + 'shipping' => $address_data->get_shipping(), | |
| 354 | + 'payment_context' => [ | |
| 355 | + 'cart' => $cart_items, | |
| 219 | 356 | ], |
| 220 | 357 | 'notification_url' => esc_url_raw(WC()->api_request_url('PayplugGateway')), |
| 221 | - 'hosted_payment' => [ | |
| 222 | - 'return_url' => esc_url_raw($order->get_checkout_order_received_url()), | |
| 358 | + 'hosted_payment' => [ | |
| 359 | + 'return_url' => $return_url, | |
| 223 | 360 | 'cancel_url' => esc_url_raw($order->get_cancel_order_url_raw()), |
| 224 | 361 | ], |
| 225 | - 'metadata' => [ | |
| 226 | - 'order_id' => $order_id, | |
| 362 | + 'metadata' => [ | |
| 363 | + 'order_id' => $order_id, | |
| 227 | 364 | 'customer_id' => ((int) $customer_id > 0) ? $customer_id : 'guest', |
| 228 | - 'domain' => $this->limit_length(esc_url_raw(home_url()), 500), | |
| 365 | + 'domain' => $this->limit_length(esc_url_raw(home_url()), 500), | |
| 229 | 366 | ], |
| 230 | 367 | ]; |
| 231 | 368 | |
| 369 | + if (PayplugWoocommerceHelper::is_checkout_block() && is_checkout()) { | |
| 370 | + $payment_data['metadata']['woocommerce_block'] = 'CHECKOUT'; | |
| 371 | + } elseif (PayplugWoocommerceHelper::is_cart_block() && is_cart()) { | |
| 372 | + $payment_data['metadata']['woocommerce_block'] = 'CART'; | |
| 373 | + } | |
| 232 | 374 | |
| 233 | 375 | /** |
| 234 | 376 | * Filter the payment data before it's used |
| 235 | 377 | * |
| @@ -238,9 +380,9 @@ | ||
| 238 | 380 | * @param array $customer_details |
| 239 | 381 | * @param PayplugAddressData $address_data |
| 240 | 382 | */ |
| 241 | 383 | $payment_data = apply_filters('payplug_gateway_payment_data', $payment_data, $order_id, [], $address_data); |
| 242 | - $payment = $this->api->payment_create($payment_data); | |
| 384 | + $payment = $this->payplug_api->payment_create($payment_data); | |
| 243 | 385 | |
| 244 | 386 | // Save transaction id for the order |
| 245 | 387 | PayplugWoocommerceHelper::is_pre_30() |
| 246 | 388 | ? update_post_meta($order_id, '_transaction_id', $payment->id) |
| @@ -263,11 +405,11 @@ | ||
| 263 | 405 | |
| 264 | 406 | PayplugGateway::log(sprintf('Payment creation complete for order #%s', $order_id)); |
| 265 | 407 | |
| 266 | 408 | return [ |
| 267 | - 'result' => 'success', | |
| 409 | + 'result' => 'success', | |
| 268 | 410 | 'redirect' => $payment->hosted_payment->payment_url, |
| 269 | - 'cancel' => $payment->hosted_payment->cancel_url, | |
| 411 | + 'cancel' => $payment->hosted_payment->cancel_url, | |
| 270 | 412 | ]; |
| 271 | 413 | } catch (\HttpException $e) { |
| 272 | 414 | PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error'); |
| 273 | 415 | throw new \Exception(__($e->getMessage(), 'payplug')); |
| @@ -276,47 +418,116 @@ | ||
| 276 | 418 | throw new \Exception(__($e->getMessage(), 'payplug')); |
| 277 | 419 | } |
| 278 | 420 | } |
| 279 | 421 | |
| 280 | - | |
| 281 | 422 | /** |
| 282 | - * Check if the gatteway is allowed for the order amount | |
| 423 | + * Check if the gatteway is allowed for the order amount | |
| 283 | 424 | * |
| 284 | 425 | * @param array |
| 426 | + * | |
| 285 | 427 | * @return array |
| 286 | 428 | */ |
| 287 | 429 | public function check_gateway($gateways) |
| 288 | 430 | { |
| 289 | 431 | if (isset($gateways[$this->id]) && $gateways[$this->id]->id == $this->id) { |
| 290 | - if(!PayplugWoocommerceHelper::is_oney_available()) { | |
| 432 | + //remove gateway if thresholds/country criteria are not met | |
| 433 | + if ($this->check_oney_is_available() === false) { | |
| 291 | 434 | unset($gateways[$this->id]); |
| 292 | 435 | } |
| 436 | + | |
| 437 | + //remove gateway if account doesn't have permission | |
| 438 | + if (!PayplugWoocommerceHelper::is_oney_available()) { | |
| 439 | + unset($gateways[$this->id]); | |
| 440 | + } else { | |
| 441 | + $gateways = parent::check_gateway($gateways); | |
| 442 | + } | |
| 293 | 443 | } |
| 444 | + | |
| 294 | 445 | return $gateways; |
| 295 | 446 | } |
| 296 | 447 | |
| 297 | 448 | /** |
| 298 | 449 | * Show Oney refund text |
| 299 | - * | |
| 450 | + * | |
| 300 | 451 | * @return void |
| 301 | 452 | */ |
| 302 | - public function oney_refund_text($order) | |
| 453 | + public function oney_refund_text($order): void | |
| 303 | 454 | { |
| 304 | - if ($this->id === $order->get_payment_method() && parent::can_refund_order($order) && $order->get_status() !== "refunded") { | |
| 455 | + if ($this->id === $order->get_payment_method() && parent::can_refund_order($order) && $order->get_status() !== 'refunded' && $this->payplug_api) { | |
| 305 | 456 | $order_metadata = $order->get_meta('_payplug_metadata'); |
| 306 | - $payment = $this->api->payment_retrieve($order_metadata['transaction_id']); | |
| 307 | - $today = current_time('Y-m-d H:i:s'); | |
| 308 | - $can_refund_date = date('Y-m-d H:i:s', $payment->__get('refundable_after')); | |
| 309 | - if ($can_refund_date >= $today) { | |
| 310 | - echo "<p style='color: red;'>" . __('Refund will be possible 48 hours after the last payment or refund transaction.', 'payplug') . "</p>"; | |
| 457 | + | |
| 458 | + if (is_array($order_metadata) && !empty($order_metadata['transaction_id'])) { | |
| 459 | + try { | |
| 460 | + $payment = $this->payplug_api->payment_retrieve($order_metadata['transaction_id']); | |
| 461 | + $today = current_time('Y-m-d H:i:s'); | |
| 462 | + $can_refund_date = date('Y-m-d H:i:s', $payment->__get('refundable_after')); | |
| 463 | + if ($can_refund_date >= $today) { | |
| 464 | + echo "<p style='color: red;'>" . __('Refund will be possible 48 hours after the last payment or refund transaction.', 'payplug') . '</p>'; | |
| 465 | + } | |
| 466 | + } catch (\Exception $e) { | |
| 467 | + PayplugGateway::log(sprintf('Could not retrieve refundable_after for Oney order: %s', $e->getMessage()), 'error'); | |
| 468 | + } | |
| 311 | 469 | } |
| 312 | 470 | } |
| 313 | 471 | } |
| 314 | 472 | |
| 315 | - public function payment_fields() | |
| 473 | + /** | |
| 474 | + * Billing and shipping addresses should have the same country and allowed by Oney | |
| 475 | + * https://payplug-prod.atlassian.net/browse/WOOC-227 | |
| 476 | + * | |
| 477 | + * @param $order | |
| 478 | + * | |
| 479 | + * @return bool | |
| 480 | + */ | |
| 481 | + public function validate_shipping_billing_country($shipping_country, $billing_country) | |
| 316 | 482 | { |
| 317 | - $description = $this->get_description(); | |
| 318 | - if (!empty($description)) { | |
| 319 | - echo wpautop(wptexturize($description)); | |
| 483 | + if ($billing_country === $shipping_country) { | |
| 484 | + return true; | |
| 320 | 485 | } |
| 486 | + | |
| 487 | + return false; | |
| 488 | + } | |
| 489 | + | |
| 490 | + /** | |
| 491 | + * Country of billing or shipping at oney payments should be on the allowedCountry that comes from pauyplug-api /account | |
| 492 | + * | |
| 493 | + * @param string $country | |
| 494 | + * @param array $allowed | |
| 495 | + * | |
| 496 | + * @return bool | |
| 497 | + */ | |
| 498 | + public function allowed_country($country, $allowed) | |
| 499 | + { | |
| 500 | + if (in_array($country, $allowed)) { | |
| 501 | + return true; | |
| 502 | + } | |
| 503 | + | |
| 504 | + return false; | |
| 505 | + } | |
| 506 | + | |
| 507 | + /** | |
| 508 | + * Empty the cart and add all order_items | |
| 509 | + * | |
| 510 | + * @param $cart | |
| 511 | + * @param $items | |
| 512 | + * | |
| 513 | + * @return void | |
| 514 | + */ | |
| 515 | + private function order_items_to_cart($cart, $items): void | |
| 516 | + { | |
| 517 | + $cart->empty_cart(); | |
| 518 | + foreach ($items as $item) { | |
| 519 | + $cart->add_to_cart($item->get_product_id(), $item->get_quantity(), $item->get_variation_id()); | |
| 520 | + } | |
| 521 | + } | |
| 522 | + | |
| 523 | + public function checkGateway() | |
| 524 | + { | |
| 525 | + $options = PayplugWoocommerceHelper::get_payplug_options(); | |
| 526 | + | |
| 527 | + // Now called unconditionally from the constructor (not just at checkout via | |
| 528 | + // is_available()), so a legacy account whose stored config predates the oney block | |
| 529 | + // (never went through Upgrade::run_upgrade()) would otherwise throw "undefined array | |
| 530 | + // key" warnings on every admin page that builds the WC gateway list (PRE-3597 review). | |
| 531 | + return $options['payment_methods']['configuration']['oney']['active'] ?? false; | |
| 321 | 532 | } |
| 322 | 533 | } |