diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000..62d7331 Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/assets/.DS_Store b/src/assets/.DS_Store new file mode 100644 index 0000000..10f526f Binary files /dev/null and b/src/assets/.DS_Store differ diff --git a/src/assets/images/.DS_Store b/src/assets/images/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/src/assets/images/.DS_Store differ diff --git a/src/controllers/PlansController.php b/src/controllers/PlansController.php index f2d06a3..9f3ba7b 100644 --- a/src/controllers/PlansController.php +++ b/src/controllers/PlansController.php @@ -92,7 +92,7 @@ function create($post_id, $post, $update, $recreated = false) $createdProduct = !empty($vindi_product_id) ? $this->routes->findProductById($vindi_product_id) : $this->routes->createProduct(array( - 'name' => PREFIX_PRODUCT . $data['name'], + 'name' => VINDI_PREFIX_PRODUCT . $data['name'], 'code' => 'WC-' . $data['id'], 'status' => ($data['status'] == 'publish') ? 'active' : 'inactive', 'description' => $data['description'], @@ -106,7 +106,7 @@ function create($post_id, $post, $update, $recreated = false) // Creates the plan within the Vindi $createdPlan = $this->routes->createPlan(array( - 'name' => PREFIX_PLAN . $data['name'], + 'name' => VINDI_PREFIX_PLAN . $data['name'], 'interval' => $plan_interval['interval'], 'interval_count' => $plan_interval['interval_count'], 'billing_trigger_type' => 'beginning_of_period', @@ -160,7 +160,7 @@ function create($post_id, $post, $update, $recreated = false) $createdProduct = !empty($vindi_product_id) ? $this->routes->findProductById($vindi_product_id) : $this->routes->createProduct(array( - 'name' => PREFIX_PRODUCT . $data['name'], + 'name' => VINDI_PREFIX_PRODUCT . $data['name'], 'code' => 'WC-' . $data['id'], 'status' => ($data['status'] == 'publish') ? 'active' : 'inactive', 'description' => $data['description'], @@ -174,7 +174,7 @@ function create($post_id, $post, $update, $recreated = false) // Creates the plan within the Vindi $createdPlan = $this->routes->createPlan(array( - 'name' => PREFIX_PLAN . $data['name'], + 'name' => VINDI_PREFIX_PLAN . $data['name'], 'interval' => $plan_interval['interval'], 'interval_count' => $plan_interval['interval_count'], 'billing_trigger_type' => 'beginning_of_period', @@ -270,7 +270,7 @@ function update($post_id) $updatedProduct = $this->routes->updateProduct( $vindi_product_id, array( - 'name' => PREFIX_PRODUCT . $data['name'], + 'name' => VINDI_PREFIX_PRODUCT . $data['name'], 'code' => 'WC-' . $data['id'], 'status' => ($data['status'] == 'publish') ? 'active' : 'inactive', 'description' => $data['description'], @@ -286,7 +286,7 @@ function update($post_id) $updatedPlan = $this->routes->updatePlan( $vindi_plan_id, array( - 'name' => PREFIX_PLAN . $data['name'], + 'name' => VINDI_PREFIX_PLAN . $data['name'], 'interval' => $plan_interval['interval'], 'interval_count' => $plan_interval['interval_count'], 'billing_trigger_type' => 'beginning_of_period', @@ -333,7 +333,7 @@ function update($post_id) $updatedProduct = $this->routes->updateProduct( $vindi_product_id, array( - 'name' => PREFIX_PRODUCT . $data['name'], + 'name' => VINDI_PREFIX_PRODUCT . $data['name'], 'code' => 'WC-' . $data['id'], 'status' => ($data['status'] == 'publish') ? 'active' : 'inactive', 'description' => $data['description'], @@ -351,7 +351,7 @@ function update($post_id) $updatedPlan = $this->routes->updatePlan( $vindi_plan_id, array( - 'name' => PREFIX_PLAN . $data['name'], + 'name' => VINDI_PREFIX_PLAN . $data['name'], 'interval' => $plan_interval['interval'], 'interval_count' => $plan_interval['interval_count'], 'billing_trigger_type' => 'beginning_of_period', diff --git a/src/controllers/ProductController.php b/src/controllers/ProductController.php index 5c2baef..f5b8fe6 100644 --- a/src/controllers/ProductController.php +++ b/src/controllers/ProductController.php @@ -73,7 +73,7 @@ function create($post_id, $post, $update, $recreated = false) // Creates the product within the Vindi $createdProduct = $this->routes->createProduct(array( - 'name' => PREFIX_PRODUCT . $data['name'], + 'name' => VINDI_PREFIX_PRODUCT . $data['name'], 'code' => 'WC-' . $data['id'], 'status' => ($data['status'] == 'publish') ? 'active' : 'inactive', 'description' => $data['description'], @@ -120,7 +120,7 @@ function update($post_id) $updatedProduct = $this->routes->updateProduct( $vindi_product_id, array( - 'name' => PREFIX_PRODUCT . $data['name'], + 'name' => VINDI_PREFIX_PRODUCT . $data['name'], 'code' => 'WC-' . $data['id'], 'status' => ($data['status'] == 'publish') ? 'active' : 'inactive', 'description' => $data['description'], diff --git a/src/includes/admin/CouponsMetaBox.php b/src/includes/admin/CouponsMetaBox.php index 34c8ce9..07ccf47 100644 --- a/src/includes/admin/CouponsMetaBox.php +++ b/src/includes/admin/CouponsMetaBox.php @@ -49,11 +49,11 @@ public static function output( $coupon_id, $coupon ) public static function save($post_id, $post) { // Check the nonce (again). - if ( empty( $_POST['woocommerce_meta_nonce'] ) || ! wp_verify_nonce( $_POST['woocommerce_meta_nonce'], 'woocommerce_save_data' ) ) { + if ( empty( VindiHelpers::sanitize_xss($_POST['woocommerce_meta_nonce']) ) || ! wp_verify_nonce( VindiHelpers::sanitize_xss($_POST['woocommerce_meta_nonce']), 'woocommerce_save_data' ) ) { return; } $coupon = new WC_Coupon( $post_id ); - $coupon->update_meta_data('cycle_count', intval($_POST['cycle_count'])); + $coupon->update_meta_data('cycle_count', intval(filter_var($_POST['cycle_count'], FILTER_SANITIZE_NUMBER_INT))); $coupon->save(); } diff --git a/src/routes/RoutesApi.php b/src/routes/RoutesApi.php index 323f0d4..5a459c9 100644 --- a/src/routes/RoutesApi.php +++ b/src/routes/RoutesApi.php @@ -30,10 +30,9 @@ function __construct(VindiSettings $vindi_settings) */ public function findProductById($product_id) { - $response = $this->api->request(sprintf( 'products/%s', - $product_id + filter_var($product_id, FILTER_SANITIZE_NUMBER_INT) ), 'GET'); $productExists = isset($response['product']['id']) ? $response['product'] : false; @@ -69,7 +68,7 @@ public function updatePlan($plan_id, $data) $response = $this->api->request(sprintf( 'plans/%s', - $plan_id + filter_var($plan_id, FILTER_SANITIZE_NUMBER_INT) ), 'PUT', $data); return $response['plan']; @@ -101,7 +100,7 @@ public function updateProduct($product_id, $data) $response = $this->api->request(sprintf( 'products/%s', - $product_id + filter_var($product_id, FILTER_SANITIZE_NUMBER_INT) ), 'PUT', $data); return $response['product']; } @@ -132,7 +131,7 @@ public function updateCustomer($user_id, $data) $response = $this->api->request(sprintf( 'customers/%s', - $user_id + filter_var($user_id, FILTER_SANITIZE_NUMBER_INT) ), 'PUT', $data); return $response['customer']; @@ -150,7 +149,7 @@ public function deleteCustomer($user_id) $response = $this->api->request(sprintf( 'customers/%s', - $user_id + filter_var($user_id, FILTER_SANITIZE_NUMBER_INT) ), 'DELETE'); return $response['customer']; @@ -169,7 +168,7 @@ public function findCustomerById($id) $response = $this->api->request(sprintf( 'customers/%s', - $id + filter_var($id, FILTER_SANITIZE_NUMBER_INT) ), 'GET'); $userExists = isset($response['customer']['id']) ? $response['customer'] : false; @@ -210,7 +209,7 @@ public function suspendSubscription($subscription_id, $cancel_bills = false) $query = '?cancel_bills=false'; $response = $this->api->request( - sprintf('subscriptions/%s%s', $subscription_id, $query), 'DELETE'); + sprintf('subscriptions/%s%s', filter_var($subscription_id, FILTER_SANITIZE_NUMBER_INT), $query), 'DELETE'); return $response; } @@ -222,7 +221,7 @@ public function suspendSubscription($subscription_id, $cancel_bills = false) */ public function activateSubscription($subscription_id) { - if ($response = $this->api->request('subscriptions/' . $subscription_id . '/reactivate', 'POST')) + if ($response = $this->api->request('subscriptions/' . filter_var($subscription_id, FILTER_SANITIZE_NUMBER_INT) . '/reactivate', 'POST')) return $response; return false; @@ -235,7 +234,7 @@ public function activateSubscription($subscription_id) */ public function getSubscription($subscription_id) { - if ($response = $this->api->request("subscriptions/{$subscription_id}",'GET')['subscription']) + if ($response = $this->api->request("subscriptions/". filter_var($subscription_id, FILTER_SANITIZE_NUMBER_INT),'GET')['subscription']) return $response; return false; @@ -248,6 +247,7 @@ public function getSubscription($subscription_id) */ public function isSubscriptionActive($subscription_id) { + $subscription_id = filter_var($subscription_id, FILTER_SANITIZE_NUMBER_INT); if (isset($this->recentRequest) && $this->recentRequest['id'] == $subscription_id) { if ($this->recentRequest['status'] != 'canceled') @@ -270,7 +270,7 @@ public function verifyCustomerPaymentProfile($payment_profile_id) { return 'success' === $this->api->request(sprintf( 'payment_profiles/%s/verify', - $payment_profile_id + filter_var($payment_profile_id, FILTER_SANITIZE_NUMBER_INT) ), 'POST')['transaction']['status']; } @@ -288,6 +288,7 @@ public function createCustomerPaymentProfile($data) public function findProductByCode($code) { + $code = sanitize_text_field($code); $transient_key = "vindi_product_{$code}"; $product = get_transient($transient_key); @@ -306,6 +307,8 @@ public function findProductByCode($code) public function findOrCreateProduct($name, $code) { + $name = sanitize_text_field($name); + $code = sanitize_text_field($code); $product = $this->findProductByCode($code); if (false === $product) @@ -340,7 +343,7 @@ public function deleteBill($bill_id, $comments = '') $query = '?comments= ' . $comments; if ($response = $this->api->request( - sprintf('bills/%s%s', $bill_id, $query), 'DELETE') + sprintf('bills/%s%s', filter_var($bill_id, FILTER_SANITIZE_NUMBER_INT), $query), 'DELETE') ) { return $response; } @@ -416,7 +419,7 @@ public function getMerchant($is_config = false) public function getCharge($charge_id) { - $response = $this->api->request("charges/{$charge_id}", 'GET'); + $response = $this->api->request("charges/" . filter_var($charge_id, FILTER_SANITIZE_NUMBER_INT), 'GET'); if (empty($response['charge'])) return false; @@ -426,7 +429,7 @@ public function getCharge($charge_id) public function getPlan($plan_id) { - $response = $this->api->request("plans/{$plan_id}", 'GET'); + $response = $this->api->request("plans/" . filter_var($plan_id, FILTER_SANITIZE_NUMBER_INT), 'GET'); if (empty($response['plan'])) { $this->current_plan = false; @@ -438,6 +441,7 @@ public function getPlan($plan_id) public function getPaymentProfile($user_vindi_id) { + $user_vindi_id = filter_var($user_vindi_id, FILTER_SANITIZE_NUMBER_INT); $customer = $this->findCustomerById($user_vindi_id); if (empty($customer)) @@ -473,7 +477,7 @@ public function findBillById($bill_id) $response = $this->api->request(sprintf( 'bills/%s', - $bill_id + filter_var($bill_id, FILTER_SANITIZE_NUMBER_INT) ), 'GET'); if (isset($response['bill'])) @@ -484,7 +488,7 @@ public function findBillById($bill_id) public function refundCharge($charge_id, $data) { - $response = $this->api->request(sprintf('charges/%s/refund', $charge_id), 'POST', $data); + $response = $this->api->request(sprintf('charges/%s/refund', filter_var($charge_id, FILTER_SANITIZE_NUMBER_INT)), 'POST', $data); if (isset($response['charge'])) { return $response['charge']; diff --git a/src/services/VindiHelpers.php b/src/services/VindiHelpers.php index 534064d..ab963e2 100644 --- a/src/services/VindiHelpers.php +++ b/src/services/VindiHelpers.php @@ -155,5 +155,17 @@ public static function get_matching_subscription_item($subscription, $order_item } return $matching_item; - } + } + + /** + * Sanitize user input to prevent XSS atacks. + * + * @since 1.0.0 + * @param string $value. String to be sanitized. + * + * @return string + */ + public static function sanitize_xss($value) { + return htmlspecialchars(strip_tags($value)); + } } diff --git a/src/utils/DefinitionVariables.php b/src/utils/DefinitionVariables.php index c9f88bf..2bda133 100644 --- a/src/utils/DefinitionVariables.php +++ b/src/utils/DefinitionVariables.php @@ -20,6 +20,6 @@ define('VINDI_URL', plugins_url('/', VINDI_FILE)); } -define('PREFIX_PRODUCT', '[WC] '); +define('VINDI_PREFIX_PRODUCT', '[WC] '); -define('PREFIX_PLAN', '[WC] '); +define('VINDI_PREFIX_PLAN', '[WC] '); diff --git a/src/utils/InterestPriceHandler.php b/src/utils/InterestPriceHandler.php index d227c19..230d91f 100644 --- a/src/utils/InterestPriceHandler.php +++ b/src/utils/InterestPriceHandler.php @@ -41,18 +41,18 @@ public function calculate_cost($cart) { } if (isset($_POST['post_data'] ) ) { - parse_str($_POST['post_data'], $post_data ); + parse_str(sanitize_text_field($_POST['post_data']), $post_data); } else { $post_data = $_POST; } if (isset($post_data['vindi_cc_installments']) && - $post_data['vindi_cc_installments'] > 1 && + filter_var($post_data['vindi_cc_installments'], FILTER_SANITIZE_NUMBER_INT) > 1 && $post_data['payment_method'] === 'vindi-credit-card' ) { global $woocommerce; $interest_rate = get_option('woocommerce_vindi-credit-card_settings', true)['interest_rate']; - $installments = intval($post_data['vindi_cc_installments']); + $installments = intval(filter_var($post_data['vindi_cc_installments'], FILTER_SANITIZE_NUMBER_INT)); $tax_total = 0; $taxes = $cart->get_taxes(); foreach($taxes as $tax) $tax_total += $tax; diff --git a/src/utils/PaymentGateway.php b/src/utils/PaymentGateway.php index 00e7348..1133dcd 100644 --- a/src/utils/PaymentGateway.php +++ b/src/utils/PaymentGateway.php @@ -147,7 +147,7 @@ public function admin_options() public function get_country_code() { if (isset($_GET['order_id'])) { - $order = new WC_Order($_GET['order_id']); + $order = new WC_Order(filter_var($_GET['order_id'], FILTER_SANITIZE_NUMBER_INT)); return $order->billing_country; } elseif ($this->vindi_settings->woocommerce->customer->get_billing_country()) { return $this->vindi_settings->woocommerce->customer->get_billing_country(); @@ -174,6 +174,7 @@ public function validate_settings() */ public function process_payment($order_id) { + $order_id = filter_var($order_id, FILTER_SANITIZE_NUMBER_INT); $this->logger->log(sprintf('Processando pedido %s.', $order_id)); $order = wc_get_order($order_id); $payment = new VindiPaymentProcessor($order, $this, $this->vindi_settings, $this->controllers); @@ -225,6 +226,9 @@ protected function is_single_order() * @return bool|WP_Error */ public function process_refund($order_id, $amount = null, $reason = '') { + $order_id = filter_var($order_id, FILTER_SANITIZE_NUMBER_INT); + $amount = filter_var($amount, FILTER_SANITIZE_NUMBER_FLOAT); + $reason = sanitize_text_field($reason); $order = wc_get_order($order_id); if (!$this->can_refund_order($order)) { @@ -262,12 +266,15 @@ public function process_refund($order_id, $amount = null, $reason = '') { /** * Get refund request args. * - * @param WC_Order $order Order object. + * @param int $bill_id Order object. * @param float $amount Refund amount. * @param string $reason Refund reason. * @return array */ public function get_refund_request($bill_id, $amount = null, $reason = '') { + $bill_id = filter_var($bill_id, FILTER_SANITIZE_NUMBER_INT); + $amount = filter_var($amount, FILTER_SANITIZE_NUMBER_FLOAT); + $reason = sanitize_text_field($reason); $request = array( 'cancel_bill' => true, 'comments' => strip_tags(wc_trim_string($reason, 255)), @@ -281,12 +288,16 @@ public function get_refund_request($bill_id, $amount = null, $reason = '') { /** * Refund an order via PayPal. * - * @param WC_Order $order Order object. + * @param int $bill_id Order object. * @param float $amount Refund amount. * @param string $reason Refund reason. * @return object Either an object of name value pairs for a success, or a WP_ERROR object. */ public function refund_transaction($bill_id, $amount = null, $reason = '') { + $bill_id = filter_var($bill_id, FILTER_SANITIZE_NUMBER_INT); + $amount = filter_var($amount, FILTER_SANITIZE_NUMBER_FLOAT); + $reason = sanitize_text_field($reason); + $data = $this->get_refund_request($bill_id, $amount, $reason); $last_charge = $this->find_bill_last_charge($bill_id); $charge_id = $last_charge['id']; @@ -301,6 +312,7 @@ public function refund_transaction($bill_id, $amount = null, $reason = '') { private function find_bill_last_charge($bill_id) { + $bill_id = filter_var($bill_id, FILTER_SANITIZE_NUMBER_INT); $bill = $this->routes->findBillById($bill_id); if(!$bill) { diff --git a/src/utils/PaymentProcessor.php b/src/utils/PaymentProcessor.php index 6bcbca6..e5ccfd5 100644 --- a/src/utils/PaymentProcessor.php +++ b/src/utils/PaymentProcessor.php @@ -2,1049 +2,1099 @@ class VindiPaymentProcessor { - /** - * Order type is invalid. - */ - const ORDER_TYPE_INVALID = 0; - - /** - * Order type is Subscription Payment. - */ - const ORDER_TYPE_SUBSCRIPTION = 1; - - /** - * Order type is Single Payment. - */ - const ORDER_TYPE_SINGLE = 2; - - /** - * @var WC_Order - */ - private $order; - - /** - * @var VindiPaymentGateway - */ - private $gateway; - - /** - * @var VindiSettings - */ - private $vindi_settings; - - /** - * @var VindiLogger - */ - private $logger; - - /** - * @var VindiRoutes - */ - private $routes; - - /** - * @var VindiControllers - */ - private $controllers; - - /** - * @var bool - */ - private $shipping_added; - - /** - * Payment Processor contructor. - * - * @param WC_Order $order The order to be processed - * @param VindiPaymentGateway $gateway Current payment gateway - * @param VindiSettings $vindi_settings The VindiSettings instance - * @param VindiControllers $controllers VindiControllers instance - */ - function __construct(WC_Order $order, VindiPaymentGateway $gateway, VindiSettings $vindi_settings, VindiControllers $controllers) - { - $this->order = $order; - $this->gateway = $gateway; - $this->vindi_settings = $vindi_settings; - $this->logger = $vindi_settings->logger; - $this->routes = $vindi_settings->routes; - $this->controllers = $controllers; - $this->shipping_added = false; - } - - /** - * Check if the order contains any subscription. - * - * @return int Order type - */ - public function get_order_type() - { - if (function_exists('wcs_order_contains_subscription') && wcs_order_contains_subscription($this->order, array('any'))) { - return static::ORDER_TYPE_SUBSCRIPTION; + /** + * Order type is invalid. + */ + const ORDER_TYPE_INVALID = 0; + + /** + * Order type is Subscription Payment. + */ + const ORDER_TYPE_SUBSCRIPTION = 1; + + /** + * Order type is Single Payment. + */ + const ORDER_TYPE_SINGLE = 2; + + /** + * @var WC_Order + */ + private $order; + + /** + * @var VindiPaymentGateway + */ + private $gateway; + + /** + * @var VindiSettings + */ + private $vindi_settings; + + /** + * @var VindiLogger + */ + private $logger; + + /** + * @var VindiRoutes + */ + private $routes; + + /** + * @var VindiControllers + */ + private $controllers; + + /** + * @var bool + */ + private $shipping_added; + + /** + * Payment Processor contructor. + * + * @param WC_Order $order The order to be processed + * @param VindiPaymentGateway $gateway Current payment gateway + * @param VindiSettings $vindi_settings The VindiSettings instance + * @param VindiControllers $controllers VindiControllers instance + */ + public function __construct(WC_Order $order, VindiPaymentGateway $gateway, VindiSettings $vindi_settings, VindiControllers $controllers) + { + $this->order = $order; + $this->gateway = $gateway; + $this->vindi_settings = $vindi_settings; + $this->logger = $vindi_settings->logger; + $this->routes = $vindi_settings->routes; + $this->controllers = $controllers; + $this->shipping_added = false; } - return static::ORDER_TYPE_SINGLE; - } - - - /** - * Find or create a customer within Vindi using the given credentials. - * - * @return array Vindi customer array - */ - public function get_customer() - { - $current_user = wp_get_current_user(); - $vindi_customer_id = get_user_meta($current_user->ID, 'vindi_customer_id', true); - $vindi_customer = $this->routes->findCustomerById($vindi_customer_id); - if(!$vindi_customer) { - $vindi_customer = $this->controllers->customers->create($current_user->ID, $this->order); + /** + * Check if the order contains any subscription. + * + * @return int Order type + */ + public function get_order_type() + { + if (function_exists('wcs_order_contains_subscription') && wcs_order_contains_subscription($this->order, array('any'))) { + return static::ORDER_TYPE_SUBSCRIPTION; + } + + return static::ORDER_TYPE_SINGLE; } + /** + * Find or create a customer within Vindi using the given credentials. + * + * @return array Vindi customer array + */ + public function get_customer() + { + $current_user = wp_get_current_user(); - // if($this->vindi_settings->send_nfe_information()) { - $vindi_customer = $this->controllers->customers->update($current_user->ID, $this->order); - // } - - if ($this->is_cc()) - $this->create_payment_profile($vindi_customer['id']); - - $this->logger->log(sprintf('Cliente Vindi: %s', $vindi_customer['id'])); - - return $vindi_customer; - } - - /** - * Build the credit card payment type. - * - * @param int $customer_id Vindi customer id - * - * @return array - */ - public function get_cc_payment_type($customer_id) - { - if ($this->gateway->verify_user_payment_profile()) return false; - - return array( - 'customer_id' => $customer_id, - 'holder_name' => $_POST['vindi_cc_fullname'], - 'card_expiration' => $_POST['vindi_cc_monthexpiry'] . '/' . $_POST['vindi_cc_yearexpiry'], - 'card_number' => $_POST['vindi_cc_number'], - 'card_cvv' => $_POST['vindi_cc_cvc'], - 'payment_method_code' => $this->payment_method_code() , - 'payment_company_code' => $_POST['vindi_cc_paymentcompany'], - ); - } - - /** - * Check if payment method is "Credit Card" - * - * @return bool - */ - public function is_cc() - { - return 'cc' === $this->gateway->type(); - } - - /** - * Check if payment method is "Bank Slip" - * - * @return bool - */ - public function is_bank_slip() - { - return 'bank_slip' === $this->gateway->type(); - } - - /** - * Retrieve payment method code - * - * @return string Vindi payment method code - */ - public function payment_method_code() - { - return $this->is_cc() ? 'credit_card' : 'bank_slip'; - } - - /** - * Interrupt the payment process and throw an error if needed. - * Log the message, add it to the order note and send an alert to the user - * - * @param string $message The error message - * @param bool $throw_exception When true an exception is thrown - * - * @return bool Always returns false - * - * @throws Exception - */ - public function abort($message, $throw_exception = false) - { - $this->logger->log($message); - $this->order->add_order_note($message); - wc_add_notice($message, 'error'); - if ($throw_exception) throw new Exception($message); - - return false; - } - - /** - * Check if the order type is valid and process it. - * - * @return array|void - * - * @throws Exception - */ - public function process() - { - switch ($orderType = $this->get_order_type()) { - case static ::ORDER_TYPE_SINGLE: - case static ::ORDER_TYPE_SUBSCRIPTION: - return $this->process_order(); - case static ::ORDER_TYPE_INVALID: - default: - return $this->abort(__('Falha ao processar carrinho de compras. Verifique os itens escolhidos e tente novamente.', VINDI) , true); - } - } - - /** - * Process current order. - * - * @return array - * - * @throws Exception - */ - public function process_order() - { - if($this->order_has_trial_and_simple_product()) { - $message = __('Não é possível comprar produtos simples e assinaturas com trial no mesmo pedido!', VINDI); - $this->order->update_status('failed', $message); - wc_add_notice($message, 'error'); - - throw new Exception($message); - return false; - } + $vindi_customer_id = get_user_meta($current_user->ID, 'vindi_customer_id', true); + // $vindi_customer = $this->routes->findCustomerById($vindi_customer_id); + $vindi_customer = false; + if (isset($vindi_customer_id) && !empty($vindi_customer_id)) { + $vindi_customer = $this->routes->findCustomerById($vindi_customer_id); + } + if (!$vindi_customer) { + $vindi_customer = $this->controllers->customers->create($current_user->ID, $this->order); + } + + // if($this->vindi_settings->send_nfe_information()) { + $vindi_customer = $this->controllers->customers->update($current_user->ID, $this->order); - $customer = $this->get_customer(); - $order_items = $this->order->get_items(); - $bills = []; - $order_post_meta = []; - $bill_products = []; - $subscriptions_ids = []; - foreach ($order_items as $order_item) { - $product = $order_item->get_product(); - - if($this->is_subscription_type($product)) { - $subscription = $this->create_subscription($customer['id'], $order_item); - $subscription_order_post_meta = []; - $subscription_id = $subscription['id']; - array_push($subscriptions_ids, $subscription_id); - $wc_subscription_id = $subscription['wc_id']; - $subscription_bill = $subscription['bill']; - $order_post_meta[$subscription_id]['cycle'] = $subscription['current_period']['cycle']; - $order_post_meta[$subscription_id]['product'] = $product->name; - $order_post_meta[$subscription_id]['bill'] = $this->create_bill_meta_for_order($subscription_bill); - - $subscription_order_post_meta[$subscription_id]['cycle'] = $subscription['current_period']['cycle']; - $subscription_order_post_meta[$subscription_id]['product'] = $product->name; - $subscription_order_post_meta[$subscription_id]['bill'] = $this->create_bill_meta_for_order($subscription_bill); - $bills[] = $subscription['bill']; - if ($message = $this->cancel_if_denied_bill_status($subscription['bill'])) { - $wc_subscription = wcs_get_subscription($wc_subscription_id); - $wc_subscription->update_status('cancelled', __($message, VINDI)); - $this->order->update_status('cancelled', __($message, VINDI)); - $this->suspend_subscriptions($subscriptions_ids); - $this->cancel_bills($bills, __('Algum pagamento do pedido não pode ser processado', VINDI)); - $this->abort(__($message, VINDI) , true); + // } + + if ($this->is_cc()) { + $this->create_payment_profile($vindi_customer['id']); } - update_post_meta($wc_subscription_id, 'vindi_subscription_id', $subscription_id); - update_post_meta($wc_subscription_id, 'vindi_order', $subscription_order_post_meta); - continue; - } + $this->logger->log(sprintf('Cliente Vindi : %s', json_encode($vindi_customer), 1)); - $bill_products[] = $order_item; + return $vindi_customer; } - if(!empty($bill_products)) { - $single_payment_bill = $this->create_bill($customer['id'], $bill_products); - $order_post_meta['single_payment']['product'] = 'Produtos Avulsos'; - $order_post_meta['single_payment']['bill'] = $this->create_bill_meta_for_order($single_payment_bill); - $bills[] = $single_payment_bill; - if ($message = $this->cancel_if_denied_bill_status($single_payment_bill)) { - $this->order->update_status('cancelled', __($message, VINDI)); - $this->suspend_subscriptions($subscriptions_ids); - $this->cancel_bills($bills, __('Algum pagamento do pedido não pode ser processado', VINDI)); - $this->abort(__($message, VINDI) , true); - } + /** + * Build the credit card payment type. + * + * @param int $customer_id Vindi customer id + * + * @return array + */ + public function get_cc_payment_type($customer_id) + { + if ($this->gateway->verify_user_payment_profile()) { + return false; + } + + return array( + 'customer_id' => $customer_id, + 'holder_name' => sanitize_text_field($_POST['vindi_cc_fullname']), + 'card_expiration' => filter_var($_POST['vindi_cc_monthexpiry'], FILTER_SANITIZE_NUMBER_INT) . '/' . filter_var($_POST['vindi_cc_yearexpiry'], FILTER_SANITIZE_NUMBER_INT), + 'card_number' => filter_var($_POST['vindi_cc_number'], FILTER_SANITIZE_NUMBER_INT), + 'card_cvv' => filter_var($_POST['vindi_cc_cvc'], FILTER_SANITIZE_NUMBER_INT), + 'payment_method_code' => $this->payment_method_code(), + 'payment_company_code' => sanitize_text_field($_POST['vindi_cc_paymentcompany']), + ); } - update_post_meta($this->order->id, 'vindi_order', $order_post_meta); - - WC()->session->__unset('current_payment_profile'); - WC()->session->__unset('current_customer'); - - remove_action('woocommerce_scheduled_subscription_payment', 'WC_Subscriptions_Manager::prepare_renewal'); - - return $this->finish_payment($bills); - } - - - - /** - * Create a payment profile for the customer - * - * @param int $customer_id Vindi customer id - * - * @throws Exception - */ - protected function create_payment_profile($customer_id) - { - $cc_info = $this->get_cc_payment_type($customer_id); - - if (false === $cc_info) - return; - - $payment_profile = $this->routes->createCustomerPaymentProfile($cc_info); - - if (!$payment_profile) - $this->abort(__('Falha ao registrar o método de pagamento. Verifique os dados e tente novamente.', VINDI) , true); - - if ($this->gateway->verify_method()) - $this->verify_payment_profile($payment_profile['id']); - } - - /** - * Check if the payment profile is valid - * - * @param int $payment_profile_id The customer's payment profile id - * - * @throws Exception - */ - 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); - } - - /** - * Get the subscription/product expiration time in months - * - * @param WC_Order_Item_Product $item - * - * @return int - */ - private function get_cycle_from_product_type($item) - { - $product = method_exists($item, 'get_product') ? $item->get_product() : false; - if ($item['type'] == 'shipping' || $item['type'] == 'tax') { - if ($this->vindi_settings->get_shipping_and_tax_config()) return 1; + /** + * Check if payment method is "Credit Card" + * + * @return bool + */ + public function is_cc() + { + return 'cc' === $this->gateway->type(); } - elseif (!$this->is_subscription_type($product) || $this->is_one_time_shipping($product)) { - return 1; + + /** + * Check if payment method is "Bank Slip" + * + * @return bool + */ + public function is_bank_slip() + { + return 'bank_slip' === $this->gateway->type(); } - $cycles = get_post_meta($product->get_id(), '_subscription_length', true); - return $cycles > 0 ? $cycles : null; - } - - /** - * Check if the product needs to be shipped only once - * - * @param WC_Product $product Woocommerce product - * - * @return bool - */ - private function is_one_time_shipping($product) - { - return get_post_meta($product->get_id(), '_subscription_one_time_shipping', true) == 'yes'; - } - - /** - * Build the array of product(s), shipping, tax and discounts to send to Vindi - * - * @param string $order_type Order type. Possible values 'bill' and 'subscription', defaults to 'bill' - * @param WC_Order_Item_Product|WC_Order_Item_Product[] $product The product to be built. If the order type is 'bill' this will be an array of WC_Order_Item_Product, - * if it's 'subscription' it will be a single WC_Order_Item_Product. - * - * @return array - * - * @throws Exception - */ - public function build_product_items($order_type = 'bill', $product) - { - $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); + + /** + * Retrieve payment method code + * + * @return string Vindi payment method code + */ + public function payment_method_code() + { + return $this->is_cc() ? 'credit_card' : 'bank_slip'; } - $product_items = []; - $order_items = []; - if('bill' === $order_type) { - $order_items = $this->build_product_from_order_item($order_type, $product); - } else { - $order_items[] = $this->build_product_from_order_item($order_type, $product); + /** + * Interrupt the payment process and throw an error if needed. + * Log the message, add it to the order note and send an alert to the user + * + * @param string $message The error message + * @param bool $throw_exception When true an exception is thrown + * + * @return bool Always returns false + * + * @throws Exception + */ + public function abort($message, $throw_exception = false) + { + $this->logger->log($message); + $this->order->add_order_note($message); + wc_add_notice($message, 'error'); + if ($throw_exception) { + throw new Exception($message); + } + + return false; } - // TODO Buscar separadamente o valor de entrega - $order_items[] = $this->build_shipping_item($order_items); - $order_items[] = $this->build_tax_item($order_items); - if ('bill' === $order_type) { - $order_items[] = $this->build_discount_item_for_bill($order_items); - $order_items[] = $this->build_interest_rate_item($order_items); + /** + * Check if the order type is valid and process it. + * + * @return array|void + * + * @throws Exception + */ + public function process() + { + switch ($orderType = $this->get_order_type()) { + case static::ORDER_TYPE_SINGLE: + case static::ORDER_TYPE_SUBSCRIPTION: + return $this->process_order(); + case static::ORDER_TYPE_INVALID: + default: + return $this->abort(__('Falha ao processar carrinho de compras. Verifique os itens escolhidos e tente novamente.', VINDI), true); + } } - foreach ($order_items as $order_item) { - if (empty($order_item)) { - continue; - } - $product_items[] = $this->$call_build_items($order_item); + /** + * Process current order. + * + * @return array + * + * @throws Exception + */ + public function process_order() + { + if ($this->order_has_trial_and_simple_product()) { + $message = __('Não é possível comprar produtos simples e assinaturas com trial no mesmo pedido!', VINDI); + $this->order->update_status('failed', $message); + wc_add_notice($message, 'error'); + + throw new Exception($message); + return false; + } + + $customer = $this->get_customer(); + $order_items = $this->order->get_items(); + + $bills = []; + $order_post_meta = []; + $bill_products = []; + $subscriptions_ids = []; + foreach ($order_items as $order_item) { + $product = $order_item->get_product(); + + if ($this->is_subscription_type($product)) { + $subscription = $this->create_subscription($customer['id'], $order_item); + $subscription_order_post_meta = []; + $subscription_id = $subscription['id']; + array_push($subscriptions_ids, $subscription_id); + $wc_subscription_id = $subscription['wc_id']; + $subscription_bill = $subscription['bill']; + $order_post_meta[$subscription_id]['cycle'] = $subscription['current_period']['cycle']; + $order_post_meta[$subscription_id]['product'] = $product->name; + $order_post_meta[$subscription_id]['bill'] = $this->create_bill_meta_for_order($subscription_bill); + + $subscription_order_post_meta[$subscription_id]['cycle'] = $subscription['current_period']['cycle']; + $subscription_order_post_meta[$subscription_id]['product'] = $product->name; + $subscription_order_post_meta[$subscription_id]['bill'] = $this->create_bill_meta_for_order($subscription_bill); + $bills[] = $subscription['bill']; + if ($message = $this->cancel_if_denied_bill_status($subscription['bill'])) { + $wc_subscription = wcs_get_subscription($wc_subscription_id); + $wc_subscription->update_status('cancelled', __($message, VINDI)); + $this->order->update_status('cancelled', __($message, VINDI)); + $this->suspend_subscriptions($subscriptions_ids); + $this->cancel_bills($bills, __('Algum pagamento do pedido não pode ser processado', VINDI)); + $this->abort(__($message, VINDI), true); + } + + update_post_meta($wc_subscription_id, 'vindi_subscription_id', $subscription_id); + update_post_meta($wc_subscription_id, 'vindi_order', $subscription_order_post_meta); + continue; + } + + array_push($order_item, $bill_products); + } + + if (!empty($bill_products)) { + $single_payment_bill = $this->create_bill($customer['id'], $bill_products); + $order_post_meta['single_payment']['product'] = 'Produtos Avulsos'; + $order_post_meta['single_payment']['bill'] = $this->create_bill_meta_for_order($single_payment_bill); + + $bills[] = $single_payment_bill; + + if ($message = $this->cancel_if_denied_bill_status($single_payment_bill)) { + $this->order->update_status('cancelled', __($message, VINDI)); + $this->suspend_subscriptions($subscriptions_ids); + $this->cancel_bills($bills, __('Algum pagamento do pedido não pode ser processado', VINDI)); + $this->abort(__($message, VINDI), true); + } + } + + update_post_meta($this->order->id, 'vindi_order', $order_post_meta); + + WC()->session->__unset('current_payment_profile'); + WC()->session->__unset('current_customer'); + + remove_action('woocommerce_scheduled_subscription_payment', 'WC_Subscriptions_Manager::prepare_renewal'); + + return $this->finish_payment($bills); } - 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); + /** + * Create a payment profile for the customer + * + * @param int $customer_id Vindi customer id + * + * @throws Exception + */ + protected function create_payment_profile($customer_id) + { + + $cc_info = $this->get_cc_payment_type($customer_id); + + if (false === $cc_info) { + return; + } + + $payment_profile = $this->routes->createCustomerPaymentProfile($cc_info); + + if (!$payment_profile) { + $this->abort(__('Falha ao registrar o método de pagamento. Verifique os dados e tente novamente.', VINDI), true); + } + + if ($this->gateway->verify_method()) { + $this->verify_payment_profile($payment_profile['id']); + } + } - return $product_items; - } - - /** - * Retrives the product(s) information. Adds the vindi_id, the type and the price to it. - * - * @param string $order_type ('subscription' or 'bill') - * @param WC_Order_Item_Product|WC_Order_Item_Product[] $order_items. Subscriptions will pass only one order_item and - * Bills will pass an array with all the products to be processed - * - * @return array - */ - protected function build_product_from_order_item($order_type, $order_items) - { - if('bill' === $order_type) { - foreach ($order_items as $key => $order_item) { - $product = $this->get_product($order_item); - $order_items[$key]['type'] = 'product'; - $order_items[$key]['vindi_id'] = $product->vindi_id; - $order_items[$key]['price'] = (float)$order_items[$key]['subtotal'] / $order_items[$key]['qty']; - } - return $order_items; + /** + * Check if the payment profile is valid + * + * @param int $payment_profile_id The customer's payment profile id + * + * @throws Exception + */ + 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); + } + } - $product = $this->get_product($order_items); - $order_items['type'] = 'product'; - $order_items['vindi_id'] = $product->vindi_id; - if ($this->subscription_has_trial($product)) { - $matching_item = $this->get_trial_matching_subscription_item($order_items); - $order_items['price'] = (float)$matching_item['subtotal'] / $matching_item['qty']; - } else { - $order_items['price'] = (float)$order_items['subtotal'] / $order_items['qty']; + + /** + * Get the subscription/product expiration time in months + * + * @param WC_Order_Item_Product $item + * + * @return int + */ + private function get_cycle_from_product_type($item) + { + $product = method_exists($item, 'get_product') ? $item->get_product() : false; + if ($item['type'] == 'shipping' || $item['type'] == 'tax') { + if ($this->vindi_settings->get_shipping_and_tax_config()) { + return 1; + } + + } elseif (!$this->is_subscription_type($product) || $this->is_one_time_shipping($product)) { + return 1; + } + $cycles = get_post_meta($product->get_id(), '_subscription_length', true); + return $cycles > 0 ? $cycles : 0; } - return $order_items; - } - - /** - * Create the shipping item to be added to the bill. - * - * @param WC_Order_Item_Product[] $order_items. Array with all items to add - * the respective delivered value // TODO. - * - * @return array - */ - protected function build_interest_rate_item($order_items) - { - $interest_rate_item = []; - - if (!($this->is_cc() && $this->installments() > 1 && $this->gateway->is_interest_rate_enabled())) { - return $interest_rate_item; + /** + * Check if the product needs to be shipped only once + * + * @param WC_Product $product Woocommerce product + * + * @return bool + */ + private function is_one_time_shipping($product) + { + return get_post_meta($product->get_id(), '_subscription_one_time_shipping', true) == 'yes'; } - $interest_rate = $this->gateway->get_interest_rate(); - $installments = $this->installments(); - $cart = WC()->cart; - $cart_total = $cart->total; - foreach ($cart->get_fees() as $index => $fee) { - if($fee->name == __('Juros', VINDI)) { - $cart_total -= $fee->amount; - } + /** + * Build the array of product(s), shipping, tax and discounts to send to Vindi + * + * @param string $order_type Order type. Possible values 'bill' and 'subscription', defaults to 'bill' + * @param WC_Order_Item_Product|WC_Order_Item_Product[] $product The product to be built. If the order type is 'bill' this will be an array of WC_Order_Item_Product, + * if it's 'subscription' it will be a single WC_Order_Item_Product. + * + * @return array + * + * @throws Exception + */ + public function build_product_items($order_type = 'bill', $product) + { + $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 = []; + $order_items = []; + + if ('bill' === $order_type) { + $order_items = $this->build_product_from_order_item($order_type, $product); + } else { + $order_items[] = $this->build_product_from_order_item($order_type, $product); + } + // TODO Buscar separadamente o valor de entrega + $order_items[] = $this->build_shipping_item($order_items); + $order_items[] = $this->build_tax_item($order_items); + + if ('bill' === $order_type) { + $order_items[] = $this->build_discount_item_for_bill($order_items); + $order_items[] = $this->build_interest_rate_item($order_items); + } + + foreach ($order_items as $order_item) { + if (empty($order_item)) { + continue; + } + + $product_items[] = $this->$call_build_items($order_item); + } + + 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; + } + + /** + * Retrives the product(s) information. Adds the vindi_id, the type and the price to it. + * + * @param string $order_type ('subscription' or 'bill') + * @param WC_Order_Item_Product|WC_Order_Item_Product[] $order_items. Subscriptions will pass only one order_item and + * Bills will pass an array with all the products to be processed + * + * @return array + */ + protected function build_product_from_order_item($order_type, $order_items) + { + if ('bill' === $order_type) { + foreach ($order_items as $key => $order_item) { + $product = $this->get_product($order_item); + $order_items[$key]['type'] = 'product'; + $order_items[$key]['vindi_id'] = $product->vindi_id; + $order_items[$key]['price'] = (float) $order_items[$key]['subtotal'] / $order_items[$key]['qty']; + } + return $order_items; + } + $product = $this->get_product($order_items); + $order_items['type'] = 'product'; + $order_items['vindi_id'] = $product->vindi_id; + if ($this->subscription_has_trial($product)) { + $matching_item = $this->get_trial_matching_subscription_item($order_items); + $order_items['price'] = (float) $matching_item['subtotal'] / $matching_item['qty']; + } else { + $order_items['price'] = (float) $order_items['subtotal'] / $order_items['qty']; + } + + return $order_items; } - $total_price = $cart_total * (1 + (($interest_rate / 100) * ($installments - 1))); - $interest_price = (float) $total_price - $cart_total; - - $item = $this->routes->findOrCreateProduct("Juros", 'wc-interest-rate'); - $interest_rate_item = array( - 'type' => 'interest_rate', - 'vindi_id' => $item['id'], - 'price' => $interest_price, - 'qty' => 1, - ); - return $interest_rate_item; - } - - /** - * Create the shipping item to be added to the bill. - * - * @param WC_Order_Item_Product[] $order_items. Array with all items to add - * the respective delivered value // TODO. - * - * @return array - */ - protected function build_shipping_item($order_items) - { - $shipping_item = []; - $shipping_method = $this->order->get_shipping_method(); - - if (empty($shipping_method)) return $shipping_item; - - foreach ($order_items as $order_item) { - $product = $order_item->get_product(); - - if($product->needs_shipping() && !$this->shipping_added) { - $item = $this->routes->findOrCreateProduct("Frete ($shipping_method)", sanitize_title($shipping_method)); - $shipping_item = array( - 'type' => 'shipping', - 'vindi_id' => $item['id'], - 'price' => (float)$this->order->get_total_shipping(), - 'qty' => 1, + + /** + * Create the shipping item to be added to the bill. + * + * @param WC_Order_Item_Product[] $order_items. Array with all items to add + * the respective delivered value // TODO. + * + * @return array + */ + protected function build_interest_rate_item($order_items) + { + $interest_rate_item = []; + + if (!($this->is_cc() && $this->installments() > 1 && $this->gateway->is_interest_rate_enabled())) { + return $interest_rate_item; + } + + $interest_rate = $this->gateway->get_interest_rate(); + $installments = $this->installments(); + $cart = WC()->cart; + $cart_total = $cart->total; + foreach ($cart->get_fees() as $index => $fee) { + if ($fee->name == __('Juros', VINDI)) { + $cart_total -= $fee->amount; + } + } + $total_price = $cart_total * (1 + (($interest_rate / 100) * ($installments - 1))); + $interest_price = (float) $total_price - $cart_total; + + $item = $this->routes->findOrCreateProduct("Juros", 'wc-interest-rate'); + $interest_rate_item = array( + 'type' => 'interest_rate', + 'vindi_id' => $item['id'], + 'price' => $interest_price, + 'qty' => 1, ); - $this->shipping_added = true; + return $interest_rate_item; + } + + /** + * Create the shipping item to be added to the bill. + * + * @param WC_Order_Item_Product[] $order_items. Array with all items to add + * the respective delivered value // TODO. + * + * @return array + */ + protected function build_shipping_item($order_items) + { + $shipping_item = []; + $shipping_method = $this->order->get_shipping_method(); + + if (empty($shipping_method)) { + return $shipping_item; + } - return $shipping_item; - } + foreach ($order_items as $order_item) { + $product = $order_item->get_product(); + + if ($product->needs_shipping() && !$this->shipping_added) { + $item = $this->routes->findOrCreateProduct("Frete ($shipping_method)", sanitize_title($shipping_method)); + $shipping_item = array( + 'type' => 'shipping', + 'vindi_id' => $item['id'], + 'price' => (float) $this->order->get_total_shipping(), + 'qty' => 1, + ); + $this->shipping_added = true; + + return $shipping_item; + } + } } - } - - /** - * Create the tax item. - * - * @param WC_Order_Item_Product[] $order_items. Products to calculate the tax amount - * - * @return array - */ - protected function build_tax_item($order_items) - { - $taxItem = []; - $total_order_tax = $this->vindi_settings->woocommerce->cart->get_total_tax(); - $total_tax = 0; - if (empty($total_order_tax)) { - return $taxItem; + + /** + * Create the tax item. + * + * @param WC_Order_Item_Product[] $order_items. Products to calculate the tax amount + * + * @return array + */ + protected function build_tax_item($order_items) + { + $taxItem = []; + $total_order_tax = $this->vindi_settings->woocommerce->cart->get_total_tax(); + $total_tax = 0; + if (empty($total_order_tax)) { + return $taxItem; + } + + foreach ($order_items as $order_item) { + if (!empty($order_item['type'])) { + if ($order_item['type'] === 'shipping') { + $total_tax += (float) ($this->order->get_shipping_tax()); + } else { + $total_tax += (float) ($order_item->get_total_tax()); + } + } + } + + $item = $this->routes->findOrCreateProduct("Taxa", 'wc-tax'); + $taxItem = array( + 'type' => 'tax', + 'vindi_id' => $item['id'], + 'price' => (float) $total_tax, + 'qty' => 1, + ); + + return $taxItem; } - foreach ($order_items as $order_item) { - if(!empty($order_item['type'])) { - if ($order_item['type'] === 'shipping') { - $total_tax += (float)($this->order->get_shipping_tax()); - } else { - $total_tax += (float)($order_item->get_total_tax()); + /** + * Create discount item for a bill. + * + * @param WC_Order_Item_Product[] $order_items. All the products to calculate + * the discount amount + * + * @return array + */ + 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'])) { + $bill_total_discount += (float) ($order_item['subtotal'] - $order_item['total']); + } } - } + + if (empty($bill_total_discount)) { + return $discount_item; + } + + $item = $this->routes->findOrCreateProduct("Cupom de desconto", 'wc-discount'); + $discount_item = array( + 'type' => 'discount', + 'vindi_id' => $item['id'], + 'price' => (float) $bill_total_discount * -1, + 'qty' => 1, + ); + + return $discount_item; } - $item = $this->routes->findOrCreateProduct("Taxa", 'wc-tax'); - $taxItem = array( - 'type' => 'tax', - 'vindi_id' => $item['id'], - 'price' => (float)$total_tax, - 'qty' => 1 - ); - - return $taxItem; - } - - /** - * Create discount item for a bill. - * - * @param WC_Order_Item_Product[] $order_items. All the products to calculate - * the discount amount - * - * @return array - */ - 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'])) { - $bill_total_discount += (float) ($order_item['subtotal'] - $order_item['total']); - } + /** + * Create bill product item to send to the Vindi API. + * + * @param WC_Order_Item_Product $order_item. The product to be converted to + * the correct product format. + * + * @return array + */ + protected function build_product_items_for_bill($order_item) + { + + $item = array( + 'product_id' => $order_item['vindi_id'], + 'quantity' => $order_item['qty'], + 'pricing_schema' => array( + 'price' => $order_item['price'], + 'schema_type' => 'per_unit', + ), + ); + + if ( + 'discount' == $order_item['type'] || 'shipping' == $order_item['type'] || + 'tax' == $order_item['type'] || 'interest_rate' == $order_item['type'] + ) { + $item = array( + 'product_id' => $order_item['vindi_id'], + 'amount' => $order_item['price'], + ); + } + + return $item; } - if (empty($bill_total_discount)) { - return $discount_item; + /** + * Create subscription product item to send to the Vindi API. + * + * @param WC_Order_Item_Product $order_item. The product to be converted to + * the correct product format. + * + * @return array + */ + protected function build_product_items_for_subscription($order_item) + { + + $plan_cycles = $this->get_cycle_from_product_type($order_item); + + $product_item = array( + 'product_id' => $order_item['vindi_id'], + 'quantity' => $order_item['qty'], + 'cycles' => $plan_cycles, + 'pricing_schema' => array( + 'price' => $order_item['price'], + 'schema_type' => 'per_unit', + ), + ); + + if (!empty($this->order->get_total_discount()) && $order_item['type'] == 'line_item') { + $product_item['discounts'] = []; + + $coupons = array_values($this->vindi_settings->woocommerce->cart->get_coupons()); + foreach ($coupons as $coupon) { + if ($this->coupon_supports_product($order_item, $coupon)) { + $product_item['discounts'][] = $this->build_discount_item_for_subscription($coupon, $plan_cycles); + } + } + } + return $product_item; } - $item = $this->routes->findOrCreateProduct("Cupom de desconto", 'wc-discount'); - $discount_item = array( - 'type' => 'discount', - 'vindi_id' => $item['id'], - 'price' => (float)$bill_total_discount * -1, - 'qty' => 1 - ); - - return $discount_item; - } - - /** - * Create bill product item to send to the Vindi API. - * - * @param WC_Order_Item_Product $order_item. The product to be converted to - * the correct product format. - * - * @return array - */ - protected function build_product_items_for_bill($order_item) - { - $item = array( - 'product_id' => $order_item['vindi_id'], - 'quantity' => $order_item['qty'], - 'pricing_schema' => array( - 'price' => $order_item['price'], - 'schema_type' => 'per_unit' - ) - ); - - if ( - 'discount' == $order_item['type'] || 'shipping' == $order_item['type'] || - 'tax' == $order_item['type'] || 'interest_rate' == $order_item['type'] - ) { - $item = array( - 'product_id' => $order_item['vindi_id'], - 'amount' => $order_item['price'] - ); + /** + * Verify that the coupon can be applied to the current product. + * + * @param WC_Order_Item_Product $order_item. The product. + * @param WC_Coupon $coupon. The coupon. + * + * @return bool + */ + protected function coupon_supports_product($order_item, $coupon) + { + $product_id = $order_item->get_product()->id; + $included_products = $coupon->get_product_ids(); + $excluded_products = $coupon->get_excluded_product_ids(); + + if (!empty($excluded_products)) { + if (in_array($product_id, $excluded_products)) { + // The coupon doesn't support the current product + return false; + } + } + if (!empty($included_products)) { + if (!in_array($product_id, $included_products)) { + // The coupon doesn't support the current product + return false; + } + } + + return true; } - return $item; - } - - /** - * Create subscription product item to send to the Vindi API. - * - * @param WC_Order_Item_Product $order_item. The product to be converted to - * the correct product format. - * - * @return array - */ - protected function build_product_items_for_subscription($order_item) - { - $plan_cycles = $this->get_cycle_from_product_type($order_item); - $product_item = array( - 'product_id' => $order_item['vindi_id'], - 'quantity' => $order_item['qty'], - 'cycles' => $plan_cycles, - 'pricing_schema' => array( - 'price' => $order_item['price'], - 'schema_type' => 'per_unit' - ) - ); - - if (!empty($this->order->get_total_discount()) && $order_item['type'] == 'line_item') { - $product_item['discounts'] = []; - - $coupons = array_values($this->vindi_settings->woocommerce->cart->get_coupons()); - foreach ($coupons as $coupon) { - if($this->coupon_supports_product($order_item, $coupon)) { - $product_item['discounts'][] = $this->build_discount_item_for_subscription($coupon, $plan_cycles); + /** + * Create a discount item for a subscription. + * + * @param WC_Coupon $coupon. The coupon to be added. + * @param int $plan_cycles. The amount of cycles that the subscription has. + * + * @return array + */ + protected function build_discount_item_for_subscription($coupon, $plan_cycles = 0) + { + $discount_item = []; + + $amount = $coupon->get_amount(); + $discount_type = $coupon->get_discount_type(); + if ($discount_type == 'fixed_cart') { + $discount_item['discount_type'] = 'amount'; + $discount_item['amount'] = $amount / $this->order->get_item_count(); + $discount_item['cycles'] = 1; + return $discount_item; + } elseif (strpos($discount_type, 'fixed') !== false) { + $discount_item['discount_type'] = 'amount'; + $discount_item['amount'] = $amount; + } elseif (strpos($discount_type, 'percent') !== false) { + $discount_item['discount_type'] = 'percentage'; + $discount_item['percentage'] = $amount; } - } + $discount_item['cycles'] = $this->config_discount_cycles($coupon, $plan_cycles); + + return $discount_item; } - return $product_item; - } - - /** - * Verify that the coupon can be applied to the current product. - * - * @param WC_Order_Item_Product $order_item. The product. - * @param WC_Coupon $coupon. The coupon. - * - * @return bool - */ - protected function coupon_supports_product($order_item, $coupon) - { - $product_id = $order_item->get_product()->id; - $included_products = $coupon->get_product_ids(); - $excluded_products = $coupon->get_excluded_product_ids(); - - if(!empty($excluded_products)) { - if(in_array($product_id, $excluded_products)) { - // The coupon doesn't support the current product - return false; - } + + /** + * Configure the discount cycles that the coupon will be used. + * + * @param WC_Coupon $coupon. The coupon to be added. + * @param int $plan_cycles. The amount of cycles that the subscription has. + * + * @return int + */ + protected function config_discount_cycles($coupon, $plan_cycles = 0) + { + $get_plan_length = + function ($cycle_count, $plan_cycles) { + if (!$cycle_count) { + return null; + } + + if ($plan_cycles) { + return min($plan_cycles, $cycle_count); + } + return $cycle_count; + }; + $cycle_count = get_post_meta($coupon->id, 'cycle_count', true); + + switch ($cycle_count) { + case '0': + return null; + default: + return $get_plan_length($cycle_count, $plan_cycles); + } } - if(!empty($included_products)) { - if(!in_array($product_id, $included_products)) { - // The coupon doesn't support the current product - return false; - } + + /** + * Retrieve number of installments from order. + * If the order contains subscriptions the return will be 1, + * else it will be the amount selected by the user during checkout. + * + * @return int + */ + protected function installments() + { + if ( + 'credit_card' == $this->payment_method_code() && + isset($_POST['vindi_cc_installments']) + ) { + return filter_var($_POST['vindi_cc_installments'], FILTER_SANITIZE_NUMBER_INT); + } + + return 1; } - return true; - } - - /** - * Create a discount item for a subscription. - * - * @param WC_Coupon $coupon. The coupon to be added. - * @param int $plan_cycles. The amount of cycles that the subscription has. - * - * @return array - */ - protected function build_discount_item_for_subscription($coupon, $plan_cycles = 0) { - $discount_item = []; - - $amount = $coupon->get_amount(); - $discount_type = $coupon->get_discount_type(); - if($discount_type == 'fixed_cart') { - $discount_item['discount_type'] = 'amount'; - $discount_item['amount'] = $amount / $this->order->get_item_count(); - $discount_item['cycles'] = 1; - return $discount_item; - } elseif(strpos($discount_type, 'fixed') !== false) { - $discount_item['discount_type'] = 'amount'; - $discount_item['amount'] = $amount; - } elseif(strpos($discount_type, 'percent') !== false) { - $discount_item['discount_type'] = 'percentage'; - $discount_item['percentage'] = $amount; + /** + * Retrieve Plan for Vindi Subscription. + * + * @param WC_Order_Item_Product $order_item + * + * @return int|bool + */ + public function get_plan_from_order_item($order_item) + { + $product = $order_item->get_product(); + + if (isset($order_item['variation_id']) && $order_item['variation_id'] != 0) { + $vindi_plan = get_post_meta($order_item['variation_id'], 'vindi_plan_id', true); + if (empty($vindi_plan) || !is_numeric($vindi_plan) || is_null($vindi_plan) || $vindi_plan == 0) { + $vindi_plan = get_post_meta($product->get_id(), 'vindi_plan_id', true); + } + } else { + $vindi_plan = get_post_meta($product->get_id(), 'vindi_plan_id', true); + } + + if ($this->is_subscription_type($product) and !empty($vindi_plan)) { + return $vindi_plan; + } + + $this->abort(__('O produto selecionado não é uma assinatura.', VINDI), true); } - $discount_item['cycles'] = $this->config_discount_cycles($coupon, $plan_cycles); - - return $discount_item; - } - - /** - * Configure the discount cycles that the coupon will be used. - * - * @param WC_Coupon $coupon. The coupon to be added. - * @param int $plan_cycles. The amount of cycles that the subscription has. - * - * @return int - */ - protected function config_discount_cycles($coupon, $plan_cycles = 0) - { - $get_plan_length = - function ($cycle_count, $plan_cycles) + + /** + * Create a subscription within Vindi + * + * @param int $customer_id ID of the customer that placed the order + * @param WC_Order_item_product $order_item Item to add to the subscription. + * + * @return array + * @throws Exception + */ + protected function create_subscription($customer_id, $order_item) { - if (!$cycle_count) { - return null; - } - - if ($plan_cycles) { - return min($plan_cycles, $cycle_count); - } - return $cycle_count; - }; - $cycle_count = get_post_meta($coupon->id, 'cycle_count', true); - - switch ($cycle_count) { - case '0': - return null; - default: - return $get_plan_length($cycle_count, $plan_cycles); + + $vindi_plan = $this->get_plan_from_order_item($order_item); + + $wc_subscription_id = VindiHelpers::get_matching_subscription($this->order, $order_item)->id; + + $data = array( + 'customer_id' => $customer_id, + 'payment_method_code' => $this->payment_method_code(), + 'plan_id' => $vindi_plan, + 'product_items' => $this->build_product_items('subscription', $order_item)[0], + 'code' => $wc_subscription_id, + 'installments' => $this->installments(), + ); + + $subscription = $this->routes->createSubscription($data); + + // TODO caso ocorra o erro no pagamento de uma assinatura cancelar as outras + if (!isset($subscription['id']) || empty($subscription['id'])) { + + $message = sprintf(__('Pagamento Falhou. (%s)', VINDI), $this->vindi_settings->api->last_error); + $this->order->update_status('failed', $message); + + throw new Exception($message); + } + + $subscription['wc_id'] = $wc_subscription_id; + + return $subscription; } - } - - /** - * Retrieve number of installments from order. - * If the order contains subscriptions the return will be 1, - * else it will be the amount selected by the user during checkout. - * - * @return int - */ - protected function installments() - { - if ('credit_card' == $this->payment_method_code() && isset($_POST['vindi_cc_installments'])) return $_POST['vindi_cc_installments']; - - return 1; - } - - /** - * Retrieve Plan for Vindi Subscription. - * - * @param WC_Order_Item_Product $order_item - * - * @return int|bool - */ - public function get_plan_from_order_item($order_item) - { - $product = $order_item->get_product(); - - if (isset($order_item['variation_id']) && $order_item['variation_id'] != 0) { - $vindi_plan = get_post_meta($order_item['variation_id'], 'vindi_plan_id', true); - if (empty($vindi_plan) || !is_numeric($vindi_plan) || is_null($vindi_plan) || $vindi_plan == 0) { - $vindi_plan = get_post_meta($product->get_id(), 'vindi_plan_id', true); - } + + /** + * Create a bill within Vindi + * + * @param int $customer_id ID of the customer that placed the order + * @param WC_Order_item_product[] $order_items Array with items to add to the bill. + * + * @return int + * @throws Exception + */ + 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('bill', $order_items), + 'bill_items' => $order_items, + 'code' => $this->order->id, + 'installments' => $this->installments(), + ); + + $bill = $this->routes->createBill($data); + + if (!$bill) { + $this->logger->log(sprintf('Erro no pagamento do pedido %s.', $this->order->id)); + $message = sprintf(__('Pagamento Falhou. (%s)', VINDI), $this->vindi_settings->api->last_error); + $this->order->update_status('failed', $message); + + throw new Exception($message); + } + return $bill; } - else $vindi_plan = get_post_meta($product->get_id(), 'vindi_plan_id', true); - - if ($this->is_subscription_type($product) and !empty($vindi_plan)) return $vindi_plan; - - $this->abort(__('O produto selecionado não é uma assinatura.', VINDI) , true); - } - - - /** - * Create a subscription within Vindi - * - * @param int $customer_id ID of the customer that placed the order - * @param WC_Order_item_product $order_item Item to add to the subscription. - * - * @return array - * @throws Exception - */ - protected function create_subscription($customer_id, $order_item) - { - $vindi_plan = $this->get_plan_from_order_item($order_item); - $wc_subscription_id = VindiHelpers::get_matching_subscription($this->order, $order_item)->id; - $data = array( - 'customer_id' => $customer_id, - 'payment_method_code' => $this->payment_method_code(), - 'plan_id' => $vindi_plan, - 'product_items' => $this->build_product_items('subscription', $order_item), - 'code' => $wc_subscription_id, - 'installments' => $this->installments() - ); - - $subscription = $this->routes->createSubscription($data); - - // TODO caso ocorra o erro no pagamento de uma assinatura cancelar as outras - if (!isset($subscription['id']) || empty($subscription['id'])) { - $this->logger->log(sprintf('Erro no pagamento item %s do pedido %s.', $order_item->name, $this->order->id)); - - $message = sprintf(__('Pagamento Falhou. (%s)', VINDI) , $this->vindi_settings->api->last_error); - $this->order->update_status('failed', $message); - - throw new Exception($message); + + /** + * Create bill meta array to add to the order + * + * @param array $bill The bill returned from Vindi API + * + * @return array + */ + protected function create_bill_meta_for_order($bill) + { + $bill_meta['id'] = $bill['id']; + $bill_meta['status'] = $bill['status']; + if (isset($bill['charges']) && count($bill['charges'])) { + $bill_meta['bank_slip_url'] = $bill['charges'][0]['print_url']; + } + return $bill_meta; } - $subscription['wc_id'] = $wc_subscription_id; - - return $subscription; - } - - /** - * Create a bill within Vindi - * - * @param int $customer_id ID of the customer that placed the order - * @param WC_Order_item_product[] $order_items Array with items to add to the bill. - * - * @return int - * @throws Exception - */ - 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('bill', $order_items), - 'code' => $this->order->id, - 'installments' => $this->installments() - ); - - $bill = $this->routes->createBill($data); - - if (!$bill) { - $this->logger->log(sprintf('Erro no pagamento do pedido %s.', $this->order->id)); - $message = sprintf(__('Pagamento Falhou. (%s)', VINDI) , $this->vindi_settings->api->last_error); - $this->order->update_status('failed', $message); - - throw new Exception($message); + /** + * Check if bill was rejected by Vindi + * + * @param array $bill The bill returned from Vindi API + * + * @return bool|string + */ + protected function cancel_if_denied_bill_status($bill) + { + if (empty($bill['charges'])) { + return false; + } + + $last_charge = end($bill['charges']); + $transaction_status = $last_charge['last_transaction']['status']; + $denied_status = ['rejected' => 'Infelizmente não foi possível autorizar seu pagamento.', 'failure' => 'Ocorreu um erro ao aprovar a transação, tente novamente.']; + + if (array_key_exists($transaction_status, $denied_status)) { + return $denied_status[$transaction_status]; + } + + return false; } - return $bill; - } - - /** - * Create bill meta array to add to the order - * - * @param array $bill The bill returned from Vindi API - * - * @return array - */ - protected function create_bill_meta_for_order($bill) - { - $bill_meta['id'] = $bill['id']; - $bill_meta['status'] = $bill['status']; - if (isset($bill['charges']) && count($bill['charges'])) { - $bill_meta['bank_slip_url'] = $bill['charges'][0]['print_url']; + + /** + * Suspend subscription within Vindi + * + * @param array $subscriptions_ids Array with the IDs of subscriptions that were processed + */ + protected function suspend_subscriptions($subscriptions_ids) + { + foreach ($subscriptions_ids as $subscription_id) { + $this->routes->suspendSubscription($subscription_id, true); + } } - return $bill_meta; - } - - /** - * Check if bill was rejected by Vindi - * - * @param array $bill The bill returned from Vindi API - * - * @return bool|string - */ - protected function cancel_if_denied_bill_status($bill) - { - if (empty($bill['charges'])) { - return false; + + /** + * Suspend bills within Vindi + * + * @param array $bills Array with the bills that were processed + */ + protected function cancel_bills($bills, $comments = '') + { + foreach ($bills as $bill) { + $this->routes->deleteBill($bill['id'], $comments); + } } - $last_charge = end($bill['charges']); - $transaction_status = $last_charge['last_transaction']['status']; - $denied_status = ['rejected' => 'Infelizmente não foi possível autorizar seu pagamento.', 'failure' => 'Ocorreu um erro ao aprovar a transação, tente novamente.']; + /** + * Finish the payment + * + * @param array $bills Order bills returned from Vindi API + * + * @return array + */ + protected function finish_payment($bills) + { + $this->vindi_settings->woocommerce->cart->empty_cart(); + + $bills_status = []; + foreach ($bills as $bill) { + if ($bill['status'] == 'paid') { + $data_to_log = sprintf('O Pagamento da fatura %s do pedido %s foi realizado com sucesso pela Vindi.', $bill['id'], $this->order->id); + $status_message = __('O Pagamento foi realizado com sucesso pela Vindi.', VINDI); + } else { + $data_to_log = sprintf('Aguardando pagamento da fatura %s do pedido %s pela Vindi.', $bill['id'], $this->order->id); + $status_message = __('Aguardando pagamento do pedido.', VINDI); + } + array_push($bills_status, $bill['status']); + $this->logger->log($data_to_log); + } + if (sizeof($bills_status) == sizeof(array_keys($bills_status, 'paid'))) { + $status = $this->vindi_settings->get_return_status(); + } else { + $status = 'pending'; + } + $this->order->update_status($status, $status_message); - if (array_key_exists($transaction_status, $denied_status)) { - return $denied_status[$transaction_status]; + return array( + 'result' => 'success', + 'redirect' => $this->order->get_checkout_order_received_url(), + ); } - return false; - } - - /** - * Suspend subscription within Vindi - * - * @param array $subscriptions_ids Array with the IDs of subscriptions that were processed - */ - protected function suspend_subscriptions($subscriptions_ids) - { - foreach ($subscriptions_ids as $subscription_id) { - $this->routes->suspendSubscription($subscription_id, true); + /** + * Find or create the product within Vindi + * and add the vindi id to the product array + * + * @param WC_Order_Item_Product $order_item + * + * @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(); + + $vindi_product_id = get_post_meta($product, 'vindi_product_id', true); + + if (!$vindi_product_id) { + $vindi_product = null; + if (!$this->is_subscription_type($product)) { + $vindi_product = $this->controllers->products->create($product_id, '', '', true); + } else { + $vindi_product = $this->controllers->plans->create($product_id, '', '', true); + } + + $vindi_product_id = $vindi_product['id']; + } + if (empty($vindi_product_id) || !$vindi_product_id) { + + $vindi_product_id = 63; + } + + $product->vindi_id = (int) $vindi_product_id; + return $product; } - } - - /** - * Suspend bills within Vindi - * - * @param array $bills Array with the bills that were processed - */ - protected function cancel_bills($bills, $comments = '') - { - foreach ($bills as $bill) { - $this->routes->deleteBill($bill['id'], $comments); + + /** + * Check if the order has a subscription with trial and simple products. + * + * @since 1.0.0 + * @return bool + */ + public function order_has_trial_and_simple_product() + { + $has_trial = false; + $has_simple_product = false; + $order_items = $this->order->get_items(); + foreach ($order_items as $order_item) { + $product = $order_item->get_product(); + if ($this->subscription_has_trial($product)) { + $has_trial = true; + if ($has_simple_product) { + return true; + } + + } else { + $has_simple_product = true; + if ($has_trial) { + return true; + } + + } + } + return $has_trial && $has_simple_product; } - } - - /** - * Finish the payment - * - * @param array $bills Order bills returned from Vindi API - * - * @return array - */ - protected function finish_payment($bills) - { - $this->vindi_settings->woocommerce->cart->empty_cart(); - - $bills_status = []; - foreach ($bills as $bill) { - if ($bill['status'] == 'paid') { - $data_to_log = sprintf('O Pagamento da fatura %s do pedido %s foi realizado com sucesso pela Vindi.', $bill['id'], $this->order->id); - $status_message = __('O Pagamento foi realizado com sucesso pela Vindi.', VINDI); - } else { - $data_to_log = sprintf('Aguardando pagamento da fatura %s do pedido %s pela Vindi.', $bill['id'], $this->order->id); - $status_message = __('Aguardando pagamento do pedido.', VINDI); - } - array_push($bills_status, $bill['status']); - $this->logger->log($data_to_log); + + /** + * Check if the product is variable + * + * @param WC_Product $product + * @return bool + */ + protected function is_variable(WC_Product $product) + { + return (boolean) preg_match('/variation/', $product->get_type()); } - if(sizeof($bills_status) == sizeof(array_keys($bills_status, 'paid'))) { - $status = $this->vindi_settings->get_return_status(); - } else { - $status = 'pending'; + + /** + * Check if the product is a subscription + * + * @param WC_Product $product + * @return bool + */ + protected function is_subscription_type(WC_Product $product) + { + return (boolean) preg_match('/subscription/', $product->get_type()); } - $this->order->update_status($status, $status_message); - - return array( - 'result' => 'success', - 'redirect' => $this->order->get_checkout_order_received_url() , - ); - } - - /** - * Find or create the product within Vindi - * and add the vindi id to the product array - * - * @param WC_Order_Item_Product $order_item - * - * @return WC_Product Woocommerce product array with a vindi id - */ - protected function get_product($order_item) - { - $product = $order_item->get_product(); - $product_id = $product->get_id(); - $vindi_product_id = get_post_meta($product_id, 'vindi_product_id', true); - - if (!$vindi_product_id) { - $vindi_product = null; - if(!$this->is_subscription_type($product)) { - $vindi_product = $this->controllers->products->create($product_id, '', '', true); - } else { - $vindi_product = $this->controllers->plans->create($product_id, '', '', true); - } - - $vindi_product_id = $vindi_product['id']; + + /** + * Check if the subscription has a trial period + * + * @param WC_Product $product + * @return bool + */ + protected function subscription_has_trial(WC_Product $product) + { + return $this->is_subscription_type($product) && class_exists('WC_Subscriptions_Product') && WC_Subscriptions_Product::get_trial_length($product->get_id()) > 0; } - $product->vindi_id = (int) $vindi_product_id; - return $product; - } - - /** - * Check if the order has a subscription with trial and simple products. - * - * @since 1.0.0 - * @return bool - */ - public function order_has_trial_and_simple_product() - { - $has_trial = false; - $has_simple_product = false; - $order_items = $this->order->get_items(); - foreach ($order_items as $order_item) { - $product = $order_item->get_product(); - if ($this->subscription_has_trial($product)) { - $has_trial = true; - if ($has_simple_product) return true; - } else { - $has_simple_product = true; - if ($has_trial) return true; - } + /** + * Get trial item quantity, subtotal and total price. + * + * @param WC_Order_Item_Product $order_item + * @return WC_Order_Item_Product + */ + protected function get_trial_matching_subscription_item(WC_Order_Item_Product $order_item) + { + $subscription = VindiHelpers::get_matching_subscription($this->order, $order_item); + $matching_item = VindiHelpers::get_matching_subscription_item($subscription, $order_item); + return $matching_item; } - return $has_trial && $has_simple_product; - } - - /** - * Check if the product is variable - * - * @param WC_Product $product - * @return bool - */ - protected function is_variable(WC_Product $product) - { - return (boolean)preg_match('/variation/', $product->get_type()); - } - - /** - * Check if the product is a subscription - * - * @param WC_Product $product - * @return bool - */ - protected function is_subscription_type(WC_Product $product) - { - return (boolean)preg_match('/subscription/', $product->get_type()); - } - - /** - * Check if the subscription has a trial period - * - * @param WC_Product $product - * @return bool - */ - protected function subscription_has_trial(WC_Product $product) - { - return $this->is_subscription_type($product) && class_exists( 'WC_Subscriptions_Product' ) && WC_Subscriptions_Product::get_trial_length($product->get_id()) > 0; - } - - /** - * Get trial item quantity, subtotal and total price. - * - * @param WC_Order_Item_Product $order_item - * @return WC_Order_Item_Product - */ - protected function get_trial_matching_subscription_item(WC_Order_Item_Product $order_item) - { - $subscription = VindiHelpers::get_matching_subscription($this->order, $order_item); - $matching_item = VindiHelpers::get_matching_subscription_item($subscription, $order_item); - return $matching_item; - } }