* @copyright 2007-2013 PrestaShop SA
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
abstract class PaymentModule extends PaymentModuleCore
{
/**
* Validate an order in database
* Function called from a payment module
*
* @param integer $id_cart Value
* @param integer $id_order_state Value
* @param float $amount_paid Amount really paid by customer (in the default currency)
* @param string $payment_method Payment method (eg. 'Credit card')
* @param string $message Message to attach to order
*/
public function validateOrder($id_cart, $id_order_state, $amount_paid, $payment_method = 'Unknown',
$message = null, $extra_vars = array(), $currency_special = null, $dont_touch_amount = false,
$secure_key = false, Shop $shop = null)
{
$this->context->cart = new Cart($id_cart);
$this->context->customer = new Customer($this->context->cart->id_customer);
$this->context->language = new Language($this->context->cart->id_lang);
$this->context->shop = ($shop ? $shop : new Shop($this->context->cart->id_shop));
ShopUrl::resetMainDomainCache();
$id_currency = $currency_special ? (int)$currency_special : (int)$this->context->cart->id_currency;
$this->context->currency = new Currency($id_currency, null, $this->context->shop->id);
if (Configuration::get('PS_TAX_ADDRESS_TYPE') == 'id_address_delivery')
$context_country = $this->context->country;
$order_status = new OrderState((int)$id_order_state, (int)$this->context->language->id);
if (!Validate::isLoadedObject($order_status))
throw new PrestaShopException('Can\'t load Order state status');
if (!$this->active)
die(Tools::displayError());
// Does order already exists ?
if (Validate::isLoadedObject($this->context->cart) && $this->context->cart->OrderExists() == false)
{
if ($secure_key !== false && $secure_key != $this->context->cart->secure_key)
die(Tools::displayError());
// For each package, generate an order
$delivery_option_list = $this->context->cart->getDeliveryOptionList();
$package_list = $this->context->cart->getPackageList();
$cart_delivery_option = $this->context->cart->getDeliveryOption();
// If some delivery options are not defined, or not valid, use the first valid option
foreach ($delivery_option_list as $id_address => $package)
if (!isset($cart_delivery_option[$id_address]) || !array_key_exists($cart_delivery_option[$id_address], $package))
foreach ($package as $key => $val)
{
$cart_delivery_option[$id_address] = $key;
break;
}
$order_list = array();
$order_detail_list = array();
$reference = Order::generateReference();
$this->currentOrderReference = $reference;
$order_creation_failed = false;
$cart_total_paid = (float)Tools::ps_round((float)$this->context->cart->getOrderTotal(true, Cart::BOTH), 2);
foreach ($cart_delivery_option as $id_address => $key_carriers)
foreach ($delivery_option_list[$id_address][$key_carriers]['carrier_list'] as $id_carrier => $data)
foreach ($data['package_list'] as $id_package)
{
// Rewrite the id_warehouse
$package_list[$id_address][$id_package]['id_warehouse'] = (int)$this->context->cart->getPackageIdWarehouse($package_list[$id_address][$id_package], (int)$id_carrier);
$package_list[$id_address][$id_package]['id_carrier'] = $id_carrier;
}
// Make sure CarRule caches are empty
CartRule::cleanCache();
foreach ($package_list as $id_address => $packageByAddress)
foreach ($packageByAddress as $id_package => $package)
{
$order = new Order();
$order->product_list = $package['product_list'];
if (Configuration::get('PS_TAX_ADDRESS_TYPE') == 'id_address_delivery')
{
$address = new Address($id_address);
$this->context->country = new Country($address->id_country, $this->context->cart->id_lang);
}
$carrier = null;
if (!$this->context->cart->isVirtualCart() && isset($package['id_carrier']))
{
$carrier = new Carrier($package['id_carrier'], $this->context->cart->id_lang);
$order->id_carrier = (int)$carrier->id;
$id_carrier = (int)$carrier->id;
}
else
{
$order->id_carrier = 0;
$id_carrier = 0;
}
$order->id_customer = (int)$this->context->cart->id_customer;
$order->id_address_invoice = (int)$this->context->cart->id_address_invoice;
$order->id_address_delivery = (int)$id_address;
$order->id_currency = $this->context->currency->id;
$order->id_lang = (int)$this->context->cart->id_lang;
$order->id_cart = (int)$this->context->cart->id;
$order->reference = $reference;
$order->id_shop = (int)$this->context->shop->id;
$order->id_shop_group = (int)$this->context->shop->id_shop_group;
$order->secure_key = ($secure_key ? pSQL($secure_key) : pSQL($this->context->customer->secure_key));
$order->payment = $payment_method;
if (isset($this->name))
$order->module = $this->name;
$order->recyclable = $this->context->cart->recyclable;
$order->gift = (int)$this->context->cart->gift;
$order->gift_message = $this->context->cart->gift_message;
$order->mobile_theme = $this->context->cart->mobile_theme;
$order->conversion_rate = $this->context->currency->conversion_rate;
$amount_paid = !$dont_touch_amount ? Tools::ps_round((float)$amount_paid, 2) : $amount_paid;
$order->total_paid_real = 0;
$order->total_products = (float)$this->context->cart->getOrderTotal(false, Cart::ONLY_PRODUCTS, $order->product_list, $id_carrier);
$order->total_products_wt = (float)$this->context->cart->getOrderTotal(true, Cart::ONLY_PRODUCTS, $order->product_list, $id_carrier);
$order->total_discounts_tax_excl = (float)abs($this->context->cart->getOrderTotal(false, Cart::ONLY_DISCOUNTS, $order->product_list, $id_carrier));
$order->total_discounts_tax_incl = (float)abs($this->context->cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS, $order->product_list, $id_carrier));
$order->total_discounts = $order->total_discounts_tax_incl;
$order->total_shipping_tax_excl = (float)$this->context->cart->getPackageShippingCost((int)$id_carrier, false, null, $order->product_list);
$order->total_shipping_tax_incl = (float)$this->context->cart->getPackageShippingCost((int)$id_carrier, true, null, $order->product_list);
$order->total_shipping = $order->total_shipping_tax_incl;
if (!is_null($carrier) && Validate::isLoadedObject($carrier))
$order->carrier_tax_rate = $carrier->getTaxesRate(new Address($this->context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}));
$order->total_wrapping_tax_excl = (float)abs($this->context->cart->getOrderTotal(false, Cart::ONLY_WRAPPING, $order->product_list, $id_carrier));
$order->total_wrapping_tax_incl = (float)abs($this->context->cart->getOrderTotal(true, Cart::ONLY_WRAPPING, $order->product_list, $id_carrier));
$order->total_wrapping = $order->total_wrapping_tax_incl;
$order->total_paid_tax_excl = (float)Tools::ps_round((float)$this->context->cart->getOrderTotal(false, Cart::BOTH, $order->product_list, $id_carrier), 2);
$order->total_paid_tax_incl = (float)Tools::ps_round((float)$this->context->cart->getOrderTotal(true, Cart::BOTH, $order->product_list, $id_carrier), 2);
$order->total_paid = $order->total_paid_tax_incl;
$order->invoice_date = '0000-00-00 00:00:00';
$order->delivery_date = '0000-00-00 00:00:00';
// Creating order
$result = $order->add();
if (!$result)
throw new PrestaShopException('Can\'t save Order');
// Hook validate order
Hook::exec('actionBeforeAddOrder', array(
'cart' => $this->context->cart,
'order' => $order,
'customer' => $this->context->customer,
'currency' => $this->context->currency,
'orderStatus' => $order_status
));
// Amount paid by customer is not the right one -> Status = payment error
// We don't use the following condition to avoid the float precision issues : http://www.php.net/manual/en/language.types.float.php
// if ($order->total_paid != $order->total_paid_real)
// We use number_format in order to compare two string
if ($order_status->logable && number_format($cart_total_paid, 2) != number_format($amount_paid, 2))
$id_order_state = Configuration::get('PS_OS_ERROR');
$order_list[] = $order;
// Insert new Order detail list using cart for the current order
$order_detail = new OrderDetail(null, null, $this->context);
$order_detail->createList($order, $this->context->cart, $id_order_state, $order->product_list, 0, true, $package_list[$id_address][$id_package]['id_warehouse']);
$order_detail_list[] = $order_detail;
// Adding an entry in order_carrier table
if (!is_null($carrier))
{
$order_carrier = new OrderCarrier();
$order_carrier->id_order = (int)$order->id;
$order_carrier->id_carrier = (int)$id_carrier;
$order_carrier->weight = (float)$order->getTotalWeight();
$order_carrier->shipping_cost_tax_excl = (float)$order->total_shipping_tax_excl;
$order_carrier->shipping_cost_tax_incl = (float)$order->total_shipping_tax_incl;
$order_carrier->add();
}
}
// The country can only change if the address used for the calculation is the delivery address, and if multi-shipping is activated
if (Configuration::get('PS_TAX_ADDRESS_TYPE') == 'id_address_delivery')
$this->context->country = $context_country;
// Register Payment only if the order status validate the order
if ($order_status->logable)
{
// $order is the last order loop in the foreach
// The method addOrderPayment of the class Order make a create a paymentOrder
// linked to the order reference and not to the order id
if (isset($extra_vars['transaction_id']))
$transaction_id = $extra_vars['transaction_id'];
else
$transaction_id = null;
if (!$order->addOrderPayment($amount_paid, null, $transaction_id))
throw new PrestaShopException('Can\'t save Order Payment');
}
// Next !
$only_one_gift = false;
$cart_rule_used = array();
$products = $this->context->cart->getProducts();
$cart_rules = $this->context->cart->getCartRules();
// Make sure CarRule caches are empty
CartRule::cleanCache();
foreach ($order_detail_list as $key => $order_detail)
{
$order = $order_list[$key];
if (!$order_creation_failed && isset($order->id))
{
if (!$secure_key)
$message .= '
'.Tools::displayError('Warning: the secure key is empty, check your payment account before validation');
// Optional message to attach to this order
if (isset($message) & !empty($message))
{
$msg = new Message();
$message = strip_tags($message, '
');
if (Validate::isCleanHtml($message))
{
$msg->message = $message;
$msg->id_order = (int)$order->id;
$msg->private = 1;
$msg->add();
}
}
// Insert new Order detail list using cart for the current order
//$orderDetail = new OrderDetail(null, null, $this->context);
//$orderDetail->createList($order, $this->context->cart, $id_order_state);
// Construct order detail table for the email
$products_list = '';
$virtual_product = true;
foreach ($order->product_list as $key => $product)
{
$price = Product::getPriceStatic((int)$product['id_product'], false, ($product['id_product_attribute'] ? (int)$product['id_product_attribute'] : null), 6, null, false, true, $product['cart_quantity'], false, (int)$order->id_customer, (int)$order->id_cart, (int)$order->{Configuration::get('PS_TAX_ADDRESS_TYPE')});
$price_wt = Product::getPriceStatic((int)$product['id_product'], true, ($product['id_product_attribute'] ? (int)$product['id_product_attribute'] : null), 2, null, false, true, $product['cart_quantity'], false, (int)$order->id_customer, (int)$order->id_cart, (int)$order->{Configuration::get('PS_TAX_ADDRESS_TYPE')});
$customization_quantity = 0;
$customized_datas = Product::getAllCustomizedDatas((int)$order->id_cart);
if (isset($customized_datas[$product['id_product']][$product['id_product_attribute']]))
{
$customization_text = '';
foreach ($customized_datas[$product['id_product']][$product['id_product_attribute']][$order->id_address_delivery] as $customization)
{
if (isset($customization['datas'][Product::CUSTOMIZE_TEXTFIELD]))
foreach ($customization['datas'][Product::CUSTOMIZE_TEXTFIELD] as $text)
$customization_text .= $text['name'].': '.$text['value'].'
';
if (isset($customization['datas'][Product::CUSTOMIZE_FILE]))
$customization_text .= sprintf(Tools::displayError('%d image(s)'), count($customization['datas'][Product::CUSTOMIZE_FILE])).'
';
$customization_text .= '---
';
}
$customization_text = rtrim($customization_text, '---
');
$customization_quantity = (int)$product['customization_quantity'];
$products_list .=
'