diff --git a/readme.txt b/readme.txt index 55fe460f..dce71c0b 100644 --- a/readme.txt +++ b/readme.txt @@ -11,14 +11,14 @@ Tested up to: 6.4 WC requires at least: 3.0.0 WC tested up to: 8.6.1 Requires PHP: 5.6 -Stable Tag: 1.3.0 +Stable Tag: 1.3.2 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html Venda de assinaturas de produtos e serviços pelo plugin de cobrança recorrente para o WooCommerce. == Description == -O **Vindi WooCommerce** oferece uma solução completa para pagamentos únicos e assinaturas com cartão de crédito e boleto utilizando o [Woocommerce Subscriptions](https://www.woothemes.com/products/woocommerce-subscriptions/). Basta ter [uma conta habilitada na Vindi](https://www.vindi.com.br/cadastro/) para começar a cobrar seus clientes. +O **Vindi WooCommerce** oferece uma solução completa para pagamentos únicos e assinaturas com cartão de crédito, boleto, bolePix e Pix utilizando o [Woocommerce Subscriptions](https://www.woothemes.com/products/woocommerce-subscriptions/). Basta ter [uma conta habilitada na Vindi](https://www.vindi.com.br/cadastro/) para começar a cobrar seus clientes. A [Vindi](https://www.vindi.com.br/) é líder em cobrança recorrente no Brasil. Com milhares de clientes usando soluções como pagamento online, soluções de notas fiscais integradas, emissão de boletos por email e PDF, integrações com ERPs e diversos relatórios, a Vindi possibilita um sistema online completo para negócios de venda recorrente. Além disso, empresas podem usar o gateway de pagamento integrado ao billing recorrente ou para faturas avulsas. @@ -40,11 +40,24 @@ Para dúvidas e suporte técnico, entre em contato com a equipe Vindi através d == Changelog == += 1.3.2 - 22/05/2024 = +-Lançamento da versão de patch. +- **Correção:** As assinaturas com trial não estão mais passando do tempo limite +- **Correção:** Texto da descrição do plugin agora possui bolePix e Pix +- **Correção:** Alteração do texto que informava a proibição de ter assinatura e produto avulso no mesmo carrinho + += 1.3.1 - 15/05/2024 = +-Lançamento da versão de patch. +- **Correção:** Utilização de cupons para desconto dos produtos que possuem taxa +- **Correção:** Não é mais possível a comprar de uma assinatura com produto avulso no mesmo carrinho +- **Correção:** Não pode mais comprar duas assinaturas iguais + = 1.3.0 - 06/05/2024 = -Lançamento da versão de patch. - **Melhoria:** Refatoração nas funções de processamento de webhooks - **Correção:** Atualização de expressões regulares para identificação de bandeiras de cartão + = 1.2.9 - 18/04/2024 = -Lançamento da versão de patch. - **Correção:** Utilização da função save durante atualização de metadados com HPOS diff --git a/src/VindiWoocommerce.php b/src/VindiWoocommerce.php index 5cc3f28d..af5294d3 100644 --- a/src/VindiWoocommerce.php +++ b/src/VindiWoocommerce.php @@ -1,4 +1,5 @@ webhooks, 'handle' )); + add_filter('woocommerce_add_to_cart_validation', [$this, 'limit_same_subscriptions'], 10, 3); + add_filter('woocommerce_update_cart_validation', [$this, 'limit_duplicate_subscriptions_cart_update'], 10, 4); + add_filter('woocommerce_add_to_cart_validation', [$this, 'disallow_subscription_single_product_cart'], 10, 4); add_filter('woocommerce_cart_needs_payment', [$this, 'filter_woocommerce_cart_needs_payment'], 10, 2); add_action('wp_ajax_renew_pix_charge', [$this, 'renew_pix_charge']); add_action('wp_ajax_nopriv_renew_pix_charge', [$this, 'renew_pix_charge']); + do_action('woocommerce_set_cart_cookies', true); } /** @@ -129,6 +134,8 @@ public function init() require_once plugin_dir_path(__FILE__) . '/i18n/Languages.php'; require_once plugin_dir_path(__FILE__) . '/services/VindiHelpers.php'; require_once plugin_dir_path(__FILE__) . '/services/Webhooks.php'; + require_once plugin_dir_path(__FILE__) . '/services/WebhooksHelpers.php'; + // Loading Abstract Method and Utils require_once plugin_dir_path(__FILE__) . '/utils/PaymentGateway.php'; @@ -137,7 +144,7 @@ public function init() require_once plugin_dir_path(__FILE__) . '/utils/PostMeta.php'; require_once plugin_dir_path(__FILE__) . '/includes/admin/CouponsMetaBox.php'; - require_once plugin_dir_path(__FILE__) . '/includes/admin/ProductsMetabox.php'; + require_once plugin_dir_path(__FILE__) . '/includes/admin/ProductsMetabox.php'; require_once plugin_dir_path(__FILE__) . '/includes/admin/Settings.php'; require_once plugin_dir_path(__FILE__) . '/includes/gateways/CreditPayment.php'; require_once plugin_dir_path(__FILE__) . '/includes/gateways/BankSlipPayment.php'; @@ -211,20 +218,21 @@ private function cart_has_trial($cart) $items = $cart->get_cart(); foreach ($items as $item) { if (class_exists('WC_Subscriptions_Product') - && WC_Subscriptions_Product::get_trial_length($item['product_id']) > 0) { - return true; + && WC_Subscriptions_Product::get_trial_length($item['product_id']) > 0 + ) { + return true; } } return false; } - + public function renew_pix_charge() { $order_id = filter_input(INPUT_POST, 'order_id', FILTER_SANITIZE_NUMBER_INT); $charge_id = filter_input(INPUT_POST, 'charge_id', FILTER_SANITIZE_NUMBER_INT); $subscription_id = filter_input(INPUT_POST, 'subscription_id', FILTER_SANITIZE_NUMBER_INT); - + $order = wc_get_order($order_id); $vindi_order = $order->get_meta('vindi_order', true); @@ -237,12 +245,12 @@ public function renew_pix_charge() $subscription = $vindi_order[$subscription_id]; $bill = [ - 'id' => $subscription['bill']['id'], - 'status' => $subscription['bill']['status'], - 'charge_id' => $charge['id'], - 'pix_expiration' => $last_transaction['max_days_to_keep_waiting_payment'], - 'pix_code' => $last_transaction['qrcode_original_path'], - 'pix_qr' => $last_transaction['qrcode_path'], + 'id' => $subscription['bill']['id'], + 'status' => $subscription['bill']['status'], + 'charge_id' => $charge['id'], + 'pix_expiration' => $last_transaction['max_days_to_keep_waiting_payment'], + 'pix_code' => $last_transaction['qrcode_original_path'], + 'pix_qr' => $last_transaction['qrcode_path'], ]; $vindi_order[$subscription_id]['bill'] = $bill; @@ -251,6 +259,124 @@ public function renew_pix_charge() } } } + + public function limit_same_subscriptions($passed, $product_id, $quantity) + { + $product = wc_get_product($product_id); + + if ($product->is_virtual()) { + return $passed; + } + + if (WC_Subscriptions_Product::is_subscription($product_id)) { + $subscription_count = $this->get_subscription_count($product_id); + + if ($subscription_count + $quantity > 1) { + wc_add_notice('Você só pode ter até 1 assinatura do mesmo produto no seu carrinho.', 'error'); + return false; + } + } + + return $passed; + } + + public function get_subscription_count($product_id) + { + $cart = WC()->cart->get_cart(); + $subscription_count = 0; + + foreach ($cart as $cart_item) { + if ($cart_item['data']->get_id() === $product_id) { + $subscription_count += $cart_item['quantity']; + } + } + + return $subscription_count; + } + + public function limit_duplicate_subscriptions_cart_update($passed, $cart_item_key, $values, $quantity) + { + $product_id = $values['product_id']; + $product = wc_get_product($product_id); + + if ($this->is_virtual_product($product)) { + return $passed; + } + + if ($this->subscription_exceeds_limit($product_id, $quantity)) { + return false; + } + + return $passed; + } + + public function is_virtual_product($product) + { + return $product->is_virtual(); + } + + public function subscription_exceeds_limit($product_id, $quantity) + { + if (WC_Subscriptions_Product::is_subscription($product_id)) { + $subscription_count = $this->count_subscriptions_in_cart($product_id); + + if ($subscription_count >= 1 && $quantity > 1) { + $message ='Você só pode ter até 1 assinatura do mesmo produto no seu carrinho.'; + wc_add_notice(__($message, 'vindi-payment-gateway'), 'error'); + return true; + } + } + + return false; + } + + public function count_subscriptions_in_cart($product_id) + { + $subscription_count = 0; + foreach (WC()->cart->get_cart() as $cart_item) { + if ($cart_item['data']->get_id() === $product_id) { + $subscription_count++; + } + } + return $subscription_count; + } + + public function disallow_subscription_single_product_cart($passed, $product_id, $quantity) + { + $product = wc_get_product($product_id); + + if ($product->is_virtual()) { + return $passed; + } + + if ($this->is_cart_mixed_with_subscription($product_id)) { + wc_add_notice(__('Olá! Finalize a compra da assinatura adicionada + ao carrinho antes de adicionar outra assinatura ou produto.', 'vindi-payment-gateway'), 'error'); + return false; + } + + return $passed; + } + + public function is_cart_mixed_with_subscription($product_id) + { + $cart = WC()->cart->get_cart(); + if (empty($cart)) { + return false; + } + + $is_subscription = false; + $new_product_subscription = WC_Subscriptions_Product::is_subscription($product_id); + + foreach ($cart as $cart_item) { + if (WC_Subscriptions_Product::is_subscription($cart_item['data']->get_id())) { + $is_subscription = true; + break; + } + } + + return $is_subscription !== $new_product_subscription; + } } add_action('plugins_loaded', array(WcVindiPayment::class, 'get_instance')); diff --git a/src/includes/gateways/BankSlipPayment.php b/src/includes/gateways/BankSlipPayment.php index 0b81714f..305459b1 100644 --- a/src/includes/gateways/BankSlipPayment.php +++ b/src/includes/gateways/BankSlipPayment.php @@ -102,11 +102,10 @@ public function init_form_fields() # Issue: https://github.com/vindi/vindi-woocommerce/issues/75 public function bank_slip_quantity_to_render($order) { - if (is_null($order[0])) { - return $order; + if (isset($order[0])) { + return $order[0]; } - - return $order[0]; + return $order; } public function payment_fields() diff --git a/src/services/Webhooks.php b/src/services/Webhooks.php index c96e4f2e..76006b7b 100644 --- a/src/services/Webhooks.php +++ b/src/services/Webhooks.php @@ -19,6 +19,11 @@ class VindiWebhooks */ private $routes; + /** + * @var WebhooksHelper + */ + private $webhooksHelpers; + /** * @param VindiSettings $vindi_settings */ @@ -26,6 +31,7 @@ public function __construct(VindiSettings $vindi_settings) { $this->vindi_settings = $vindi_settings; $this->routes = $vindi_settings->routes; + $this->webhooksHelpers = new WebhooksHelpers($this); } /** @@ -99,36 +105,40 @@ private function test($data) */ private function bill_created($data) { + $response = ['message' => 'Não foi possível emitir a fatura', 'status' => 422]; try { if (empty($data->bill->subscription)) { return; } - $renew_infos = [ - 'wc_subscription_id' => $data->bill->subscription->code, - 'vindi_subscription_id' => $data->bill->subscription->id, - 'plan_name' => str_replace('[WC] ', '', $data->bill->subscription->plan->name), - 'cycle' => $data->bill->period->cycle, - 'bill_status' => $data->bill->status, - 'bill_id' => $data->bill->id, - 'bill_print_url' => $data->bill->charges[0]->print_url + + $renewInfos = [ + 'wc_subscription_id' => $data->bill->subscription->code, + 'vindi_subscription_id' => $data->bill->subscription->id, + 'plan_name' => str_replace('[WC] ', '', $data->bill->subscription->plan->name), + 'cycle' => $data->bill->period->cycle, + 'bill_status' => $data->bill->status, + 'bill_id' => $data->bill->id, + 'bill_print_url' => $data->bill->charges[0]->print_url ]; - if (!$this->subscription_has_order_in_cycle($renew_infos['vindi_subscription_id'], $renew_infos['cycle'])) { - $this->subscription_renew($renew_infos); - $this->update_next_payment($data); - return wp_send_json(['message' => 'Fatura emitida corretamente'], 200); + + if ($this->webhooksHelpers->handle_subscription_renewal($renewInfos, $data)) { + $response = ['message' => 'Fatura emitida corretamente', 'status' => 200]; + } elseif ($this->webhooksHelpers->handle_trial_period($renewInfos['wc_subscription_id'])) { + $response = ['message' => 'O estado da assinatura passou para "Em espera"', 'status' => 200]; } - return wp_send_json(['message' => 'Não foi possível emitir a fatura'], 422); } catch (\Exception $e) { $this->handle_exception('bill_created', $e->getMessage(), $data->bill->id); - return wp_send_json(['message' => 'Erro durante o processamento da fatura.'], 500); + $response = ['message' => 'Erro durante o processamento da fatura.', 'status' => 500]; } - } + return wp_send_json(['message' => $response['message']], $response['status']); + } + /** * Process subscription_renew event from webhook * @param $renew_infos array */ - private function subscription_renew($renew_infos) + public function subscription_renew($renew_infos) { $subscription = $this->find_subscription_by_id($renew_infos['wc_subscription_id']); @@ -159,7 +169,7 @@ private function subscription_renew($renew_infos) // We've already processed the renewal remove_action('woocommerce_scheduled_subscription_payment', 'WC_Subscriptions_Manager::prepare_renewal'); - } + } /** * Process bill_paid event from webhook @@ -386,17 +396,17 @@ private function handle_exception($event, $error, $data) * @param int id * @return WC_Subscription */ - private function find_subscription_by_id($id) + public function find_subscription_by_id($id_item) { // Webhooks Ids has "WC-" prefix - $sanitized_id = explode('WC-', $id); + $sanitized_id = explode('WC-', $id_item); $subscription = wcs_get_subscription(end($sanitized_id)); if (empty($subscription)) - throw new Exception(sprintf(__('Assinatura #%s não encontrada!', VINDI), $id), 2); + throw new Exception(sprintf(__('Assinatura #%s não encontrada!', VINDI), $id_item), 2); return $subscription; - } + } /** * @param int id @@ -483,7 +493,7 @@ private function find_order_by_subscription_and_cycle($subscription_id, $cycle) * * @return boolean */ - private function subscription_has_order_in_cycle($subscription_id, $cycle) + public function subscription_has_order_in_cycle($subscription_id, $cycle) { $query = $this->query_order_by_metas(array( array( @@ -517,7 +527,7 @@ private function query_order_by_metas(array $metas) * * @param $data object */ - private function update_next_payment($data) + public function update_next_payment($data) { // let's find the subscription in the API // we need this step because the actual next billing date does not come from the /bill webhook @@ -551,7 +561,7 @@ private function update_next_payment($data) $subscription->update_dates(array('next_payment' => $next_payment)); $subscription->update_dates(array('end_date' => $end_date)); } - } + } private function format_date($date) { diff --git a/src/services/WebhooksHelpers.php b/src/services/WebhooksHelpers.php new file mode 100644 index 00000000..d23bb84f --- /dev/null +++ b/src/services/WebhooksHelpers.php @@ -0,0 +1,47 @@ +vindiWebhooks = $vindiWebhooks; + } + + public function handle_subscription_renewal($renewInfos, $data) + { + $vindiId = $renewInfos['vindi_subscription_id']; + $cycle = $renewInfos['cycle']; + $hasOrder = $this->vindiWebhooks->subscription_has_order_in_cycle($vindiId, $cycle); + if (!$hasOrder) { + $this->vindiWebhooks->subscription_renew($renewInfos); + $this->vindiWebhooks->update_next_payment($data); + return true; + } + return false; + } + + public function handle_trial_period($subscriptionId) + { + $cleanSubscriptionId = $this->vindiWebhooks->find_subscription_by_id($subscriptionId); + $subscription = wcs_get_subscription($cleanSubscriptionId); + $now = new DateTime(); + $endTrial = new DateTime(); + $endTrial->setTimestamp($subscription->get_time('trial_end')); + if ($endTrial > $now && $subscription->get_status() == "active") { + $parentId = $subscription->get_parent_id(); + $order = new WC_Order($parentId); + $order->update_status('pending', 'Período de teste vencido'); + $subscription->update_status('on-hold'); + return true; + } + return false; + } +} diff --git a/src/utils/DefinitionVariables.php b/src/utils/DefinitionVariables.php index 1c9e0a0d..85543008 100644 --- a/src/utils/DefinitionVariables.php +++ b/src/utils/DefinitionVariables.php @@ -1,6 +1,6 @@ routes = $vindi_settings->routes; $this->controllers = $controllers; $this->single_freight = $this->vindi_settings->get_option('shipping_and_tax_config') == "yes" ? true : false; - } /** @@ -281,7 +281,7 @@ public function process_order() $this->check_trial_and_single_product(); $customer = $this->get_customer(); $order_items = $this->order->get_items(); - + $bills = []; $order_post_meta = []; $bill_products = []; @@ -312,7 +312,7 @@ public function process_order() $this->check_multiple_subscriptions_of_same_period($subscriptions_grouped_by_period); foreach ($subscription_products as $subscription_order_item) { - if(empty($subscription_order_item)) + if (empty($subscription_order_item)) continue; try { @@ -420,7 +420,6 @@ protected function create_payment_profile($customer_id) if ($this->gateway->verify_method()) { $this->verify_payment_profile($payment_profile['id']); } - } /** @@ -436,7 +435,6 @@ protected function verify_payment_profile($payment_profile_id) if (!$this->routes->verifyCustomerPaymentProfile($payment_profile_id)) { $this->abort(__('Não foi possível realizar a verificação do seu cartão de crédito!', VINDI), true); } - } /** @@ -458,10 +456,8 @@ private function get_cycle_from_product_type($item) if ($item['type'] == 'shipping' || $item['type'] == 'tax') { if ($this->vindi_settings->get_shipping_and_tax_config()) { return 1; - } return $this->single_freight ? 1 : null; - } elseif (!$product || !$this->is_subscription_type($product)) { return 1; } @@ -497,17 +493,40 @@ private function is_one_time_shipping($product) */ public function build_product_items($product, $order_type = 'bill') { + if (!$product) { $this->abort(__("Ocorreu um erro ao gerar o seu pedido!", VINDI), true); } + $call_build_items = $this->get_call_build_items_method($order_type); + + $product_items = []; + $order_items = []; + $order_items = $this->add_additional_items($order_items, $order_type, $product); + + $product_items = $this->build_items($order_items, $call_build_items); + + if (empty($product_items)) { + return $this->abort(__('Falha ao recuperar informações sobre o produto na Vindi. Verifique os dados e tente novamente.', VINDI), true); + } + + $new_item = $this->calculate_discount($product_items); + return $new_item; + } + + protected function get_call_build_items_method($order_type) + { $call_build_items = "build_product_items_for_{$order_type}"; if (false === method_exists($this, $call_build_items)) { $this->abort(__("Ocorreu um erro ao gerar o seu pedido!", VINDI), true); } - $product_items = []; + return $call_build_items; + } + + protected function add_additional_items($order_items, $order_type, $product) + { $order_items = []; if ('bill' === $order_type) { @@ -515,9 +534,9 @@ public function build_product_items($product, $order_type = 'bill') } else { $order_items[] = $this->build_product_from_order_item($order_type, $product); } + $order_items[] = $this->build_shipping_item($order_items); $order_items[] = $this->build_tax_item($order_items); - $order_items[] = $this->build_sign_up_fee_item($order_items); if ('bill' === $order_type) { @@ -525,20 +544,112 @@ public function build_product_items($product, $order_type = 'bill') } $order_items[] = $this->build_interest_rate_item($order_items); + return $order_items; + } + + protected function build_items($order_items, $call_build_items) + { + $product_items = []; foreach ($order_items as $order_item) { if (!empty($order_item)) { $newProduct = $this->$call_build_items($order_item); $product_items[] = $newProduct; } - continue; } - if (empty($product_items)) { - return $this->abort(__('Falha ao recuperar informações sobre o produto na Vindi. Verifique os dados e tente novamente.', VINDI), true); + return $product_items; + } + + protected function calculate_discount($order_items) + { + $new_order_items = []; + $remainder = 0; + $item = $this->routes->findOrCreateProduct("[WC] Taxa de adesão", "WC-SUF"); + $taxa_id = array( + 'vindi_id' => $item['id'] + ); + + foreach ($order_items as $order_item) { + $new_order_item = $order_item; + $total_discount = 0; + + $full_price = $this->calculate_full_price($order_item); + $remainder = $this->apply_discount($order_item, $full_price, $remainder); + + if ($order_item['product_id'] === $taxa_id['vindi_id']) { + $total_discount = $this->validate_discount_percentage_sign_up_fee($order_item); + $remainder = $this->apply_remainder($remainder, $full_price, $new_order_item); + } + + if ($total_discount > 0) { + $new_order_item['discounts'][] = array( + 'discount_type' => 'amount', + 'amount' => $total_discount, + 'cycles' => 1 + ); + } + + $new_order_items[] = $new_order_item; } - return $product_items; + return $new_order_items; + } + + protected function validate_discount_percentage_sign_up_fee($order_item) + { + $coupons = array_values($this->vindi_settings->woocommerce->cart->get_coupons()); + $total_discount = 0; + + foreach ($coupons as $coupon) { + $discount_type = $coupon->get_discount_type(); + if ($discount_type === 'percent') { + $discount_amount = $coupon->get_amount(); + $price = $order_item['pricing_schema']['price']; + $quantity = $order_item['quantity']; + $discount = abs(($price * $quantity)) * $discount_amount / 100; + $total_discount += $discount; + } + } + return $total_discount; + } + + protected function calculate_full_price($order_item) + { + $price = isset($order_item['pricing_schema']['price']) ? abs($order_item['pricing_schema']['price']) : 0; + $quantity = isset($order_item['quantity']) ? $order_item['quantity'] : 1; + $total_price = $price * $quantity; + return $total_price; + } + + protected function apply_discount($order_item, $full_price, $remainder) + { + if (isset($order_item['discounts'])) { + $discountTotal = array_reduce($order_item['discounts'], function ($total, $discount) { + if (isset($discount['amount'])) { + return $total + $discount['amount']; + } + return $total; + }, 0); + if ($discountTotal > $full_price) { + $remainder = $discountTotal - $full_price; + } + } + return $remainder; + } + + protected function apply_remainder($remainder, $full_price, &$new_order_item) + { + if ($remainder > 0) { + $amount_to_apply = min($remainder, $full_price); + $new_order_item['discounts'][] = array( + 'discount_type' => 'amount', + 'amount' => $amount_to_apply, + 'cycles' => 1 + ); + $remainder -= $amount_to_apply; + } + return $remainder; } /** @@ -556,9 +667,8 @@ protected function build_product_from_order_item($order_type, $order_items) foreach ($order_items as $key => $order_item) { $product = $order_item->get_product(); $order_items[$key]['type'] = 'product'; - $order_items[$key]['vindi_id'] = $this->routes->findProductByCode('WC-' . $product->id)['id']; + $order_items[$key]['vindi_id'] = $this->routes->findProductByCode('WC-' . $product->get_id())['id']; $order_items[$key]['price'] = (float) $order_items[$key]['subtotal'] / $order_items[$key]['qty']; - } return $order_items; } @@ -609,7 +719,7 @@ protected function build_sign_up_fee_item($order_items) 'type' => 'sign_up_fee', 'vindi_id' => $item['id'], 'price' => (float) $sign_up_fee, - 'qty' => 1, + 'qty' => $order_item['quantity'], ); $order_item['price'] -= $sign_up_fee; @@ -669,7 +779,7 @@ protected function build_shipping_item($order_items) { $shipping_item = []; $shipping_method = $this->order->get_shipping_method(); - $get_total_shipping = $this->order->get_total_shipping(); + $get_total_shipping = $this->order->get_shipping_total(); if (empty($shipping_method)) { return $shipping_item; @@ -757,7 +867,6 @@ protected function build_tax_item($order_items) protected function build_discount_item_for_bill($order_items) { $discount_item = []; - $coupons = array_values($this->vindi_settings->woocommerce->cart->get_coupons()); $bill_total_discount = 0; foreach ($order_items as $order_item) { if (isset($order_item['subtotal']) && isset($order_item['total'])) { @@ -828,18 +937,19 @@ protected function build_product_items_for_subscription($order_item) 'quantity' => $order_item['qty'], 'cycles' => $plan_cycles, 'pricing_schema' => array( - 'price' => $order_item['price'], + 'price' => abs($order_item['price']), 'schema_type' => 'per_unit', ), ); - $coupons = array_values($this->vindi_settings->woocommerce->cart->get_coupons()); + if (!empty($coupons) && $order_item['type'] == 'line_item') { $product_item['discounts'] = []; foreach ($coupons as $coupon) { if ($this->coupon_supports_product($order_item, $coupon)) { - $product_item['discounts'][] = $this->build_discount_item_for_subscription($coupon, $plan_cycles); + $discount_item = $this->build_discount_item_for_subscription($coupon, $order_item, $plan_cycles); + $product_item['discounts'][] = $discount_item; } } } @@ -857,7 +967,7 @@ protected function build_product_items_for_subscription($order_item) */ protected function coupon_supports_product($order_item, $coupon) { - $product_id = $order_item->get_product()->id; + $product_id = $order_item->get_product()->get_id(); $included_products = $coupon->get_product_ids(); $excluded_products = $coupon->get_excluded_product_ids(); @@ -885,27 +995,31 @@ protected function coupon_supports_product($order_item, $coupon) * * @return array */ - protected function build_discount_item_for_subscription($coupon, $plan_cycles = 0) + protected function build_discount_item_for_subscription($coupon, $order_item, $plan_cycles = 0) { $discount_item = []; - $amount = $coupon->get_amount(); $discount_type = $coupon->get_discount_type(); if ($discount_type == 'fixed_cart') { + $total_cart = WC()->cart->subtotal; + $percentage_item = $order_item['subtotal'] / $total_cart; + $discount_value = $percentage_item * $amount; $discount_item['discount_type'] = 'amount'; - $discount_item['amount'] = $amount / $this->order->get_item_count(); + $discount_item['amount'] = (float) $discount_value; $discount_item['cycles'] = 1; return $discount_item; + } elseif ($discount_type == 'fixed_product') { + $discount_value = $order_item['quantity'] * $amount; + $discount_item['discount_type'] = 'amount'; + $discount_item['amount'] = (float) $discount_value; } elseif (strpos($discount_type, 'fixed') !== false) { $discount_item['discount_type'] = 'amount'; - $discount_item['amount'] = $amount; - } elseif (strpos($discount_type, 'percent') !== false || - strpos($discount_type, 'recurring_percent') !== false) { + $discount_item['amount'] = (float) $amount; + } elseif (strpos($discount_type, 'percent') !== false ||strpos($discount_type, 'recurring_percent') !== false) { $discount_item['discount_type'] = 'amount'; - $discount_item['amount'] = $this->order->get_discount_total() / $this->order->get_item_count(); + $discount_item['amount'] = abs($amount / 100 * ($order_item['price'] * $order_item['quantity'])); } $discount_item['cycles'] = $this->config_discount_cycles($coupon, $plan_cycles); - return $discount_item; } @@ -919,16 +1033,17 @@ protected function build_discount_item_for_subscription($coupon, $plan_cycles = */ protected function config_discount_cycles($coupon, $plan_cycles = 0) { - $cycle_count = get_post_meta($coupon->id, 'cycle_count', true); - + $cycle_count = get_post_meta($coupon->get_id(), 'cycle_count', true); + if ($coupon->get_discount_type() == 'recurring_percent') { - $cycle_count = WC_Subscriptions_Coupon::get_coupon_limit($coupon->id); + $subscriptions_coupon = new WC_Subscriptions_Coupon(); + $cycle_count = $subscriptions_coupon->get_coupon_limit($coupon->get_id()); } - + if ($cycle_count == 0) { return null; } - + return $this->get_plan_length($cycle_count, $plan_cycles); } @@ -1056,7 +1171,7 @@ private function cancel_subscriptions_and_order($wc_subscriptions_ids, $subscrip { $this->suspend_subscriptions($subscriptions_ids); - foreach($wc_subscriptions_ids as $wc_subscription_id) { + foreach ($wc_subscriptions_ids as $wc_subscription_id) { $wc_subscription = wcs_get_subscription($wc_subscription_id); $wc_subscription->update_status('cancelled', __($message, VINDI)); } @@ -1079,19 +1194,18 @@ private function cancel_subscriptions_and_order($wc_subscriptions_ids, $subscrip */ protected function create_bill($customer_id, $order_items) { - $data = array( 'customer_id' => $customer_id, 'payment_method_code' => $this->payment_method_code(), 'bill_items' => $this->build_product_items($order_items, 'bill'), - 'code' => $this->order->id, + 'code' => $this->order->get_id(), 'installments' => $this->installments(), ); $bill = $this->routes->createBill($data); if (!$bill) { - $this->logger->log(sprintf('Erro no pagamento do pedido %s.', $this->order->id)); + $this->logger->log(sprintf('Erro no pagamento do pedido %s.', $this->order->get_id())); $message = sprintf(__('Pagamento Falhou. (%s)', VINDI), $this->vindi_settings->api->last_error); $this->order->update_status('failed', $message); @@ -1102,6 +1216,7 @@ protected function create_bill($customer_id, $order_items) $this->logger->log(sprintf('Update Bill: %s', json_encode($bill))); $this->order->save(); } + return $bill; } @@ -1117,13 +1232,15 @@ protected function create_bill_meta_for_order($bill) $bill_meta = []; $bill_meta['id'] = $bill['id']; $bill_meta['status'] = $bill['status']; - + if (isset($bill['charges']) && count($bill['charges'])) { $charges = end($bill['charges']); $bill_meta['bank_slip_url'] = $charges['print_url'] ?? ''; - - if (array_intersect([$this->payment_method_code()], ['pix', 'pix_bank_slip']) - && isset($charges['last_transaction']['gateway_response_fields'])) { + + if ( + array_intersect([$this->payment_method_code()], ['pix', 'pix_bank_slip']) + && isset($charges['last_transaction']['gateway_response_fields']) + ) { $transaction = $charges['last_transaction']['gateway_response_fields']; $bill_meta['charge_id'] = $charges['id']; $bill_meta['pix_expiration'] = $transaction['max_days_to_keep_waiting_payment'] ?? ''; @@ -1246,7 +1363,7 @@ private function update_order_status($bills_status) * @return WC_Product Woocommerce product array with a vindi id */ protected function get_product($order_item) - { + { $product = $order_item->get_product(); $product_id = $order_item->get_id(); @@ -1264,11 +1381,10 @@ protected function get_product($order_item) if (empty($vindi_product_id) || !$vindi_product_id) { $vindi_product_id = $this->routes->findProductByCode('WC-' . $product->id)['id']; - } $product->vindi_id = (int) $vindi_product_id > 0 ? $vindi_product_id : 63; - if($product->id === null) $product->id = 63; + if ($product->id === null) $product->id = 63; return $product; } @@ -1291,13 +1407,11 @@ public function order_has_trial_and_simple_product() if ($has_simple_product) { return true; } - } else { $has_simple_product = true; if ($has_trial) { return true; } - } } return $has_trial && $has_simple_product; @@ -1311,7 +1425,7 @@ public function order_has_trial_and_simple_product() */ protected function is_variable(WC_Product $product) { - return (boolean) preg_match('/variation/', $product->get_type()); + return (bool) preg_match('/variation/', $product->get_type()); } /** @@ -1322,7 +1436,7 @@ protected function is_variable(WC_Product $product) */ protected function is_subscription_type(WC_Product $product) { - return (boolean) preg_match('/subscription/', $product->get_type()); + return (bool) preg_match('/subscription/', $product->get_type()); } /** @@ -1378,6 +1492,5 @@ protected function get_vindi_code(String $product) } catch (Exception $err) { return $product; } - } } diff --git a/vindi.php b/vindi.php index ac74a774..66125627 100644 --- a/vindi.php +++ b/vindi.php @@ -6,7 +6,7 @@ * Description: Adiciona o gateway de pagamento da Vindi para o WooCommerce. * Author: Vindi * Author URI: https://www.vindi.com.br - * Version: 1.3.0 + * Version: 1.3.2 * Requires at least: 4.4 * Tested up to: 6.4 * Text Domain: vindi-payment-gateway