Page MenuHomeIn-Portal Phabricator

in-commerce
No OneTemporary

File Metadata

Created
Sun, Jan 5, 9:26 AM

in-commerce

Index: branches/5.2.x/units/gateways/gw_classes/google_checkout.php
===================================================================
--- branches/5.2.x/units/gateways/gw_classes/google_checkout.php (revision 15599)
+++ branches/5.2.x/units/gateways/gw_classes/google_checkout.php (revision 15600)
@@ -1,940 +1,940 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
require_once GW_CLASS_PATH.'/gw_base.php';
$class_name = 'kGWGoogleCheckout'; // for automatic installation
class kGWGoogleCheckout extends kGWBase
{
var $gwParams = Array ();
function InstallData()
{
$data = array(
'Gateway' => Array('Name' => 'Google Checkout', 'ClassName' => 'kGWGoogleCheckout', 'ClassFile' => 'google_checkout.php', 'RequireCCFields' => 0),
'ConfigFields' => Array(
'submit_url' => Array('Name' => 'Submit URL', 'Type' => 'text', 'ValueList' => '', 'Default' => 'https://checkout.google.com/api/checkout/v2'),
'merchant_id' => Array('Name' => 'Google merchant ID', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'merchant_key' => Array('Name' => 'Google merchant key', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'shipping_control' => Array('Name' => 'Shipping Control', 'Type' => 'select', 'ValueList' => '3=la_CreditDirect,4=la_CreditPreAuthorize', 'Default' => 3),
)
);
return $data;
}
/**
* Returns payment form submit url
*
* @param Array $gw_params gateway params from payment type config
* @return string
*/
function getFormAction($gw_params)
{
return $gw_params['submit_url'].'/checkout/Merchant/'.$gw_params['merchant_id'];
}
/**
* Processed input data and convets it to fields understandable by gateway
*
* @param Array $item_data current order fields
* @param Array $tag_params additional params for gateway passed through tag
* @param Array $gw_params gateway params from payment type config
* @return Array
*/
function getHiddenFields($item_data, $tag_params, $gw_params)
{
$ret = Array();
$this->gwParams = $gw_params;
$cart_xml = $this->getCartXML($item_data);
$ret['cart'] = base64_encode($cart_xml);
$ret['signature'] = base64_encode( $this->CalcHmacSha1($cart_xml, $gw_params) );
return $ret;
}
function getCartXML($cart_fields)
{
// 1. prepare shopping cart content
$sql = 'SELECT *
FROM '.TABLE_PREFIX.'OrderItems oi
LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = oi.ProductId
WHERE oi.OrderId = '.$cart_fields['OrderId'];
$order_items = $this->Conn->Query($sql);
$ml_formatter = $this->Application->recallObject('kMultiLanguage');
/* @var $ml_formatter kMultiLanguage */
$cart_xml = Array ();
foreach ($order_items as $order_item) {
$cart_xml[] = ' <item>
- <item-name>'.htmlspecialchars($order_item['ProductName']).'</item-name>
- <item-description>'.htmlspecialchars($order_item[$ml_formatter->LangFieldName('DescriptionExcerpt')]).'</item-description>'.
+ <item-name>'.htmlspecialchars($order_item['ProductName'], null, CHARSET).'</item-name>
+ <item-description>'.htmlspecialchars($order_item[$ml_formatter->LangFieldName('DescriptionExcerpt')], null, CHARSET).'</item-description>'.
$this->getPriceXML('unit-price', $order_item['Price']).'
<quantity>'.$order_item['Quantity'].'</quantity>
</item>';
}
$cart_xml = '<items>'.implode("\n", $cart_xml).'</items>';
// 2. add order identification info (for google checkout notification)
$cart_xml .= ' <merchant-private-data>
<session_id>'.$this->Application->GetSID().'</session_id>
<order_id>'.$cart_fields['OrderId'].'</order_id>
</merchant-private-data>';
// 3. add all shipping types (with no costs)
$sql = 'SELECT Name
FROM '.TABLE_PREFIX.'ShippingType
WHERE Status = '.STATUS_ACTIVE;
$shipping_types = $this->Conn->GetCol($sql);
$shipping_xml = '';
foreach ($shipping_types as $shipping_name) {
- $shipping_xml .= ' <merchant-calculated-shipping name="'.htmlspecialchars($shipping_name).'">
+ $shipping_xml .= ' <merchant-calculated-shipping name="'.htmlspecialchars($shipping_name, null, CHARSET).'">
<price currency="USD">0.00</price>
</merchant-calculated-shipping>';
}
$use_ssl = substr($this->gwParams['submit_url'], 0, 8) == 'https://' ? true : null;
$shipping_url = $this->getNotificationUrl('units/gateways/gw_classes/notify_scripts/google_checkout_shippings.php', $use_ssl);
$shipping_xml = '<merchant-checkout-flow-support>
<shipping-methods>'.$shipping_xml.'</shipping-methods>
<merchant-calculations>
<merchant-calculations-url>'.$shipping_url.'</merchant-calculations-url>
</merchant-calculations>
</merchant-checkout-flow-support>';
$xml = '<checkout-shopping-cart xmlns="http://checkout.google.com/schema/2">
<shopping-cart>'.$cart_xml.'</shopping-cart>
<checkout-flow-support>'.$shipping_xml.'</checkout-flow-support>
</checkout-shopping-cart>';
return $xml;
}
/**
* Returns price formatted as xml tag
*
* @param string $tag_name
* @param float $price
* @return string
*/
function getPriceXML($tag_name, $price)
{
$currency = $this->Application->RecallVar('curr_iso');
return '<'.$tag_name.' currency="'.$currency.'">'.sprintf('%.2f', $price).'</'.$tag_name.'>';
}
/**
* Calculates the cart's hmac-sha1 signature, this allows google to verify
* that the cart hasn't been tampered by a third-party.
*
* {@link http://code.google.com/apis/checkout/developer/index.html#create_signature}
*
* @param string $data the cart's xml
* @return string the cart's signature (in binary format)
*/
function CalcHmacSha1($data, $gw_params) {
$key = $gw_params['merchant_key'];
$blocksize = 64;
$hashfunc = 'sha1';
if (mb_strlen($key) > $blocksize) {
$key = pack('H*', $hashfunc($key));
}
$key = str_pad($key, $blocksize, chr(0x00));
$ipad = str_repeat(chr(0x36), $blocksize);
$opad = str_repeat(chr(0x5c), $blocksize);
$hmac = pack(
'H*', $hashfunc(
($key^$opad).pack(
'H*', $hashfunc(
($key^$ipad).$data
)
)
)
);
return $hmac;
}
/**
* Returns XML request, that GoogleCheckout posts to notification / shipping calculation scripts
*
* @return string
*/
function getRequestXML()
{
$xml_data = $GLOBALS['HTTP_RAW_POST_DATA'];
if ( $this->Application->isDebugMode() ) {
$this->toLog($xml_data, 'xml_request.html');
}
return $xml_data;
// for debugging
/*return '<order-state-change-notification xmlns="http://checkout.google.com/schema/2"
serial-number="c821426e-7caa-4d51-9b2e-48ef7ecd6423">
<google-order-number>434532759516557</google-order-number>
<new-financial-order-state>CHARGEABLE</new-financial-order-state>
<new-fulfillment-order-state>NEW</new-fulfillment-order-state>
<previous-financial-order-state>REVIEWING</previous-financial-order-state>
<previous-fulfillment-order-state>NEW</previous-fulfillment-order-state>
<timestamp>2007-03-19T15:06:29.051Z</timestamp>
</order-state-change-notification>';*/
}
/**
* Processes notifications from google checkout
*
* @param Array $gw_params
* @return int
*/
function processNotification($gw_params)
{
// parse xml & get order_id from there, like sella pay
$this->gwParams = $gw_params;
$xml_helper = $this->Application->recallObject('kXMLHelper');
/* @var $xml_helper kXMLHelper */
$root_node =& $xml_helper->Parse( $this->getRequestXML() );
/* @var $root_node kXMLNode */
$this->Application->XMLHeader();
define('DBG_SKIP_REPORTING', 1);
$order_approvable = false;
switch ($root_node->Name) {
case 'MERCHANT-CALCULATION-CALLBACK':
$xml_responce = $this->getShippingXML($root_node);
break;
case 'NEW-ORDER-NOTIFICATION':
case 'RISK-INFORMATION-NOTIFICATION':
case 'ORDER-STATE-CHANGE-NOTIFICATION':
// http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Notification_API.html#new_order_notifications
list ($order_approvable, $xml_responce) = $this->getNotificationResponceXML($root_node);
break;
}
echo $xml_responce;
if ( $this->Application->isDebugMode() ) {
$this->toLog($xml_responce, 'xml_responce.html');
}
return $order_approvable ? 1 : 0;
}
/**
* Writes XML requests and responces to a file
*
* @param string $xml_data
* @param string $xml_file
*/
function toLog($xml_data, $xml_file)
{
$fp = fopen( (defined('RESTRICTED') ? RESTRICTED : FULL_PATH) . '/' . $xml_file, 'a' );
fwrite($fp, '--- ' . adodb_date('Y-m-d H:i:s') . ' ---' . "\n" . $xml_data);
fclose($fp);
}
/**
* Processes notification
*
* @param kXMLNode $root_node
*/
function getNotificationResponceXML(&$root_node)
{
// we can get notification type by "$root_node->Name"
$order_approvable = false;
switch ($root_node->Name) {
case 'NEW-ORDER-NOTIFICATION':
$order_approvable = $this->processNewOrderNotification($root_node);
break;
case 'RISK-INFORMATION-NOTIFICATION':
$order_approvable = $this->processRiskInformationNotification($root_node);
break;
case 'ORDER-STATE-CHANGE-NOTIFICATION':
$order_approvable = $this->processOrderStateChangeNotification($root_node);
break;
}
// !!! globally set order id, so gw_responce.php will not fail in setting TransactionStatus
// 1. receive new order notification
// put address & payment type in our order using id found in merchant-private-data (Make order status: Incomplete)
// 2. receive risk information
// don't know what to do, just mark order some how (Make order status: Incomplete)
// 3. receive status change notification to CHARGEABLE (Make order status: Pending)
// only mark order status
// 4. admin approves order
// make api call, that changes order state (fulfillment-order-state) to PROCESSING or DELIVERED (see manual)
// 5. admin declines order
// make api call, that changes order state (fulfillment-order-state) to WILL_NOT_DELIVER
// Before you ship the items in an order, you should ensure that you have already received the new order notification for the order,
// the risk information notification for the order and an order state change notification informing you that the order's financial
// state has been updated to CHARGEABLE
return Array ($order_approvable, '<notification-acknowledgment xmlns="http://checkout.google.com/schema/2" serial-number="'.$root_node->Attributes['SERIAL-NUMBER'].'" />');
}
/**
* Returns shipping calculations and places part of shipping address into order (1st step)
*
* http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Merchant_Calculations_API.html#Returning_Merchant_Calculation_Results
*
* @param kXMLNode $node
* @return string
*/
function getShippingXML(&$root_node)
{
// 1. extract data from xml
$search_nodes = Array (
'SHOPPING-CART:MERCHANT-PRIVATE-DATA',
'CALCULATE:ADDRESSES:ANONYMOUS-ADDRESS',
'CALCULATE:SHIPPING',
);
foreach ($search_nodes as $search_string) {
$found_node =& $root_node;
/* @var $found_node kXMLNode */
$search_string = explode(':', $search_string);
foreach ($search_string as $search_node) {
$found_node =& $found_node->FindChild($search_node);
}
$node_data = Array ();
$sub_node =& $found_node->firstChild;
/* @var $sub_node kXMLNode */
do {
if ($found_node->Name == 'SHIPPING') {
$node_data[] = $sub_node->Attributes['NAME'];
}
else {
$node_data[$sub_node->Name] = $sub_node->Data;
}
} while ( ($sub_node =& $sub_node->NextSibling()) );
switch ($found_node->Name) {
case 'MERCHANT-PRIVATE-DATA':
$order_id = $node_data['ORDER_ID'];
$session_id = $node_data['SESSION_ID'];
break;
case 'ANONYMOUS-ADDRESS':
$address_info = $node_data;
$address_id = $found_node->Attributes['ID'];
break;
case 'SHIPPING':
$process_shippings = $node_data;
break;
}
}
// 2. update shipping address in order
$order = $this->Application->recallObject('ord', null, Array ('skip_autoload' => true));
/* @var $order OrdersItem */
$order->Load($order_id);
$shipping_address = Array (
'ShippingCity' => $address_info['CITY'],
'ShippingState' => $address_info['REGION'],
'ShippingZip' => $address_info['POSTAL-CODE'],
);
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$shipping_address['ShippingCountry'] = $cs_helper->getCountryIso($address_info['COUNTRY-CODE'], true);
$order->SetDBFieldsFromHash($shipping_address);
$order->Update();
// 3. get shipping rates based on given address
$shipping_types_xml = '';
$shipping_types = $this->getOrderShippings($order);
// add available shipping types
foreach ($shipping_types as $shipping_type) {
$shipping_name = $shipping_type['ShippingName'];
$processable_shipping_index = array_search($shipping_name, $process_shippings);
if ($processable_shipping_index !== false) {
- $shipping_types_xml .= '<result shipping-name="'.htmlspecialchars($shipping_name).'" address-id="'.$address_id.'">
+ $shipping_types_xml .= '<result shipping-name="'.htmlspecialchars($shipping_name, null, CHARSET).'" address-id="'.$address_id.'">
<shipping-rate currency="USD">'.sprintf('%01.2f', $shipping_type['TotalCost']).'</shipping-rate>
<shippable>true</shippable>
</result>';
// remove available shipping type from processable list
unset($process_shippings[$processable_shipping_index]);
}
}
// add unavailable shipping types
foreach ($process_shippings as $shipping_name) {
- $shipping_types_xml .= '<result shipping-name="'.htmlspecialchars($shipping_name).'" address-id="'.$address_id.'">
+ $shipping_types_xml .= '<result shipping-name="'.htmlspecialchars($shipping_name, null, CHARSET).'" address-id="'.$address_id.'">
<shipping-rate currency="USD">0.00</shipping-rate>
<shippable>false</shippable>
</result>';
}
$shipping_types_xml = '<?xml version="1.0" encoding="UTF-8"?>
<merchant-calculation-results xmlns="http://checkout.google.com/schema/2">
<results>'.$shipping_types_xml.'</results>
</merchant-calculation-results>';
return $shipping_types_xml;
}
/**
* Places all information from google checkout into order (2nd step)
*
* @param kXMLNode $root_node
*/
function processNewOrderNotification(&$root_node)
{
// 1. extract data from xml
$search_nodes = Array (
'SHOPPING-CART:MERCHANT-PRIVATE-DATA',
'ORDER-ADJUSTMENT:SHIPPING:MERCHANT-CALCULATED-SHIPPING-ADJUSTMENT',
'BUYER-ID',
'GOOGLE-ORDER-NUMBER',
'BUYER-SHIPPING-ADDRESS',
'BUYER-BILLING-ADDRESS',
);
$user_address = Array ();
foreach ($search_nodes as $search_string) {
$found_node =& $root_node;
/* @var $found_node kXMLNode */
$search_string = explode(':', $search_string);
foreach ($search_string as $search_node) {
$found_node =& $found_node->FindChild($search_node);
}
$node_data = Array ();
if ($found_node->Children) {
$sub_node =& $found_node->firstChild;
/* @var $sub_node kXMLNode */
do {
$node_data[$sub_node->Name] = $sub_node->Data;
} while ( ($sub_node =& $sub_node->NextSibling()) );
}
switch ($found_node->Name) {
case 'MERCHANT-PRIVATE-DATA':
$order_id = $node_data['ORDER_ID'];
$session_id = $node_data['SESSION_ID'];
break;
case 'MERCHANT-CALCULATED-SHIPPING-ADJUSTMENT':
$shpipping_info = $node_data;
break;
case 'BUYER-ID':
$buyer_id = $found_node->Data;
break;
case 'GOOGLE-ORDER-NUMBER':
$google_order_number = $found_node->Data;
break;
case 'BUYER-SHIPPING-ADDRESS':
$user_address['Shipping'] = $node_data;
break;
case 'BUYER-BILLING-ADDRESS':
$user_address['Billing'] = $node_data;
break;
}
}
// 2. update shipping address in order
$order = $this->Application->recallObject('ord', null, Array ('skip_autoload' => true));
/* @var $order OrdersItem */
$order->Load($order_id);
if (!$order->isLoaded()) {
return false;
}
// 2.1. this is 100% notification from google -> mark order with such payment type
$order->SetDBField('PaymentType', $this->Application->GetVar('payment_type_id'));
$this->parsed_responce = Array (
'GOOGLE-ORDER-NUMBER' => $google_order_number,
'BUYER-ID' => $buyer_id
);
// 2.2. save google checkout order information (maybe needed for future notification processing)
$order->SetDBField('GWResult1', serialize($this->parsed_responce));
$order->SetDBField('GoogleOrderNumber', $google_order_number);
// 2.3. set user-selected shipping type
$shipping_types = $this->getOrderShippings($order);
foreach ($shipping_types as $shipping_type) {
if ($shipping_type['ShippingName'] == $shpipping_info['SHIPPING-NAME']) {
$order->SetDBField('ShippingInfo', serialize(Array (1 => $shipping_type))); // minimal package number is 1
$order->SetDBField('ShippingCost', $shipping_type['TotalCost']); // set total shipping cost
break;
}
}
// 2.4. set full shipping & billing address
$address_mapping = Array (
'CONTACT-NAME' => 'To',
'COMPANY-NAME' => 'Company',
'EMAIL' => 'Email',
'PHONE' => 'Phone',
'FAX' => 'Fax',
'ADDRESS1' => 'Address1',
'ADDRESS2' => 'Address2',
'CITY' => 'City',
'REGION' => 'State',
'POSTAL-CODE' => 'Zip',
);
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
foreach ($user_address as $field_prefix => $address_details) {
foreach ($address_mapping as $src_field => $dst_field) {
$order->SetDBField($field_prefix.$dst_field, $address_details[$src_field]);
}
if (!$order->GetDBField($field_prefix.'Phone')) {
$order->SetDBField($field_prefix.'Phone', '-'); // required field
}
$order->SetDBField( $field_prefix.'Country', $cs_helper->getCountryIso($address_details['COUNTRY-CODE'], true) );
}
$order->SetDBField('OnHold', 1);
$order->SetDBField('Status', ORDER_STATUS_PENDING);
$order->Update();
// unlink order, that GoogleCheckout used from shopping cart on site
$sql = 'DELETE
FROM '.TABLE_PREFIX.'UserSessionData
WHERE VariableName = "ord_id" AND VariableValue = '.$order->GetID();
$this->Conn->Query($sql);
// simulate visiting shipping screen
$sql = 'UPDATE '.TABLE_PREFIX.'OrderItems
SET PackageNum = 1
WHERE OrderId = '.$order->GetID();
$this->Conn->Query($sql);
return false;
}
/**
* Saves risk information in order record (3rd step)
*
* @param kXMLNode $root_node
*/
function processRiskInformationNotification(&$root_node)
{
// 1. extract data from xml
$search_nodes = Array (
'GOOGLE-ORDER-NUMBER',
'RISK-INFORMATION',
);
foreach ($search_nodes as $search_string) {
$found_node =& $root_node;
/* @var $found_node kXMLNode */
$search_string = explode(':', $search_string);
foreach ($search_string as $search_node) {
$found_node =& $found_node->FindChild($search_node);
}
$node_data = Array ();
if ($found_node->Children) {
$sub_node =& $found_node->firstChild;
/* @var $sub_node kXMLNode */
do {
$node_data[$sub_node->Name] = $sub_node->Data;
} while ( ($sub_node =& $sub_node->NextSibling()) );
}
switch ($found_node->Name) {
case 'GOOGLE-ORDER-NUMBER':
$google_order_number = $found_node->Data;
break;
case 'RISK-INFORMATION':
$risk_information = $node_data;
unset( $risk_information['BILLING-ADDRESS'] );
break;
}
}
// 2. update shipping address in order
$order = $this->Application->recallObject('ord', null, Array ('skip_autoload' => true));
/* @var $order OrdersItem */
$order->Load($google_order_number, 'GoogleOrderNumber');
if (!$order->isLoaded()) {
return false;
}
// 2.1. save risk information in order
$this->parsed_responce = unserialize($order->GetDBField('GWResult1'));
$this->parsed_responce = array_merge_recursive($this->parsed_responce, $risk_information);
$order->SetDBField('GWResult1', serialize($this->parsed_responce));
$order->Update();
return false;
}
/**
* Perform PREAUTH/SALE type transaction direct from php script wihtout redirecting to 3rd-party website
*
* @param Array $item_data
* @param Array $gw_params
* @return bool
*/
function DirectPayment($item_data, $gw_params)
{
$this->gwParams = $gw_params;
if ($gw_params['shipping_control'] == SHIPPING_CONTROL_PREAUTH) {
// when shipping control is Pre-Authorize -> do nothing and charge when admin approves order
return true;
}
$this->_chargeOrder($item_data);
return false;
}
/**
* Issue charge-order api call
*
* @param Array $item_data
* @return bool
*/
function _chargeOrder($item_data)
{
$charge_xml = ' <charge-order xmlns="http://checkout.google.com/schema/2" google-order-number="'.$item_data['GoogleOrderNumber'].'">
<amount currency="USD">'.sprintf('%.2f', $item_data['TotalAmount']).'</amount>
</charge-order>';
$root_node =& $this->executeAPICommand($charge_xml);
$this->parsed_responce = unserialize($item_data['GWResult1']);
if ($root_node->Name == 'REQUEST-RECEIVED') {
$this->parsed_responce['FINANCIAL-ORDER-STATE'] = 'CHARGING';
return true;
}
return false;
}
/**
* Perform SALE type transaction direct from php script wihtout redirecting to 3rd-party website
*
* @param Array $item_data
* @param Array $gw_params
* @return bool
*/
function Charge($item_data, $gw_params)
{
$this->gwParams = $gw_params;
if ($gw_params['shipping_control'] == SHIPPING_CONTROL_DIRECT) {
// when shipping control is Direct Payment -> do nothing and auto-charge on notification received
return true;
}
$this->_chargeOrder($item_data);
$order = $this->Application->recallObject('ord.-item', null, Array ('skip_autoload' => true));
/* @var $order OrdersItem */
$order->Load($item_data['OrderId']);
if (!$order->isLoaded()) {
return false;
}
$order->SetDBField('OnHold', 1);
$order->Update();
return false;
}
/**
* Executes API command for order and returns result
*
* @param string $command_xml
* @return kXMLNode
*/
function &executeAPICommand($command_xml)
{
$submit_url = $this->gwParams['submit_url'].'/request/Merchant/'.$this->gwParams['merchant_id'];
$curl_helper = $this->Application->recallObject('CurlHelper');
/* @var $curl_helper kCurlHelper */
$xml_helper = $this->Application->recallObject('kXMLHelper');
/* @var $xml_helper kXMLHelper */
$curl_helper->SetPostData($command_xml);
$auth_options = Array (
CURLOPT_USERPWD => $this->gwParams['merchant_id'].':'.$this->gwParams['merchant_key'],
);
$curl_helper->setOptions($auth_options);
$xml_responce = $curl_helper->Send($submit_url);
$root_node =& $xml_helper->Parse($xml_responce);
/* @var $root_node kXMLNode */
return $root_node;
}
/**
* Marks order as pending, when it's google status becomes CHARGEABLE (4th step)
*
* @param kXMLNode $root_node
*/
function processOrderStateChangeNotification(&$root_node)
{
// 1. extract data from xml
$search_nodes = Array (
'GOOGLE-ORDER-NUMBER',
'NEW-FINANCIAL-ORDER-STATE',
'PREVIOUS-FINANCIAL-ORDER-STATE',
);
$order_state = Array ();
foreach ($search_nodes as $search_string) {
$found_node =& $root_node;
/* @var $found_node kXMLNode */
$search_string = explode(':', $search_string);
foreach ($search_string as $search_node) {
$found_node =& $found_node->FindChild($search_node);
}
switch ($found_node->Name) {
case 'GOOGLE-ORDER-NUMBER':
$google_order_number = $found_node->Data;
break;
case 'NEW-FINANCIAL-ORDER-STATE':
$order_state['new'] = $found_node->Data;
break;
case 'PREVIOUS-FINANCIAL-ORDER-STATE':
$order_state['old'] = $found_node->Data;
break;
}
}
// 2. update shipping address in order
$order = $this->Application->recallObject('ord', null, Array ('skip_autoload' => true));
/* @var $order OrdersItem */
$order->Load($google_order_number, 'GoogleOrderNumber');
if (!$order->isLoaded()) {
return false;
}
$state_changed = ($order_state['old'] != $order_state['new']);
if ($state_changed) {
$order_charged = ($order_state['new'] == 'CHARGED') && ($order->GetDBField('Status') == ORDER_STATUS_PENDING);
$this->parsed_responce = unserialize($order->GetDBField('GWResult1'));
$this->parsed_responce['FINANCIAL-ORDER-STATE'] = $order_state['new'];
$order->SetDBField('GWResult1', serialize($this->parsed_responce));
if ($order_charged) {
// when using Pre-Authorize
$order->SetDBField('OnHold', 0);
}
$order->Update();
if ($order_charged) {
// when using Pre-Authorize
$order_eh = $this->Application->recallObject('ord_EventHandler');
/* @var $order_eh OrdersEventHandler */
$order_eh->SplitOrder( new kEvent('ord:OnMassOrderApprove'), $order);
}
}
// update order record in "google_checkout_notify.php" only when such state change happens
$order_chargeable = ($order_state['new'] == 'CHARGEABLE') && $state_changed;
if ($order_chargeable) {
if ($this->gwParams['shipping_control'] == SHIPPING_CONTROL_PREAUTH) {
$order->SetDBField('OnHold', 0);
$order->Update();
}
$process_xml = '<process-order xmlns="http://checkout.google.com/schema/2" google-order-number="'.$order->GetDBField('GoogleOrderNumber').'"/>';
$root_node =& $this->executeAPICommand($process_xml);
}
return $order_chargeable;
}
/**
* Retrieves shipping types available for given order
*
* @param OrdersItem $order
* @return Array
*/
function getOrderShippings(&$order)
{
$weight_sql = 'IF(oi.Weight IS NULL, 0, oi.Weight * oi.Quantity)';
$query = ' SELECT
SUM(oi.Quantity) AS TotalItems,
SUM('.$weight_sql.') AS TotalWeight,
SUM(oi.Price * oi.Quantity) AS TotalAmount,
SUM(oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Quantity, 0)) AS TotalItemsPromo,
SUM('.$weight_sql.') - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, '.$weight_sql.', 0)) AS TotalWeightPromo,
SUM(oi.Price * oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Price * oi.Quantity, 0)) AS TotalAmountPromo
FROM '.TABLE_PREFIX.'OrderItems oi
LEFT JOIN '.TABLE_PREFIX.'Products p ON oi.ProductId = p.ProductId
WHERE oi.OrderId = '.$order->GetID().' AND p.Type = 1';
$shipping_totals = $this->Conn->GetRow($query);
$this->Application->recallObject('ShippingQuoteEngine');
$quote_engine_collector = $this->Application->recallObject('ShippingQuoteCollector');
/* @var $quote_engine_collector ShippingQuoteCollector */
$shipping_quote_params = Array(
'dest_country' => $order->GetDBField('ShippingCountry'),
'dest_state' => $order->GetDBField('ShippingState'),
'dest_postal' => $order->GetDBField('ShippingZip'),
'dest_city' => $order->GetDBField('ShippingCity'),
'dest_addr1' => '',
'dest_addr2' => '',
'dest_name' => 'user-' . $order->GetDBField('PortalUserId'),
'packages' => Array(
Array(
'package_key' => 'package1',
'weight' => $shipping_totals['TotalWeight'],
'weight_unit' => 'KG',
'length' => '',
'width' => '',
'height' => '',
'dim_unit' => 'IN',
'packaging' => 'BOX',
'contents' => 'OTR',
'insurance' => '0'
),
),
'amount' => $shipping_totals['TotalAmount'],
'items' => $shipping_totals['TotalItems'],
'limit_types' => serialize(Array ('ANY')),
'promo_params' => Array (
'items' => $shipping_totals['TotalItemsPromo'],
'amount' => $shipping_totals['TotalAmountPromo'],
'weight' => $shipping_totals['TotalWeightPromo'],
),
);
return $quote_engine_collector->GetShippingQuotes($shipping_quote_params);
}
/**
* Returns gateway responce from last operation
*
* @return string
*/
function getGWResponce()
{
return serialize($this->parsed_responce);
}
/**
* Informs payment gateway, that order has been shipped
*
* http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Order_Level_Shipping.html#Deliver_Order
*
* @param Array $item_data
* @param Array $gw_params
* @return bool
*/
function OrderShipped($item_data, $gw_params)
{
$this->gwParams = $gw_params;
$shipping_info = unserialize($item_data['ShippingInfo']);
if (getArrayValue($shipping_info, 'Code')) {
$traking_carrier = '<carrier>'.$item_data['Code'].'</carrier>';
}
if ($item_data['ShippingTracking']) {
$tracking_data = '<tracking-data>'.$traking_carrier.'
<tracking-number>'.$item_data['ShippingTracking'].'</tracking-number>
</tracking-data>';
}
$ship_xml = ' <deliver-order xmlns="http://checkout.google.com/schema/2" google-order-number="'.$item_data['GoogleOrderNumber'].'">
'.$traking_data.'
<send-email>true</send-email>
</deliver-order>';
$root_node =& $this->executeAPICommand($ship_xml);
}
/**
* Informs payment gateway, that order has been declined
*
* @param Array $item_data
* @param Array $gw_params
* @return bool
*/
function OrderDeclined($item_data, $gw_params)
{
}
}
\ No newline at end of file
Index: branches/5.2.x/units/gateways/gw_classes/ideal_nl.php
===================================================================
--- branches/5.2.x/units/gateways/gw_classes/ideal_nl.php (revision 15599)
+++ branches/5.2.x/units/gateways/gw_classes/ideal_nl.php (revision 15600)
@@ -1,170 +1,170 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
/* http://www.ideal.nl/ gateway class */
require_once GW_CLASS_PATH.'/gw_base.php';
$class_name = 'kGWiDEALnl'; // for automatic installation
class kGWiDEALnl extends kGWBase
{
function InstallData()
{
$data = array(
'Gateway' => Array('Name' => 'iDEAL.nl', 'ClassName' => 'kGWiDEALnl', 'ClassFile' => 'ideal_nl.php', 'RequireCCFields' => 0),
'ConfigFields' => Array(
'partner_id' => Array('Name' => 'Partner ID', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'request_url' => Array('Name' => 'Request URL', 'Type' => 'text', 'ValueList' => '', 'Default' => 'http://www.mollie.nl/xml/ideals'),
'shipping_control' => Array('Name' => 'Shipping Control', 'Type' => 'select', 'ValueList' => '3=la_CreditDirect,4=la_CreditPreAuthorize', 'Default' => '3'),
)
);
return $data;
}
/**
* Processed input data and convets it to fields understandable by gateway
*
* @param Array $item_data
* @param Array $tag_params additional params for gateway passed through tag
* @param Array $gw_params gateway params from payment type config
* @return Array
*/
function getHiddenFields($item_data, $tag_params, $gw_params)
{
$this->Application->StoreVar('gw_success_template',$tag_params['return_template']);
$this->Application->StoreVar('gw_cancel_template',$tag_params['cancel_template']);
$curl_helper = $this->Application->recallObject('CurlHelper');
/* @var $curl_helper kCurlHelper */
$banks = $curl_helper->Send($gw_params['request_url'].'?a=banklist');
$parser = $this->Application->recallObject('kXMLHelper');
/* @var $parser kXMLHelper */
$bank_data =& $parser->Parse($banks);
$bank_data->FindChild('response');
$banks = array();
foreach ($bank_data->Children as $a_child) {
if ($a_child->Name != 'BANK') continue;
$banks[$a_child->FindChildValue('bank_id')] = $a_child->FindChildValue('bank_name');
}
$ret = $this->Application->Phrase('lu_Select_iDEAL_bank').': <select name="ideal_nl_bank_id">';
foreach ($banks as $id => $name) {
$ret .= '<option value="'.$id.'">'.$name.'</option>';
}
$ret .= '</select>';
$ret .= '<input type="hidden" name="events[ord]" value="OnCompleteOrder" />'."\n";
return $ret;
}
function DirectPayment($item_data, $gw_params)
{
$fields = array();
$fields['a'] = 'fetch';
$fields['partnerid'] = $gw_params['partner_id'];
$txt_amount = sprintf("%.2f", $item_data['TotalAmount']);
$fields['amount'] = str_replace( Array('.', ','), '', $txt_amount);
$fields['bank_id'] = $this->Application->GetVar('ideal_nl_bank_id');
$fields['description'] = 'Invoice #'.$item_data['OrderNumber'];
$fields['returnurl'] = $this->getNotificationUrl() . '?order_id='.$item_data['OrderId'];
$fields['reporturl'] = $this->getNotificationUrl() . '?mode=report&order_id='.$item_data['OrderId'];
$curl_helper = $this->Application->recallObject('CurlHelper');
/* @var $curl_helper kCurlHelper */
$curl_helper->SetRequestData($fields);
$transaction_xml = $curl_helper->Send($gw_params['request_url']);
$parser = $this->Application->recallObject('kXMLHelper');
/* @var $parser kXMLHelper */
$trans_data =& $parser->Parse($transaction_xml);
$transaction_id = $trans_data->FindChildValue('transaction_id');
$url = $trans_data->FindChildValue('url');
if ($transaction_id && $url) {
$this->Application->Redirect('external:'.$url);
}
else {
$error_msg = $trans_data->FindChildValue('message');
$this->parsed_responce['XML'] = $transaction_xml;
$this->Application->SetVar('failure_template', $this->Application->RecallVar('gw_cancel_template'));
- $this->parsed_responce['MESSAGE'] = $error_msg ? $error_msg : 'Unknown gateway error ('.htmlspecialchars($transaction_xml).')';
+ $this->parsed_responce['MESSAGE'] = $error_msg ? $error_msg : 'Unknown gateway error ('.htmlspecialchars($transaction_xml, null, CHARSET).')';
return false;
}
return true;
}
function getErrorMsg()
{
return $this->parsed_responce['MESSAGE'];
}
function getGWResponce()
{
return serialize($this->parsed_responce);
}
function processNotification($gw_params)
{
// silent mode
if ($this->Application->GetVar('mode') == 'report') {
$fields = array();
$fields['a'] = 'check';
$fields['partnerid'] = $gw_params['partner_id'];
$fields['transaction_id'] = $this->Application->GetVar('transaction_id');
$fields['bank_id'] = $this->Application->GetVar('ideal_nl_bank_id');
$curl_helper = $this->Application->recallObject('CurlHelper');
/* @var $curl_helper kCurlHelper */
$curl_helper->SetRequestData($fields);
$check_xml = $curl_helper->Send($gw_params['request_url']);
$parser = $this->Application->recallObject('kXMLHelper');
/* @var $parser kXMLHelper */
$trans_data =& $parser->Parse($check_xml);
$response = $trans_data->FindChild('order');
foreach ($response->Children as $a_child) {
$this->parsed_responce[$a_child->Name] = $a_child->Data;
}
$this->parsed_responce['XML'] = $check_xml;
$result = $trans_data->FindChildValue('payed') == 'true' ? 1:0;
return $result;
}
else {
$order = $this->Application->recallObject('ord');
if ($order->GetDBField('Status') == ORDER_STATUS_INCOMPLETE) {
// error
$t = $this->Application->RecallVar('gw_cancel_template');
$this->parsed_responce = unserialize($order->GetDBField('GWResult1'));
$this->Application->StoreVar('gw_error', $this->getErrorMsg());
$this->Application->Redirect($t, array('pass'=>'m', 'm_cat_id'=>0));
}
else {
// ok
$t = $this->Application->RecallVar('gw_success_template');
$this->Application->Redirect($t, array('pass'=>'m', 'm_cat_id'=>0));
}
}
}
}
\ No newline at end of file
Index: branches/5.2.x/units/gateways/gw_tag_processor.php
===================================================================
--- branches/5.2.x/units/gateways/gw_tag_processor.php (revision 15599)
+++ branches/5.2.x/units/gateways/gw_tag_processor.php (revision 15600)
@@ -1,127 +1,127 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class GatewayTagProcessor extends kDBTagProcessor {
/**
* Payment gateway config values for current payment type
*
* @var Array
* @access private
*/
var $ConfigValues=Array();
/**
* Payment type id for current gateway values
*
* @var int
* @access private
*/
var $PaymentTypeID=0;
function initGWConfigValues()
{
$payment_type_id = $this->Application->GetVar('pt_id');
$GWConfigValue = $this->Application->recallObject('gwfv');
$sql = 'SELECT Value, GWConfigFieldId FROM '.$GWConfigValue->TableName.' WHERE PaymentTypeId = '.$payment_type_id;
$this->ConfigValues = $this->Conn->GetCol($sql,'GWConfigFieldId');
}
function gwConfigValue($params)
{
$object = $this->getObject($params);
/* @var $object kDBItem */
$id = $object->GetID();
$value = isset($this->ConfigValues[$id]) ? $this->ConfigValues[$id] : '';
if ( !array_key_exists('no_special', $params) || !$params['no_special'] ) {
- $value = htmlspecialchars($value);
+ $value = htmlspecialchars($value, null, CHARSET);
}
if ( getArrayValue($params, 'checked') ) {
$value = ($value == 1) ? 'checked' : '';
}
return $value;
}
function PrintList($params)
{
$list = $this->Application->recallObject( $this->getPrefixSpecial(), $this->Prefix.'_List', $params);
$id_field = $this->Application->getUnitOption($this->Prefix,'IDField');
$list->Query();
$list->GoFirst();
$block_params=$this->prepareTagParams($params);
$block_params['name']=$params['block'];
$block_params['pass_params']='true';
$payment_type_object = $this->Application->recallObject('pt');
$o = '';
while (!$list->EOL())
{
$this->Application->SetVar( $this->getPrefixSpecial().'_id', $list->GetDBField($id_field) );
$display_style = $payment_type_object->GetDBField('GatewayId') == $list->GetDBField('GatewayId') ? 'table-row' : 'none';
$block_params['input_block'] = $params['input_block_prefix'].$list->GetDBField('ElementType');
$block_params['gateway_id'] = $list->GetDBField('GatewayId');
$block_params['display'] = $display_style;
$o .= $this->Application->ParseBlock($block_params, 1);
$list->GoNext();
}
return $o;
}
/**
* Prints list a all possible field options
*
* @param Array $params
* @return string
* @access protected
*/
protected function PredefinedOptions($params)
{
$object = $this->getObject($params);
/* @var $object kDBItem */
$block_params = $this->prepareTagParams($params);
$block_params['name'] = $this->SelectParam($params, 'render_as,block');
$block_params['pass_params'] = 'true';
$o = '';
$value = $this->gwConfigValue($params);
$options = explode(',', $object->GetDBField('ValueList'));
foreach ($options as $key_val) {
list($key, $val) = explode('=', $key_val);
$block_params['key'] = $key;
$block_params['option'] = $val;
$block_params['selected'] = ($key == $value ? ' ' . $params['selected'] : '');
$block_params['PrefixSpecial'] = $this->getPrefixSpecial();
$o .= $this->Application->ParseBlock($block_params, 1);
}
return $o;
}
}
\ No newline at end of file
Index: branches/5.2.x/units/product_options/product_options_tag_processor.php
===================================================================
--- branches/5.2.x/units/product_options/product_options_tag_processor.php (revision 15599)
+++ branches/5.2.x/units/product_options/product_options_tag_processor.php (revision 15600)
@@ -1,175 +1,175 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class ProductOptionsTagProcessor extends kDBTagProcessor {
function ShowOptions($params)
{
$object = $this->getObject($params);
/* @var $object kDBItem */
$opt_helper = $this->Application->recallObject('kProductOptionsHelper');
/* @var $opt_helper kProductOptionsHelper */
$parsed = $opt_helper->ExplodeOptionValues($object->GetFieldValues());
if ( !$parsed ) {
return '';
}
$values = $parsed['Values'];
$conv_prices = $parsed['Prices'];
$conv_price_types = $parsed['PriceTypes'];
$options =& $this->GetOptions();
$mode = $this->SelectParam($params, 'mode');
$combination_prefix = $this->SelectParam($params, 'combination_prefix');
$combination_field = $this->SelectParam($params, 'combination_field');
if ( $mode == 'selected' ) {
$comb = $this->Application->recallObject($combination_prefix);
/* @var $comb kDBItem */
$options = unserialize($comb->GetDBField($combination_field));
}
$block_params['name'] = $params['render_as'];
$block_params['selected'] = '';
$block_params['pass_params'] = 1;
$lang = $this->Application->recallObject('lang.current');
/* @var $lang LanguagesItem */
$o = '';
$first_selected = false;
foreach ($values as $option) {
// list($val, $label) = explode('|', $option);
$val = $option;
if ( getArrayValue($params, 'js') ) {
$block_params['id'] = addslashes($val);
- $block_params['value'] = htmlspecialchars($val);
+ $block_params['value'] = htmlspecialchars($val, null, CHARSET);
}
else {
- $block_params['id'] = htmlspecialchars($val);
- $block_params['value'] = htmlspecialchars($val);
+ $block_params['id'] = htmlspecialchars($val, null, CHARSET);
+ $block_params['value'] = htmlspecialchars($val, null, CHARSET);
}
if ( $conv_prices[$val] ) {
if ( $conv_price_types[$val] == '$' && !getArrayValue($params, 'js') && !getArrayValue($params, 'no_currency') ) {
$iso = $this->GetISO($params['currency']);
$value = sprintf("%.2f", $this->ConvertCurrency($conv_prices[$val], $iso));
$value = $this->AddCurrencySymbol($lang->formatNumber($value, 2), $iso, true); // true to force sign
$block_params['price'] = $value;
$block_params['price_type'] = '';
$block_params['sign'] = ''; //sign is included in the formatted value
}
else {
$block_params['price'] = isset($params['js']) ? $conv_prices[$val] : $lang->formatNumber($conv_prices[$val], 2);
$block_params['price_type'] = $conv_price_types[$val];
$block_params['sign'] = $conv_prices[$val] >= 0 ? '+' : '-';
}
}
else {
$block_params['price'] = '';
$block_params['price_type'] = '';
$block_params['sign'] = '';
}
/*if ($mode == 'selected') {
$selected = $combination[$object->GetID()] == $val;
}
else*/
$selected = false;
if ( !$options && isset($params['preselect_first']) && $params['preselect_first'] && !$first_selected ) {
$selected = true;
$first_selected = true;
}
if ( is_array($options) ) {
$option_value = array_key_exists($object->GetID(), $options) ? $options[$object->GetID()] : '';
if ( $object->GetDBField('OptionType') == OptionType::CHECKBOX ) {
- $selected = is_array($option_value) && in_array(htmlspecialchars($val), $option_value);
+ $selected = is_array($option_value) && in_array(htmlspecialchars($val, null, CHARSET), $option_value);
}
else { // radio buttons ?
$selected = htmlspecialchars_decode($option_value) == $val;
}
}
if ( $selected ) {
if ( $mode == 'selected' ) {
if ( $object->GetDBField('OptionType') != OptionType::CHECKBOX ) {
$block_params['selected'] = ' selected="selected" ';
}
else {
$block_params['selected'] = ' checked="checked" ';
}
}
else {
switch ($object->GetDBField('OptionType')) {
case OptionType::DROPDOWN:
$block_params['selected'] = ' selected="selected" ';
break;
case OptionType::RADIO:
case OptionType::CHECKBOX:
$block_params['selected'] = ' checked="checked" ';
break;
}
}
}
else {
$block_params['selected'] = '';
}
$o .= $this->Application->ParseBlock($block_params);
}
return $o;
}
function &GetOptions()
{
$opt_data = $this->Application->GetVar('options');
$options = getArrayValue($opt_data, $this->Application->GetVar('p_id'));
if (!$options && $this->Application->GetVar('orditems_id')) {
$ord_item = $this->Application->recallObject('orditems.-opt', null, Array ('skip_autoload' => true));
/* @var $ord_item kDBItem */
$ord_item->Load($this->Application->GetVar('orditems_id'));
$item_data = unserialize($ord_item->GetDBField('ItemData'));
$options = getArrayValue($item_data, 'Options');
}
return $options;
}
function OptionData($params)
{
$object = $this->getObject($params);
/* @var $object kDBItem */
$options =& $this->GetOptions();
return getArrayValue($options, $object->GetID());
}
function ListOptions($params)
{
return $this->PrintList2($params);
}
}
\ No newline at end of file
Index: branches/5.2.x/units/order_items/order_items_tag_processor.php
===================================================================
--- branches/5.2.x/units/order_items/order_items_tag_processor.php (revision 15599)
+++ branches/5.2.x/units/order_items/order_items_tag_processor.php (revision 15600)
@@ -1,300 +1,300 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class OrderItemsTagProcessor extends kDBTagProcessor
{
function PrintGrid($params)
{
$order = $this->Application->recallObject('ord');
/* @var $order kDBList */
if ( $order->GetDBField('Status') != ORDER_STATUS_INCOMPLETE ) {
$params['grid'] = $params['NotEditable'];
}
else {
$params['grid'] = $params['Editable'];
}
return $this->Application->ProcessParsedTag('m', 'ParseBlock', $params);
}
function IsTangible($params)
{
$object = $this->getObject($params);
/* @var $object kDBItem */
return $object->GetDBField('Type') == PRODUCT_TYPE_TANGIBLE;
}
function HasQty($params)
{
$object = $this->getObject($params);
/* @var $object kDBItem */
return in_array($object->GetDBField('Type'), Array (PRODUCT_TYPE_TANGIBLE, 6));
}
function HasDiscount($params)
{
$object = $this->getObject($params);
/* @var $object kDBItem */
return (float)$object->GetDBField('ItemDiscount') ? 1 : 0;
}
function HasOptions($params)
{
$object = $this->getObject($params);
$item_data = @unserialize($object->GetDBField('ItemData'));
return isset($item_data['Options']);
}
function PrintOptions($params)
{
$object = $this->getObject($params);
/* @var $object kDBItem */
$item_data = @unserialize($object->GetDBField('ItemData'));
$render_as = $this->SelectParam($params, 'render_as');
$block_params['name'] = $render_as;
$opt_helper = $this->Application->recallObject('kProductOptionsHelper');
/* @var $opt_helper kProductOptionsHelper */
$o = '';
$options = $item_data['Options'];
foreach ($options as $opt => $val) {
if ( !is_array($val) ) {
$val = htmlspecialchars_decode($val);
}
$key_data = $opt_helper->ConvertKey($opt, $object->GetDBField('ProductId'));
$parsed = $opt_helper->ExplodeOptionValues($key_data);
if ( $parsed ) {
$values = $parsed['Values'];
$prices = $parsed['Prices'];
$price_types = $parsed['PriceTypes'];
}
else {
$values = array ();
$prices = array ();
$price_types = array ();
}
$key = $key_data['Name'];
/*if (is_array($val)) {
$val = join(',', $val);
}*/
$lang = $this->Application->recallObject('lang.current');
/* @var $lang LanguagesItem */
if ( $render_as ) {
$block_params['option'] = $key;
if ( is_array($val) ) {
$block_params['value'] = $val;
$block_params['type'] = $key_data['OptionType'];
$block_params['price'] = $prices;
$block_params['price_type'] = $price_types;
}
else {
$price_type = array_key_exists($val, $price_types) ? $price_types[$val] : '';
$price = array_key_exists($val, $prices) ? $prices[$val] : '';
if ( $price_type == '$' ) {
$iso = $this->GetISO($params['currency']);
$value = $this->AddCurrencySymbol($lang->formatNumber($this->ConvertCurrency($price, $iso), 2), $iso, true); // true to force sign
$block_params['price'] = $value;
$block_params['price_type'] = '';
$block_params['sign'] = ''; // sign is included in the formatted value
}
else {
$block_params['price'] = $price;
$block_params['price_type'] = $price_type;
$block_params['sign'] = $price >= 0 ? '+' : '-';
}
- $block_params['value'] = htmlspecialchars($val);
+ $block_params['value'] = htmlspecialchars($val, null, CHARSET);
$block_params['type'] = $key_data['OptionType'];
}
$o .= $this->Application->ParseBlock($block_params, 1);
}
else {
$o .= $key . ': ' . $val . '<br>';
}
}
return $o;
}
function ProductsInStock($params)
{
$object = $this->getObject($params);
if (!$object->GetDBField('InventoryStatus')) {
// unlimited count available
return false;
}
if ($object->GetDBField('InventoryStatus') == 2) {
$poc_table = $this->Application->getUnitOption('poc', 'TableName');
$sql = 'SELECT QtyInStock
FROM '.$poc_table.'
WHERE (ProductId = '.$object->GetDBField('ProductId').') AND (Availability = 1) AND (CombinationCRC = '.$object->GetDBField('OptionsSalt').')';
$ret = $this->Conn->GetOne($sql);
}
else {
$ret = $object->GetDBField('QtyInStock');
}
return $ret;
}
function PrintOptionValues($params)
{
$block_params['name'] = $params['render_as'];
$values = $this->Application->Parser->GetParam('value');
/* @var $values Array */
$prices = $this->Application->Parser->GetParam('price');
$price_types = $this->Application->Parser->GetParam('price_type');
$o = '';
$i = 0;
foreach ($values as $val) {
$i++;
$val = htmlspecialchars_decode($val);
- $block_params['value'] = htmlspecialchars($val);
+ $block_params['value'] = htmlspecialchars($val, null, CHARSET);
if ($price_types[$val] == '$') {
$iso = $this->GetISO($params['currency']);
$value = $this->AddCurrencySymbol(sprintf("%.2f", $this->ConvertCurrency($prices[$val], $iso)), $iso, true); // true to force sign
$block_params['price'] = $value;
$block_params['price_type'] = '';
$block_params['sign'] = ''; // sign is included in the formatted value
}
else {
$block_params['price'] = $prices[$val];
$block_params['price_type'] = $price_types[$val];
$block_params['sign'] = $prices[$val] >= 0 ? '+' : '-';
}
$block_params['is_last'] = $i == count($values);
$o.= $this->Application->ParseBlock($block_params, 1);
}
return $o;
}
/*function ConvertKey($key, &$object)
{
static $mapping = null;
if (is_null($mapping) || !isset($mapping[$object->GetDBField('ProductId')])) {
$table = TABLE_PREFIX.'ProductOptions';
$sql = 'SELECT * FROM '.$table.' WHERE ProductId = '.$object->GetDBField('ProductId');
$mapping[$object->GetDBField('ProductId')] = $this->Conn->Query($sql, 'ProductOptionId');
}
return $mapping[$object->GetDBField('ProductId')][$key];
}*/
function PrintList($params)
{
$list =& $this->GetList($params);
$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
$list->Query();
$o = '';
$list->GoFirst();
$block_params = $this->prepareTagParams($params);
$block_params['name'] = $this->SelectParam($params, 'render_as,block');
$block_params['pass_params'] = 'true';
$product_object = $this->Application->recallObject('p', 'p', Array ('skip_autoload' => true));
/* @var $product_object kCatDBItem */
$i = 0;
$product_id = $product_object->GetID();
$product_id_get = $this->Application->GetVar('p_id');
while (!$list->EOL()) {
// load product used in orderitem
$this->Application->SetVar($this->getPrefixSpecial() . '_id', $list->GetDBField($id_field)); // for edit/delete links using GET
$this->Application->SetVar('p_id', $list->GetDBField('ProductId'));
$product_object->Load($list->GetDBField('ProductId')); // correct product load
$this->Application->SetVar('m_cat_id', $product_object->GetDBField('CategoryId'));
$block_params['is_last'] = ($i == $list->GetSelectedCount() - 1);
$o .= $this->Application->ParseBlock($block_params, 1);
$list->GoNext();
$i++;
}
// restore IDs used in cycle
$this->Application->SetVar('p_id', $product_id_get);
$this->Application->DeleteVar($this->getPrefixSpecial() . '_id');
if ( $product_id ) {
$product_object->Load($product_id);
}
return $o;
}
function DisplayOptionsPricing($params)
{
$object = $this->getObject($params);
/* @var $object kDBItem */
if ( $object->GetDBField('OptionsSelectionMode') == 1 ) {
return false;
}
$item_data = unserialize($object->GetDBField('ItemData'));
if ( !is_array($item_data) ) {
return false;
}
$options = getArrayValue($item_data, 'Options');
$helper = $this->Application->recallObject('kProductOptionsHelper');
/* @var $helper kProductOptionsHelper */
$crc = $helper->OptionsSalt($options, true);
$sql = 'SELECT COUNT(*)
FROM ' . TABLE_PREFIX . 'ProductOptionCombinations
WHERE CombinationCRC = ' . $crc . ' AND ProductId = ' . $object->GetDBField('ProductId') . ' AND (Price != 0 OR (PriceType = 1 AND Price = 0))';
return $this->Conn->GetOne($sql) == 0; // no overriding combinations found
}
function RowIndex($params)
{
$object = $this->getObject($params);
/* @var $object kDBItem */
return $object->GetDBField('ProductId') . ':' . $object->GetDBField('OptionsSalt') . ':' . $object->GetDBField('BackOrderFlag');
}
function FreePromoShippingAvailable($params)
{
$object = $this->getObject($params);
/* @var $object kDBItem */
$order_helper = $this->Application->recallObject('OrderHelper');
/* @var $order_helper OrderHelper */
return $order_helper->eligibleForFreePromoShipping($object);
}
}
\ No newline at end of file

Event Timeline