context->cart; // cehck cart details if ($cart->id_customer == 0 || !$this->module->active ) { Tools::redirect($this->context->link->getPageLink('order-opc', true, null)); } // Check that this payment option is still available in case the customer // changed his address just before the end of the checkout process $authorized = false; foreach (Module::getPaymentModules() as $module) { if ($module['name'] == 'qlopaypalcommerce') { $authorized = true; break; } } if (!$authorized) { die($this->module->l('This payment method is not available.', 'payment')); } $customer = new Customer($cart->id_customer); if (!Validate::isLoadedObject($customer)) { Tools::redirect($this->context->link->getPageLink('order-opc', true, null)); } } public function initContent() { parent::initContent(); if ($this->module->secure_key != Tools::getValue('token')) { die($this->module->l('Invalid token.', 'payment')); } if (Tools::isSubmit('action')) { $action = (int)Tools::getValue('action'); switch ($action) { case self::WK_PAYPAL_COMMERCE_ACTION_CREATE_ORDER: $json = Tools::file_get_contents('php://input'); $orderDetails = json_decode($json, true); $cart = $this->context->cart; WkPaypalCommerceHelper::logMsg('payment', 'Payment initiated...', true); WkPaypalCommerceHelper::logMsg('payment', 'Environment: '. Configuration::get('WK_PAYPAL_COMMERCE_PAYMENT_MODE')); WkPaypalCommerceHelper::logMsg('payment', 'Cart ID: '. $cart->id); WkPaypalCommerceHelper::logMsg('payment', 'Customer ID: '. $cart->id_customer); WkPaypalCommerceHelper::logMsg('payment', 'Currency ID: '. $cart->id_currency); WkPaypalCommerceHelper::logMsg('payment', 'Cart Total: '. $cart->getOrderTotal(true, Cart::BOTH)); WkPaypalCommerceHelper::logMsg('payment', 'Payment request data: '); $ppCommerce = new PayPalCommerce(); header('Content-Type: application/json'); // create order $orderDetails['original'] = $this->getOrderDetails(); WkPaypalCommerceHelper::logMsg('payment', json_encode($orderDetails)); $ppOrderData = $ppCommerce->orders->create($orderDetails); WkPaypalCommerceHelper::logMsg('payment', 'Payment response data: ', true); WkPaypalCommerceHelper::logMsg('payment', json_encode($ppOrderData)); die(json_encode($ppOrderData)); case self::WK_PAYPAL_COMMERCE_ACTION_CAPTURE_ORDER: $json = Tools::file_get_contents('php://input'); $orderData = json_decode($json, true); if ($orderData['orderID'] && $orderData['getOrderData']) { $ppCommerce = new PayPalCommerce(); $returnData = $ppCommerce->orders->capture($orderData['orderID']); header('Content-Type: application/json'); echo json_encode($returnData['data']); die; } elseif (Tools::getIsset('order_id')) { $orderID = Tools::getValue('order_id'); $ppCommerce = new PayPalCommerce(); $returnData = $ppCommerce->orders->get($orderID); $cart = $this->context->cart; $customer = new Customer($cart->id_customer); WkPaypalCommerceHelper::logMsg('payment', 'Payment response...', true); WkPaypalCommerceHelper::logMsg('payment', 'Cart ID: '. $cart->id); WkPaypalCommerceHelper::logMsg('payment', 'Customer ID: '. $cart->id_customer); WkPaypalCommerceHelper::logMsg('payment', 'Currency ID: '. $cart->id_currency); WkPaypalCommerceHelper::logMsg('payment', 'Cart Total: '. $cart->getOrderTotal(true, Cart::BOTH)); WkPaypalCommerceHelper::logMsg('payment', json_encode($returnData)); if (isset($returnData['data']['id']) && !empty($returnData['data']['id'])) { // Payment success $paypalOrderID = $returnData['data']['id']; $paymentStatus = ''; if (isset($returnData['data']['purchase_units'][0]['payments']['captures'][0]['status'])) { $paymentStatus = $returnData['data']['purchase_units'][0]['payments']['captures'][0]['status']; } $paypalOrderCartId = isset($returnData['data']['purchase_units'][0]['custom_id']) ? $returnData['data']['purchase_units'][0]['custom_id'] : null; $belongsToCurrentCart = ((int) $paypalOrderCartId === (int) $cart->id); if ($paypalOrderID && $paymentStatus == WkPaypalCommerceWebhook::WK_PAYPAL_COMMERCE_PAYMENT_STATUS_COMPLETED && $belongsToCurrentCart) { // Save order data $this->saveOrderData($returnData); $currency = $this->context->currency; // Order status/amount must reflect what PayPal actually captured, // not the cart's is_advance_payment flag (still editable by the // customer after payment, so it cannot be trusted here). $capturedAmount = isset($returnData['data']['purchase_units'][0]['payments']['captures'][0]['amount']['value']) ? (float) $returnData['data']['purchase_units'][0]['payments']['captures'][0]['amount']['value'] : 0; $fullTotal = (float) $cart->getOrderTotal(true, Cart::BOTH); $isFullyPaid = $capturedAmount >= $fullTotal; // set order status if ($returnData['data']['status'] == WkPaypalCommerceWebhook::WK_PAYPAL_COMMERCE_PAYMENT_STATUS_COMPLETED) { $orderStatus = $isFullyPaid ? Configuration::get('PS_OS_PAYMENT_ACCEPTED') : Configuration::get('PS_OS_PARTIAL_PAYMENT_ACCEPTED'); } else { $orderStatus = Configuration::get('PS_OS_AWAITING_PAYMENT'); } $transactionId = ''; if (isset($returnData['data']['purchase_units'][0]['payments']['captures'][0]['id'])) { $transactionId = $returnData['data']['purchase_units'][0]['payments']['captures'][0]['id']; } $extraVars = array('transaction_id' => $transactionId); // create order for the payment $this->module->validateOrder( $cart->id, $orderStatus, $capturedAmount, $this->module->l('PayPal Checkout', 'payment'), null, $extraVars, (int)$currency->id, false, $customer->secure_key ); // Update order id in paypal_seller_order table $objOrder = new Order($this->module->currentOrder); WkPayPalCommerceOrder::updateOrderReference( $paypalOrderID, $objOrder->reference ); WkPaypalCommerceHelper::logMsg('payment', 'Order ID: '. $this->module->currentOrder); WkPaypalCommerceHelper::logMsg('payment', '--------------', true); $link = new Link(); $orderLink = $link->getPageLink( 'order-confirmation', null, (int)Context::getContext()->language->id, array( 'id_cart' => (int)$cart->id, 'id_module' => (int)$this->module->id, 'id_order' => (int)$this->module->currentOrder, 'key' => $customer->secure_key, ) ); Tools::redirect($orderLink); } else { if ($paypalOrderID && $paymentStatus == WkPaypalCommerceWebhook::WK_PAYPAL_COMMERCE_PAYMENT_STATUS_COMPLETED && !$belongsToCurrentCart) { WkPaypalCommerceHelper::logMsg( 'payment', 'PayPal order/cart mismatch. Expected cart: '.$cart->id.', PayPal custom_id: '.$paypalOrderCartId ); } WkPaypalCommerceHelper::logMsg('payment', 'Payment status'. $returnData['data']['status']); WkPaypalCommerceHelper::logMsg('payment', '--------------', true); Tools::redirect('index.php?controller=order&step=1'); } } else { WkPaypalCommerceHelper::logMsg('payment', 'Payment failed...', true); WkPaypalCommerceHelper::logMsg( 'payment', 'Payment failed message: '.$returnData['data']['name'] ); WkPaypalCommerceHelper::logMsg( 'payment', 'Payment failed message: '.$returnData['data']['message'] ); WkPaypalCommerceHelper::logMsg('payment', '--------------', true); $failedLink = $this->context->link->getModuleLink( 'qlopaypalcommerce', 'errorpayment', array( 'err_name' => $returnData['data']['name'], 'err_msg' => $returnData['data']['message'] ) ); Tools::redirect($failedLink); } } break; case self::WK_PAYPAL_COMMERCE_ACTION_CANCELLED: WkPaypalCommerceHelper::logMsg('payment', 'Payment cancelled by customer...', true); WkPaypalCommerceHelper::logMsg('payment', json_encode(Tools::getAllValues())); WkPaypalCommerceHelper::logMsg('payment', '--------------', true); Tools::redirect($this->context->link->getPageLink('order-opc', true, null).'?pp_cancel=1'); break; default: # code... break; } } die; } // Save order data private function saveOrderData($orderData) { if ($orderData) { $purchaseUnits = $orderData['data']['purchase_units']; foreach ($purchaseUnits as $purchase) { $transactionId = $purchase['payments']['captures'][0]['id']; $paymentStatus = $purchase['payments']['captures'][0]['status']; $paymentTotal = $purchase['payments']['captures'][0]['amount']['value']; $paymentCurrency = $purchase['payments']['captures'][0]['amount']['currency_code']; $cart = $this->context->cart; $currency = Currency::getCurrency((int) $cart->id_currency); // total order amount if ($cart->is_advance_payment) { $cartTotalAmountTI = $cart->getOrderTotal(true, Cart::ADVANCE_PAYMENT); } else { $cartTotalAmountTI = $cart->getOrderTotal(true, Cart::BOTH); } $orderObj = new WkPayPalCommerceOrder(); $orderObj->order_reference = ''; $orderObj->id_cart = (int)$cart->id; $orderObj->id_currency = (int)$cart->id_currency; $orderObj->environment = Configuration::get('WK_PAYPAL_COMMERCE_PAYMENT_MODE'); $orderObj->id_customer = (int)$cart->id_customer; $orderObj->order_total = (float)$cartTotalAmountTI; $orderObj->checkout_currency = $currency['iso_code']; // PayPal Returned Data $orderObj->pp_paid_total = (float)$paymentTotal; $orderObj->pp_paid_currency = $paymentCurrency; $orderObj->pp_reference_id = $purchase['reference_id']; $orderObj->pp_order_id = $orderData['data']['id']; $orderObj->pp_transaction_id = $transactionId; $orderObj->pp_payment_status = $paymentStatus; $orderObj->response = json_encode($orderData); $orderObj->order_date = date('Y-m-d H:i:s'); $orderObj->save(); } return true; } return false; } // Create order data to send to PayPal private function getOrderDetails() { $orderData = array(); $orderData['intent'] = 'CAPTURE'; $cart = $this->context->cart; $customer = new Customer((int)$cart->id_customer); $ppHelper = new WkPaypalCommerceHelper(); $orderData['payer'] = array( 'name' => array( 'given_name' => $customer->firstname, 'surname' => $customer->lastname ), 'email_address' => $customer->email ); // address is not required in paypal payment, if customer has added an address then we add tha address to paypal payer object if ($billingAddress = $ppHelper->getSimpleAddress( (int)$cart->id_customer, (int)$cart->id_address_invoice )) { $orderData['payer']['address'] = array( 'address_line_1' => $billingAddress['address1'], 'address_line_2' => $billingAddress['address2'], 'admin_area_2' => $billingAddress['city'], 'admin_area_1' => $billingAddress['state_iso'], 'postal_code' => $billingAddress['postcode'], 'country_code' => $billingAddress['country_iso'], ); } $timestamp = time().rand(100, 999); $currency = Currency::getCurrency((int) $cart->id_currency); $discountTI = $cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS); $discountTE = $cart->getOrderTotal(false, Cart::ONLY_DISCOUNTS); // total cart amount // items totals will be calculated after removing discount from the total cart as additional facility is also considered as an item if ($cart->is_advance_payment) { $cartTotalAmountTI = $cart->getOrderTotal(true, Cart::ADVANCE_PAYMENT); $cartTotalAmountTE = $cart->getOrderTotal(false, Cart::ADVANCE_PAYMENT); $itemTotalAmountTI = $cartTotalAmountTI + $discountTI; $itemTotalAmountTE = $cartTotalAmountTE + $discountTE; } else { $cartTotalAmountTI = $cart->getOrderTotal(true, Cart::BOTH); $cartTotalAmountTE = $cart->getOrderTotal(false, Cart::BOTH); $itemTotalAmountTI = $cartTotalAmountTI + $discountTI; $itemTotalAmountTE = $cartTotalAmountTE + $discountTE; } $orderData['purchase_units'][0] = array( 'reference_id' => $timestamp, 'description' => "Payment for " . Configuration::get('PS_SHOP_NAME'), // Maxlength 22 'soft_descriptor' => Tools::substr(Configuration::get('PS_SHOP_NAME'), 0, 21), 'payment_group_id' => 1, 'custom_id' => $cart->id, 'invoice_id' => 'INV-' . $timestamp, ); $orderData['purchase_units'][0]['amount'] = array( 'currency_code' => $currency['iso_code'], 'value' => number_format(Tools::ps_round($cartTotalAmountTI, 2), 2, '.', ''), 'breakdown' => array( 'item_total' => array( 'currency_code' => $currency['iso_code'], 'value' => number_format(Tools::ps_round($itemTotalAmountTE, 2), 2, '.', ''), ), 'shipping' => array( 'currency_code' => $currency['iso_code'], 'value' => '0.00', ), 'shipping_discount' => array( 'currency_code' => $currency['iso_code'], 'value' => '0.00', ), 'tax_total' => array( 'currency_code' => $currency['iso_code'], 'value' => number_format(Tools::ps_round(($itemTotalAmountTI - $itemTotalAmountTE), 2), 2, '.', ''), ), 'discount' => array( 'currency_code' => $currency['iso_code'], 'value' => number_format(Tools::ps_round($discountTI, 2), 2, '.', ''), ), ) ); // set item details (for rounding issue, paypal said not to send item details) // $items = $ppHelper->getPaypalOrderItemDetails($cart->id); // $orderData['purchase_units'][0]['items'] = array_values($items); $wkEnvironment = Configuration::get('WK_PAYPAL_COMMERCE_PAYMENT_MODE'); $merchantId = ($wkEnvironment == QloPaypalCommerce::WK_PAYPAL_COMMERCE_PAYMENT_MODE_PRODUCTION) ? Configuration::get('WK_PAYPAL_COMMERCE_LIVE_MERCHANT_ID') : Configuration::get('WK_PAYPAL_COMMERCE_SANDBOX_MERCHANT_ID'); $orderData['purchase_units'][0]['payee'] = array( 'merchant_id' => $merchantId ); $orderData['application_context'] = array( 'user_action' => 'PAY_NOW', 'shipping_preference' => 'NO_SHIPPING', ); return $orderData; } }