Merge pull request #7609 from colemanw/editPerm
[civicrm-core.git] / CRM / Core / Payment / GoogleIPN.php
CommitLineData
6a488035
TO
1<?php
2
3/**
4 * Copyright (C) 2006 Google Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
0b014509 19/**
20 * Response handler code.
21 *
22 * This will be invoked every time a notification or request is sent by the Google Server.
3ecad08a
KJ
23 *
24 * To allow this code to receive responses, the url for this file
25 * must be set on the seller page under Settings->Integration as the
26 * "API Callback URL'
27 * Order processing commands can be sent automatically by placing these
28 * commands appropriately
29 *
30 * To use this code for merchant-calculated feedback, this url must be
31 * set also as the merchant-calculations-url when the cart is posted
32 * Depending on your calculations for shipping, taxes, coupons and gift
33 * certificates update parts of the code as required.
34 */
6a488035 35
6a488035 36define('GOOGLE_DEBUG_PP', 0);
28518c90
EM
37
38/**
0b014509 39 * Class CRM_Core_Payment_GoogleIPN.
28518c90 40 */
6a488035
TO
41class CRM_Core_Payment_GoogleIPN extends CRM_Core_Payment_BaseIPN {
42
43 /**
44 * We only need one instance of this object. So we use the singleton
45 * pattern and cache the instance in this variable
46 *
47 * @var object
6a488035
TO
48 */
49 static private $_singleton = NULL;
50
51 /**
100fef9d 52 * Mode of operation: live or test
6a488035
TO
53 *
54 * @var object
55 */
56 protected $_mode = NULL;
57
6c786a9b 58 /**
100fef9d 59 * @param string $name
6c786a9b
EM
60 * @param $type
61 * @param $object
62 * @param bool $abort
63 *
64 * @return mixed
65 */
00be9182 66 public static function retrieve($name, $type, $object, $abort = TRUE) {
6a488035
TO
67 $value = CRM_Utils_Array::value($name, $object);
68 if ($abort && $value === NULL) {
69 CRM_Core_Error::debug_log_message("Could not find an entry for $name");
70 echo "Failure: Missing Parameter<p>";
71 exit();
72 }
73
74 if ($value) {
75 if (!CRM_Utils_Type::validate($value, $type)) {
76 CRM_Core_Error::debug_log_message("Could not find a valid entry for $name");
77 echo "Failure: Invalid Parameter<p>";
78 exit();
79 }
80 }
81
82 return $value;
83 }
84
85 /**
fe482240 86 * Constructor.
6a488035 87 *
6a0b768e
TO
88 * @param string $mode
89 * The mode of operation: live or test.
6a488035 90 *
77b97be7
EM
91 * @param $paymentProcessor
92 *
93 * @return \CRM_Core_Payment_GoogleIPN
6a488035 94 */
00be9182 95 public function __construct($mode, &$paymentProcessor) {
6a488035
TO
96 parent::__construct();
97
98 $this->_mode = $mode;
99 $this->_paymentProcessor = $paymentProcessor;
100 }
101
102 /**
103 * The function gets called when a new order takes place.
104 *
6a0b768e
TO
105 * @param xml $dataRoot
106 * Response send by google in xml format.
107 * @param array $privateData
108 * Contains the name value pair of <merchant-private-data>.
6a488035 109 *
0b014509 110 * @param string $component
6a488035 111 *
0b014509 112 * @return bool
6a488035 113 */
00be9182 114 public function newOrderNotify($dataRoot, $privateData, $component) {
6a488035
TO
115 $ids = $input = $params = array();
116
117 $input['component'] = strtolower($component);
118
119 $ids['contact'] = self::retrieve('contactID', 'Integer', $privateData, TRUE);
120 $ids['contribution'] = self::retrieve('contributionID', 'Integer', $privateData, TRUE);
121
122 $ids['contributionRecur'] = $ids['contributionPage'] = NULL;
123 if ($input['component'] == "event") {
353ffa53 124 $ids['event'] = self::retrieve('eventID', 'Integer', $privateData, TRUE);
6a488035 125 $ids['participant'] = self::retrieve('participantID', 'Integer', $privateData, TRUE);
353ffa53 126 $ids['membership'] = NULL;
6a488035
TO
127 }
128 else {
129 $ids['membership'] = self::retrieve('membershipID', 'Integer', $privateData, FALSE);
130 $ids['related_contact'] = self::retrieve('relatedContactID', 'Integer', $privateData, FALSE);
131 $ids['onbehalf_dupe_alert'] = self::retrieve('onBehalfDupeAlert', 'Integer', $privateData, FALSE);
132 $ids['contributionRecur'] = self::retrieve('contributionRecurID', 'Integer', $privateData, FALSE);
133 }
134
135 $paymentProcessorID = CRM_Core_DAO::getFieldValue(
136 'CRM_Financial_DAO_PaymentProcessorType',
137 'Google_Checkout',
138 'id',
139 'payment_processor_type'
140 );
141
142 if (!$this->validateData($input, $ids, $objects, TRUE, $paymentProcessorID)) {
143 return FALSE;
144 }
145
146 $input['invoice'] = $privateData['invoiceID'];
147 $input['newInvoice'] = $dataRoot['google-order-number']['VALUE'];
148
149 if ($ids['contributionRecur']) {
150 if ($objects['contributionRecur']->invoice_id == $dataRoot['serial-number']) {
151 CRM_Core_Error::debug_log_message("The new order notification already handled: {$dataRoot['serial-number']}.");
0b014509 152 return FALSE;
6a488035
TO
153 }
154 else {
155 $transaction = new CRM_Core_Transaction();
156
157 CRM_Core_Error::debug_log_message("New order for an installment received.");
158 $recur = &$objects['contributionRecur'];
159
160 // fix dates that already exist
161 $dates = array('create', 'start', 'end', 'cancel', 'modified');
162 foreach ($dates as $date) {
163 $name = "{$date}_date";
164 if ($recur->$name) {
165 $recur->$name = CRM_Utils_Date::isoToMysql($recur->$name);
166 }
167 }
168 $recur->invoice_id = $dataRoot['serial-number'];
169 $recur->processor_id = $input['newInvoice'];
170 $recur->save();
171
172 if ($objects['contribution']->contribution_status_id == 1) {
173 // create a contribution and then get it processed
174 $contribution = new CRM_Contribute_DAO_Contribution();
175 $contribution->contact_id = $ids['contact'];
353ffa53 176 $contribution->financial_type_id = $objects['contributionType']->id;
6a488035
TO
177 $contribution->contribution_page_id = $objects['contribution']->contribution_page_id;
178 $contribution->contribution_recur_id = $ids['contributionRecur'];
179 $contribution->receive_date = date('YmdHis');
180 $contribution->currency = $objects['contribution']->currency;
181 $contribution->payment_instrument_id = $objects['contribution']->payment_instrument_id;
182 $contribution->amount_level = $objects['contribution']->amount_level;
183 $contribution->address_id = $objects['contribution']->address_id;
184 $contribution->invoice_id = $input['invoice'];
185 $contribution->total_amount = $dataRoot['order-total']['VALUE'];
186 $contribution->contribution_status_id = 2;
94d1bc8d
PJ
187 $contribution->campaign_id = $objects['contribution']->campaign_id;
188
6a488035
TO
189 $objects['contribution'] = $contribution;
190 }
191 $transaction->commit();
192 }
193 }
194
195 // make sure the invoice is valid and matches what we have in the contribution record
196 $contribution = &$objects['contribution'];
197
198 if ($contribution->invoice_id != $input['invoice']) {
0b014509 199 CRM_Core_Error::debug_log_message("Invoice values don't match between database and IPN request");
200 return FALSE;
6a488035
TO
201 }
202
203 // lets replace invoice-id with google-order-number because thats what is common and unique
204 // in subsequent calls or notifications sent by google.
205 $contribution->invoice_id = $input['newInvoice'];
206
207 $input['amount'] = $dataRoot['order-total']['VALUE'];
208
209 if ($contribution->total_amount != $input['amount']) {
210 CRM_Core_Error::debug_log_message("Amount values dont match between database and IPN request");
0b014509 211 return FALSE;
6a488035
TO
212 }
213
214 if (!$this->getInput($input, $ids, $dataRoot)) {
215 return FALSE;
216 }
217
218 $transaction = new CRM_Core_Transaction();
219
220 // check if contribution is already completed, if so we ignore this ipn
221 if ($contribution->contribution_status_id == 1) {
222 CRM_Core_Error::debug_log_message("returning since contribution has already been handled");
0b014509 223 return FALSE;
6a488035
TO
224 }
225 else {
226 /* Since trxn_id hasn't got any use here,
e70a7fc0
TO
227 * lets make use of it by passing the eventID/membershipTypeID to next level.
228 * And change trxn_id to google-order-number before finishing db update */
6a488035 229
a7488080 230 if (!empty($ids['event'])) {
6a488035
TO
231 $contribution->trxn_id = $ids['event'] . CRM_Core_DAO::VALUE_SEPARATOR . $ids['participant'];
232 }
a7488080 233 elseif (!empty($ids['membership'])) {
6a488035
TO
234 $contribution->trxn_id = $ids['membership'][0] . CRM_Core_DAO::VALUE_SEPARATOR . $ids['related_contact'] . CRM_Core_DAO::VALUE_SEPARATOR . $ids['onbehalf_dupe_alert'];
235 }
236 }
237
6a488035
TO
238 $contribution->save();
239 $transaction->commit();
240
241 return TRUE;
242 }
243
244 /**
245 * The function gets called when the state(CHARGED, CANCELLED..) changes for an order
246 *
6a0b768e
TO
247 * @param string $status
248 * Status of the transaction send by google.
fd31fa4c 249 * @param $dataRoot
6a0b768e
TO
250 * @param array $privateData
251 * Contains the name value pair of <merchant-private-data>.
6a488035 252 *
0b014509 253 * @param string $component
6a488035 254 *
0b014509 255 * @return bool
6a488035 256 */
00be9182 257 public function orderStateChange($status, $dataRoot, $privateData, $component) {
6a488035
TO
258 $input = $objects = $ids = array();
259 $input['component'] = strtolower($component);
260
261 $ids['contributionRecur'] = self::retrieve('contributionRecurID', 'Integer', $privateData, FALSE);
262 $serial = $dataRoot['serial-number'];
263 $orderNo = $dataRoot['google-order-number']['VALUE'];
264
265 $contribution = new CRM_Contribute_BAO_Contribution();
266 $contribution->invoice_id = $orderNo;
267
268 if (!$contribution->find(TRUE)) {
269 CRM_Core_Error::debug_log_message("orderStateChange: Could not find contribution record with invoice id: $serial");
0b014509 270 return FALSE;
6a488035
TO
271 }
272
273 // Google sends the charged notification twice.
274 // So to make sure, code is not executed again.
275 if ($contribution->contribution_status_id == 1) {
276 CRM_Core_Error::debug_log_message("Contribution already handled (ContributionID = {$contribution->id}).");
0b014509 277 return FALSE;
6a488035
TO
278 }
279
280 // make sure invoice is set to serial no for recurring payments, to avoid violating uniqueness
281 $contribution->invoice_id = $ids['contributionRecur'] ? $serial : $orderNo;
282
283 $objects['contribution'] = &$contribution;
284 $ids['contribution'] = $contribution->id;
285 $ids['contact'] = $contribution->contact_id;
286
287 $ids['event'] = $ids['participant'] = $ids['membership'] = NULL;
288 $ids['contributionPage'] = NULL;
289
290 if ($input['component'] == "event") {
291 list($ids['event'], $ids['participant']) = explode(CRM_Core_DAO::VALUE_SEPARATOR,
292 $contribution->trxn_id
293 );
294 }
295 else {
296 $ids['related_contact'] = NULL;
297 $ids['onbehalf_dupe_alert'] = NULL;
298 if ($contribution->trxn_id) {
2aa397bc 299 list($ids['membership'], $ids['related_contact'], $ids['onbehalf_dupe_alert']) = explode(CRM_Core_DAO::VALUE_SEPARATOR,
353ffa53 300 $contribution->trxn_id
2aa397bc 301 );
6a488035
TO
302 }
303 foreach (array(
353ffa53
TO
304 'membership',
305 'related_contact',
3bdca100 306 'onbehalf_dupe_alert',
353ffa53 307 ) as $fld) {
6a488035
TO
308 if (!is_numeric($ids[$fld])) {
309 unset($ids[$fld]);
310 }
311 }
312 }
313
314 $paymentProcessorID = CRM_Core_DAO::getFieldValue(
315 'CRM_Financial_DAO_PaymentProcessorType',
316 'Google_Checkout',
317 'id',
318 'payment_processor_type'
319 );
320
321 $this->loadObjects($input, $ids, $objects, TRUE, $paymentProcessorID);
322
323 $transaction = new CRM_Core_Transaction();
324
6a488035
TO
325 if ($status == 'PAYMENT_DECLINED' ||
326 $status == 'CANCELLED_BY_GOOGLE' ||
327 $status == 'CANCELLED'
328 ) {
329 return $this->failed($objects, $transaction);
330 }
331
353ffa53 332 $input['amount'] = $contribution->total_amount;
6a488035
TO
333 $input['fee_amount'] = NULL;
334 $input['net_amount'] = NULL;
353ffa53
TO
335 $input['trxn_id'] = $ids['contributionRecur'] ? $serial : $dataRoot['google-order-number']['VALUE'];
336 $input['is_test'] = $contribution->is_test;
6a488035
TO
337
338 $recur = NULL;
339 if ($ids['contributionRecur']) {
340 $recur = $objects['contributionRecur'];
341 }
342 $this->completeTransaction($input, $ids, $objects, $transaction, $recur);
343
344 $this->completeRecur($input, $ids, $objects);
345 }
346
6c786a9b
EM
347 /**
348 * @param $input
349 * @param $ids
350 * @param $objects
351 */
00be9182 352 public function completeRecur($input, $ids, $objects) {
6a488035 353 if ($ids['contributionRecur']) {
353ffa53
TO
354 $recur = &$objects['contributionRecur'];
355 $contributionCount = CRM_Core_DAO::singleValueQuery("
6a488035
TO
356SELECT count(*)
357FROM civicrm_contribution
358WHERE contribution_recur_id = {$ids['contributionRecur']}
359");
360 $autoRenewMembership = FALSE;
361 if ($recur->id &&
362 isset($ids['membership']) &&
363 $ids['membership']
364 ) {
365 $autoRenewMembership = TRUE;
366 }
367 if ($recur->installments && ($contributionCount >= $recur->installments)) {
368 $contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
369
370 $recur->create_date = CRM_Utils_Date::isoToMysql($recur->create_date);
371 $recur->start_date = CRM_Utils_Date::isoToMysql($recur->start_date);
372 $recur->cancel_date = CRM_Utils_Date::isoToMysql($recur->cancel_date);
373 $recur->end_date = date('YmdHis');
374 $recur->modified_date = date('YmdHis');
375 $recur->contribution_status_id = array_search('Completed', $contributionStatus);
376 $recur->trnx_id = $dataRoot['google-order-number']['VALUE'];
377 $recur->save();
378
379 //send recurring Notification email for user
380 CRM_Contribute_BAO_ContributionPage::recurringNotify(
381 CRM_Core_Payment::RECURRING_PAYMENT_END,
382 $ids['contact'],
383 $ids['contributionPage'],
384 $recur,
385 $autoRenewMembership
386 );
387 }
388 elseif ($contributionCount == 1) {
389 CRM_Contribute_BAO_ContributionPage::recurringNotify(
390 CRM_Core_Payment::RECURRING_PAYMENT_START,
391 $ids['contact'],
392 $ids['contributionPage'],
393 $recur,
394 $autoRenewMembership
395 );
396 }
397 }
398 }
399
400 /**
204c86d5
EM
401 * @deprecated
402 * Payment processor singletons removed - this is an IPN so left but probably can go
100fef9d 403 * Singleton function used to manage this object
6a488035 404 *
6a0b768e
TO
405 * @param string $mode
406 * The mode of operation: live or test.
6a488035 407 *
2a6da8d7
EM
408 * @param $component
409 * @param $paymentProcessor
410 *
6a488035 411 * @return object
6a488035 412 */
00be9182 413 public static function &singleton($mode, $component, &$paymentProcessor) {
6a488035
TO
414 if (self::$_singleton === NULL) {
415 self::$_singleton = new CRM_Core_Payment_GoogleIPN($mode, $paymentProcessor);
416 }
417 return self::$_singleton;
418 }
419
420 /**
421 * The function retrieves the amount the contribution is for, based on the order-no google sends
422 *
6a0b768e
TO
423 * @param int $orderNo
424 * <order-total> send by google.
6a488035
TO
425 *
426 * @return amount
6a488035 427 */
00be9182 428 public function getAmount($orderNo) {
6a488035
TO
429 $contribution = new CRM_Contribute_DAO_Contribution();
430 $contribution->invoice_id = $orderNo;
431 if (!$contribution->find(TRUE)) {
432 CRM_Core_Error::debug_log_message("getAmount: Could not find contribution record with invoice id: $orderNo");
433 echo "Failure: Could not find contribution record with invoice id: $orderNo <p>";
434 exit();
435 }
436 return $contribution->total_amount;
437 }
438
439 /**
440 * The function returns the component(Event/Contribute..), given the google-order-no and merchant-private-data
441 *
6a0b768e
TO
442 * @param array $privateData
443 * Contains the name value pair of <merchant-private-data>.
444 * @param int $orderNo
445 * <order-total> send by google.
446 * @param string $root
447 * Root of xml-response.
6a488035 448 *
2a6da8d7
EM
449 * @param $response
450 * @param $serial
451 * @internal param \xml $xml_response response send by google in xml format
a6c01b45
CW
452 * @return array
453 * context of this call (test, module, payment processor id)
6a488035 454 */
00be9182 455 public function getContext($privateData, $orderNo, $root, $response, $serial) {
6a488035
TO
456 $contributionID = CRM_Utils_Array::value('contributionID', $privateData);
457 $contribution = new CRM_Contribute_DAO_Contribution();
458 if ($root == 'new-order-notification') {
459 $contribution->id = $contributionID;
460 }
461 else {
462 $contribution->invoice_id = $orderNo;
463 }
464 if (!$contribution->find(TRUE)) {
465 CRM_Core_Error::debug_log_message("getContext: Could not find contribution record with invoice id: $orderNo");
466 $response->SendAck($serial);
467 }
468
469 $module = 'Contribute';
470 if (stristr($contribution->source, ts('Online Contribution'))) {
471 $module = 'Contribute';
472 }
473 elseif (stristr($contribution->source, ts('Online Event Registration'))) {
474 $module = 'Event';
475 }
476 $isTest = $contribution->is_test;
477
478 $ids = $input = $objects = array();
479 $objects['contribution'] = &$contribution;
480 $ids['contributionRecur'] = self::retrieve('contributionRecurID', 'Integer', $privateData, FALSE);
481 $input['component'] = strtolower($module);
482
483 if (!$ids['contributionRecur'] && $contribution->contribution_status_id == 1) {
484 CRM_Core_Error::debug_log_message("Contribution already handled (ContributionID = {$contribution->id}).");
485 // There is no point in going further. Return ack so we don't receive the same ipn.
486 $response->SendAck($serial);
487 }
488
489 if ($input['component'] == 'event') {
490 if ($root == 'new-order-notification') {
491 $ids['event'] = $privateData['eventID'];
492 }
493 else {
3bdca100 494 list($ids['event'], $ids['participant']) = explode(CRM_Core_DAO::VALUE_SEPARATOR, $contribution->trxn_id);
6a488035
TO
495 }
496 }
497
498 $paymentProcessorID = CRM_Core_DAO::getFieldValue(
499 'CRM_Financial_DAO_PaymentProcessor',
500 'Google_Checkout',
501 'id',
502 'payment_processor_type'
503 );
504
505 $this->loadObjects($input, $ids, $objects, FALSE, $paymentProcessorID);
506
507 if (!$ids['paymentProcessor']) {
508 CRM_Core_Error::debug_log_message("Payment processor could not be retrieved.");
509 // There is no point in going further. Return ack so we don't receive the same ipn.
510 $response->SendAck($serial);
511 }
512
513 return array($isTest, $input['component'], $ids['paymentProcessor']);
514 }
515
516 /**
517 * This method is handles the response that will be invoked (from extern/googleNotify) every time
518 * a notification or request is sent by the Google Server.
67f947ac
EM
519 *
520 * @param string $xml_response
6a488035 521 */
00be9182 522 public static function main($xml_response) {
6a488035
TO
523 require_once 'Google/library/googleresponse.php';
524 require_once 'Google/library/googlerequest.php';
525 require_once 'Google/library/googlemerchantcalculations.php';
526 require_once 'Google/library/googleresult.php';
527 require_once 'Google/library/xml-processing/gc_xmlparser.php';
528
529 $config = CRM_Core_Config::singleton();
530
531 // Retrieve the XML sent in the HTTP POST request to the ResponseHandler
532 if (get_magic_quotes_gpc()) {
533 $xml_response = stripslashes($xml_response);
534 }
535
536 $headers = CRM_Utils_System::getAllHeaders();
537
538 if (GOOGLE_DEBUG_PP) {
539 CRM_Core_Error::debug_var('RESPONSE', $xml_response, TRUE, TRUE, 'Google');
540 }
541
542 // Retrieve the root and data from the xml response
543 $response = new GoogleResponse();
544 list($root, $data) = $response->GetParsedXML($xml_response);
545 // lets retrieve the private-data & order-no
546 $privateData = NULL;
547 if (array_key_exists('shopping-cart', $data[$root])) {
2aa397bc 548 $privateData = $data[$root]['shopping-cart']['merchant-private-data']['VALUE'];
6a488035
TO
549 }
550 if (empty($privateData) && array_key_exists('order-summary', $data[$root])
353ffa53
TO
551 && array_key_exists('shopping-cart', $data[$root]['order-summary'])
552 ) {
6a488035
TO
553 $privateData = $data[$root]['order-summary']['shopping-cart']['merchant-private-data']['VALUE'];
554 }
555 $privateData = $privateData ? self::stringToArray($privateData) : '';
353ffa53
TO
556 $orderNo = $data[$root]['google-order-number']['VALUE'];
557 $serial = $data[$root]['serial-number'];
6a488035
TO
558
559 // a dummy object to call get context and a parent function inside it.
560 $ipn = new CRM_Core_Payment_GoogleIPN('live', $dummyProcessor);
561 list($mode, $module, $paymentProcessorID) = $ipn->getContext($privateData, $orderNo, $root, $response, $serial);
562 $mode = $mode ? 'test' : 'live';
563
564 $paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($paymentProcessorID, $mode);
565 $merchant_id = $paymentProcessor['user_name'];
566 $merchant_key = $paymentProcessor['password'];
567 $response->SetMerchantAuthentication($merchant_id, $merchant_key);
568
569 $server_type = ($mode == 'test') ? 'sandbox' : 'production';
570 $request = new GoogleRequest($merchant_id, $merchant_key, $server_type);
571
572 $ipn = self::singleton($mode, $module, $paymentProcessor);
573
574 if (GOOGLE_DEBUG_PP) {
575 CRM_Core_Error::debug_var('RESPONSE-ROOT', $response->root, TRUE, TRUE, 'Google');
576 }
577
578 //Check status and take appropriate action
579 $status = $response->HttpAuthentication($headers);
580
581 switch ($root) {
582 case "request-received":
583 case "error":
584 case "diagnosis":
585 case "checkout-redirect":
586 case "merchant-calculation-callback":
587 break;
588
3bdca100 589 case "new-order-notification":
2aa397bc
TO
590 $response->SendAck($serial, FALSE);
591 $ipn->newOrderNotify($data[$root], $privateData, $module);
592 break;
6a488035 593
3bdca100 594 case "order-state-change-notification":
2aa397bc
TO
595 $response->SendAck($serial, FALSE);
596 $new_financial_state = $data[$root]['new-financial-order-state']['VALUE'];
597 $new_fulfillment_order = $data[$root]['new-fulfillment-order-state']['VALUE'];
6a488035 598
2aa397bc
TO
599 switch ($new_financial_state) {
600 case 'CHARGEABLE':
601 break;
6a488035 602
2aa397bc
TO
603 case 'CHARGED':
604 case 'PAYMENT_DECLINED':
605 case 'CANCELLED':
606 case 'CANCELLED_BY_GOOGLE':
607 $ipn->orderStateChange($new_financial_state, $data[$root], $privateData, $module);
608 break;
6a488035 609
2aa397bc
TO
610 case 'REVIEWING':
611 case 'CHARGING':
612 break;
6a488035 613
2aa397bc
TO
614 default:
615 break;
616 }
617 break;
6a488035 618
3bdca100 619 case "authorization-amount-notification":
2aa397bc
TO
620 $response->SendAck($serial, FALSE);
621 $new_financial_state = $data[$root]['order-summary']['financial-order-state']['VALUE'];
622 $new_fulfillment_order = $data[$root]['order-summary']['fulfillment-order-state']['VALUE'];
623
624 switch ($new_financial_state) {
625 case 'CHARGEABLE':
626 // For google-handled subscriptions chargeorder needn't be initiated,
627 // assuming auto-charging is turned on.
628 //$request->SendProcessOrder($data[$root]['google-order-number']['VALUE']);
629 //$request->SendChargeOrder($data[$root]['google-order-number']['VALUE'],'');
630 break;
631
632 case 'CHARGED':
633 case 'PAYMENT_DECLINED':
634 case 'CANCELLED':
635 break;
636
637 case 'REVIEWING':
638 case 'CHARGING':
639 case 'CANCELLED_BY_GOOGLE':
640 break;
641
642 default:
643 break;
644 }
645 break;
6a488035
TO
646
647 case "charge-amount-notification":
648 case "chargeback-amount-notification":
649 case "refund-amount-notification":
650 case "risk-information-notification":
651 $response->SendAck($serial);
652 break;
653
654 default:
655 break;
656 }
657 }
658
6c786a9b
EM
659 /**
660 * @param $input
661 * @param $ids
662 * @param $dataRoot
663 *
664 * @return bool
665 */
00be9182 666 public function getInput(&$input, &$ids, $dataRoot) {
6a488035
TO
667 if (!$this->getBillingID($ids)) {
668 return FALSE;
669 }
670
671 $billingID = $ids['billing'];
672 $lookup = array(
673 "first_name" => 'contact-name',
674 // "last-name" not available with google (every thing in contact-name)
675 "last_name" => 'last_name',
676 "street_address-{$billingID}" => 'address1',
677 "city-{$billingID}" => 'city',
678 "state-{$billingID}" => 'region',
679 "postal_code-{$billingID}" => 'postal-code',
680 "country-{$billingID}" => 'country-code',
681 );
682
683 foreach ($lookup as $name => $googleName) {
684 if (array_key_exists($googleName, $dataRoot['buyer-billing-address'])) {
2aa397bc 685 $value = $dataRoot['buyer-billing-address'][$googleName]['VALUE'];
6a488035
TO
686 }
687 $input[$name] = $value ? $value : NULL;
688 }
689 return TRUE;
690 }
691
692 /**
693 * Converts the comma separated name-value pairs in <merchant-private-data>
694 * to an array of name-value pairs.
ea3ddccf 695 *
696 * @param string $str
697 *
698 * @return array
6a488035 699 */
00be9182 700 public static function stringToArray($str) {
6a488035
TO
701 $vars = $labels = array();
702 $labels = explode(',', $str);
703 foreach ($labels as $label) {
704 $terms = explode('=', $label);
705 $vars[$terms[0]] = $terms[1];
706 }
707 return $vars;
708 }
96025800 709
6a488035 710}