Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F1102885
google_checkout.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Aug 19, 11:14 AM
Size
29 KB
Mime Type
text/x-php
Expires
Thu, Aug 21, 11:14 AM (1 d, 3 h)
Engine
blob
Format
Raw Data
Handle
714197
Attached To
rMINC Modules.In-Commerce
google_checkout.php
View Options
<?php
/**
* @version $Id: google_checkout.php 16522 2017-01-20 20:28:16Z alex $
* @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
);
/** @var kMultiLanguage $ml_formatter */
$ml_formatter
=
$this
->
Application
->
recallObject
(
'kMultiLanguage'
);
$cart_xml
=
Array
();
foreach
(
$order_items
as
$order_item
)
{
$cart_xml
[]
=
' <item>
<item-name>'
.
kUtil
::
escape
(
$order_item
[
'ProductName'
],
kUtil
::
ESCAPE_HTML
).
'</item-name>
<item-description>'
.
kUtil
::
escape
(
$order_item
[
$ml_formatter
->
LangFieldName
(
'DescriptionExcerpt'
)],
kUtil
::
ESCAPE_HTML
).
'</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="'
.
kUtil
::
escape
(
$shipping_name
,
kUtil
::
ESCAPE_HTML
).
'">
<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
=
file_get_contents
(
'php://input'
);
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
;
/** @var kXMLHelper $xml_helper */
$xml_helper
=
$this
->
Application
->
recallObject
(
'kXMLHelper'
);
/** @var kXMLNode $root_node */
$root_node
=&
$xml_helper
->
Parse
(
$this
->
getRequestXML
()
);
$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
,
'--- '
.
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
)
{
/** @var kXMLNode $found_node */
$found_node
=&
$root_node
;
$search_string
=
explode
(
':'
,
$search_string
);
foreach
(
$search_string
as
$search_node
)
{
$found_node
=&
$found_node
->
FindChild
(
$search_node
);
}
$node_data
=
Array
();
/** @var kXMLNode $sub_node */
$sub_node
=&
$found_node
->
firstChild
;
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
/** @var OrdersItem $order */
$order
=
$this
->
Application
->
recallObject
(
'ord'
,
null
,
Array
(
'skip_autoload'
=>
true
));
$order
->
Load
(
$order_id
);
$shipping_address
=
Array
(
'ShippingCity'
=>
$address_info
[
'CITY'
],
'ShippingState'
=>
$address_info
[
'REGION'
],
'ShippingZip'
=>
$address_info
[
'POSTAL-CODE'
],
);
/** @var kCountryStatesHelper $cs_helper */
$cs_helper
=
$this
->
Application
->
recallObject
(
'CountryStatesHelper'
);
$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="'
.
kUtil
::
escape
(
$shipping_name
,
kUtil
::
ESCAPE_HTML
).
'" 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="'
.
kUtil
::
escape
(
$shipping_name
,
kUtil
::
ESCAPE_HTML
).
'" 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
)
{
/** @var kXMLNode $found_node */
$found_node
=&
$root_node
;
$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
)
{
/** @var kXMLNode $sub_node */
$sub_node
=&
$found_node
->
firstChild
;
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
/** @var OrdersItem $order */
$order
=
$this
->
Application
->
recallObject
(
'ord'
,
null
,
Array
(
'skip_autoload'
=>
true
));
$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'
,
);
/** @var kCountryStatesHelper $cs_helper */
$cs_helper
=
$this
->
Application
->
recallObject
(
'CountryStatesHelper'
);
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
)
{
/** @var kXMLNode $found_node */
$found_node
=&
$root_node
;
$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
)
{
/** @var kXMLNode $sub_node */
$sub_node
=&
$found_node
->
firstChild
;
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
/** @var OrdersItem $order */
$order
=
$this
->
Application
->
recallObject
(
'ord'
,
null
,
Array
(
'skip_autoload'
=>
true
));
$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
);
/** @var OrdersItem $order */
$order
=
$this
->
Application
->
recallObject
(
'ord.-item'
,
null
,
Array
(
'skip_autoload'
=>
true
));
$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'
];
/** @var kCurlHelper $curl_helper */
$curl_helper
=
$this
->
Application
->
recallObject
(
'CurlHelper'
);
/** @var kXMLHelper $xml_helper */
$xml_helper
=
$this
->
Application
->
recallObject
(
'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
);
/** @var kXMLNode $root_node */
$root_node
=&
$xml_helper
->
Parse
(
$xml_responce
);
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
)
{
/** @var kXMLNode $found_node */
$found_node
=&
$root_node
;
$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
/** @var OrdersItem $order */
$order
=
$this
->
Application
->
recallObject
(
'ord'
,
null
,
Array
(
'skip_autoload'
=>
true
));
$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
/** @var OrdersEventHandler $order_eh */
$order_eh
=
$this
->
Application
->
recallObject
(
'ord_EventHandler'
);
$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'
);
/** @var ShippingQuoteCollector $quote_engine_collector */
$quote_engine_collector
=
$this
->
Application
->
recallObject
(
'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
)
{
}
}
Event Timeline
Log In to Comment