Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
paypal_ipn_receiver.inc
1 <?php
17 require_once SQ_CORE_PACKAGE_PATH.'/page/page.inc';
18 
19 define('LOG_FILE_PREFIX', 'paypal_transactions');
20 define('FAIL_LOG_FILE', LOG_FILE_PREFIX.'_failure');
21 
34 {
35 
42  function __construct($assetid=0)
43  {
44  parent::__construct($assetid);
45 
46  }//end constructor
47 
48 
56  public function _getAllowedLinks()
57  {
58  $allowed_links = parent::_getAllowedLinks();
59 
60  $allowed_links[SQ_LINK_NOTICE]['asset'] = Array('card' => 'M', 'exclusive' => FALSE);
61 
62  return $allowed_links;
63 
64  }//end _getAllowedLinks()
65 
66 
73  public function printFrontend()
74  {
75  $payment_info = Array();
76  $payment_info['request_server'] = "There is a {$_SERVER['REQUEST_METHOD']} Request from {$_SERVER['REMOTE_ADDR']}";
77 
78  //only process if the request method is POST
79  if ($_SERVER['REQUEST_METHOD'] == 'POST') {
80  $payment_info['txn_type'] = array_get_index($_POST, 'txn_type', '');
81  $payment_info['txn_id'] = array_get_index($_POST, 'txn_id', '');
82  $payment_info['test_ipn'] = array_get_index($_POST, 'test_ipn', '0');
83  $payment_info['payment_status'] = array_get_index($_POST, 'payment_status', '');
84  $payment_info['payment_date'] = array_get_index($_POST, 'payment_date', '');
85 
86  //do not proceed if there is no transaction type, transaction ID or payment_status
87  if (empty($payment_info['txn_type']) || empty($payment_info['txn_id']) || empty($payment_info['payment_status'])) {
88  $payment_info['error'] = 'THERE IS NO TRANSACTION TYPE, TRANSACTION ID OR PAYMENT STATUS.';
89  $this->_returnPageNotFound($payment_info);
90  }
91 
92  if ($payment_info['payment_status'] == 'Pending') {
93  $payment_info['incomplete_reason'] = array_get_index($_POST, 'pending_reason', '');
94  } else {
95  $payment_info['incomplete_reason'] = array_get_index($_POST, 'reason_code', '');
96  }
97 
98  $payment_info['custom'] = array_get_index($_POST, 'custom', '');
99  //if there is no custom parameter, it must not be a Paypal POST request (our payment button always sends custom variable)
100  if (empty($payment_info['custom'])) {
101  $payment_info['error'] = 'CUSTOM VARIABLE IS NOT SENT BACK FROM PAYPAL.';
102  $this->_returnPageNotFound($payment_info);
103  }
104 
105  $GLOBALS['SQ_SYSTEM']->am->includeAsset('paypal_payment_button');
106 
107  list($custom_hash, $button_id, $custom) = explode(Paypal_Payment_Button::$CUSTOM_PARAM_SPLIT_STR, $payment_info['custom']);
108 
109  $payment_info['button_id'] = $button_id;
110  if (empty($button_id)) {
111  $payment_info['error'] = 'BUTTON ID IS NOT SPECIFIED.';
112  $this->_returnPageNotFound($payment_info);
113  }
114 
115  $button = $GLOBALS['SQ_SYSTEM']->am->getAsset($button_id);
116 
117  $paypal_config_id = $button->attr('paypal_config_id');
118  if (empty($paypal_config_id)) {
119  $payment_info['error'] = "PAYPAL CONFIGURATION IS NOT SPECIFIED FOR THE BUTTON #$button_id.";
120  $this->_returnPageNotFound($payment_info);
121  }
122 
123  $paypal_config = $GLOBALS['SQ_SYSTEM']->am->getAsset($paypal_config_id);
124 
125  //check if there is any malicious changes to our custom parameter
126  $custom_var = $button_id.Paypal_Payment_Button::$CUSTOM_PARAM_SPLIT_STR.$custom;
127 
128  if (!empty($custom)) {
129  $custom = urldecode($custom);
130  }
131 
132  $notify_url_query = array_get_index($_SERVER, 'QUERY_STRING', '');
133 
134  $payment_info['real_custom_var'] = $custom;
135  $payment_info['notify_url_query'] = $notify_url_query;
136 
137  if ($custom_hash != md5($paypal_config->attr('custom_param_secret_str').$custom_var.$paypal_config->attr('custom_param_secret_str').$notify_url_query)) {
138  //someone tampered with the custom paramater
139  $payment_info['error'] = 'THE CHECKSUM OF CUSTOM VARIABLES AND NOTIFY URL QUERY STRING IS INVALID.';
140  $this->_returnPageNotFound($payment_info);
141  }
142 
143  $paypal_account_id = $button->attr('paypal_account_id');
144  if (empty($paypal_account_id)) {
145  $payment_info['error'] = "PAYPAL BUSINESS ACCOUNT IS NOT SPECIFIED FOR THE BUTTON #$button_id.";
146  $this->_returnPageNotFound($payment_info);
147  }
148 
149  $paypal_account = $GLOBALS['SQ_SYSTEM']->am->getAsset($paypal_account_id);
150 
151  $payment_info['business'] = array_get_index($_POST, 'business', '');
152  //check if the payment is made to correct merchant
153  if ($payment_info['business'] != $paypal_account->attr('account_id')) {
154  $payment_info['error'] = "THE PAYMENT'S MERCHANT ID ({$payment_info['business']}) DOES NOT MATCH THE MERCHANT ID ({$paypal_account->attr('account_id')}) OF THE PAYPAL ACCOUNT #$paypal_account_id.";
155  $this->_returnPageNotFound($payment_info);
156  }
157 
158  //THE PAYMENT IS VALID
159 
160  //get other information of this payment to write to the log file
161  $payment_info['mc_currency'] = array_get_index($_POST, 'mc_currency', '');
162  $payment_info['mc_gross'] = array_get_index($_POST, 'mc_gross', 0);
163  if ($payment_info['txn_type'] == 'cart') {
164  $payment_info['num_cart_items'] = array_get_index($_POST, 'num_cart_items', 0);
165  for ($i = 1; $i <= $payment_info['num_cart_items']; $i++) {
166  $payment_info['item_name'.$i] = array_get_index($_POST, 'item_name'.$i, '');
167  $payment_info['item_number'.$i] = array_get_index($_POST, 'item_number'.$i, '');
168  $payment_info['quantity'.$i] = array_get_index($_POST, 'quantity'.$i, 1);
169  $payment_info['mc_gross_'.$i] = array_get_index($_POST, 'mc_gross_'.$i, 0);
170  $payment_info['price_'.$i] = $payment_info['mc_gross_'.$i]/$payment_info['quantity'.$i];
171  }
172  } else {
173  $payment_info['item_name'] = array_get_index($_POST, 'item_name', '');
174  $payment_info['item_number'] = array_get_index($_POST, 'item_number', '');
175  $payment_info['quantity'] = array_get_index($_POST, 'quantity', 1);
176  $payment_info['price'] = $payment_info['mc_gross']/$payment_info['quantity'];
177  }
178 
179  //split custom parameters
180  $params = Array();
181  $custom_splits = explode(',', $custom);
182  foreach ($custom_splits as $name_value) {
183  list($name, $value) = explode('=', $name_value);
184  $params[trim($name)] = trim($value);
185  }
186 
187  //set all custom parameters to $_POST array so that triggers can use them later
188  foreach ($params as $key => $value) {
189  $_POST[$key] = $value;
190  }
191 
192  //get target asset
193  $assetid = '';
194  $link = $GLOBALS['SQ_SYSTEM']->am->getLink($this->id, SQ_LINK_NOTICE, '', FALSE, 'target_asset');
195  if (!empty($link)) {
196  $assetid = $link['minorid'];
197  }
198 
199  if (empty($assetid)) {
200  //get assetid from custom parameter
201  $assetid = array_get_index($_POST, $this->attr('assetid_param_name'), '');
202  }
203 
204  if (empty($assetid)) {
205  $payment_info['target_assetid'] = "No target asset ID specified.";
206  } else {
207  $payment_info['target_assetid'] = $assetid;
208  $target_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
209 
210  //if payment is completed, broadcast IPN Completed event
211  if ($payment_info['payment_status'] == 'Completed') {
212  if (empty($target_asset)) {
213  $payment_info['ipn_event'] = "No IPN Completed event is broadcasted because there is no asset with ID #$assetid.";
214  } else {
215  $GLOBALS['SQ_SYSTEM']->broadcastTriggerEvent('trigger_event_paypal_ipn_completed', $target_asset);
216  $payment_info['ipn_event'] = "An IPN Completed event was broadcasted to the asset #$assetid.";
217  }
218  } else {
219  $payment_info['ipn_event'] = "No IPN Completed event is broadcasted because payment status is {$payment_info['payment_status']}.";
220  }
221  }
222 
223  $paypal_account->addPaypalOrder($payment_info, $message);
224  $payment_info['paypal_order'] = $message;
225 
226  //log message
227  $this->_logIncomingRequestInfo(LOG_FILE_PREFIX.'_account_'.$paypal_account_id, $payment_info);
228 
229  } else {
230  //trick that there is no asset at this URL because the URL of IPN Receiver should not be revealed
231  $this->_returnPageNotFound($payment_info);
232  }
233 
234  }//end printFrontend()
235 
236 
246  private function _returnPageNotFound($payment_info) {
247  $protocol = current_protocol();
248  $url = strip_url(current_url(FALSE, TRUE));
249  if (!headers_sent()) {
250  header('HTTP/1.0 404 Not Found');
251  }
252 
253  //log message before triggering an error
254  $payment_info['status_code'] = "A 404 status code was sent back to the client.";
255  $this->_logIncomingRequestInfo(FAIL_LOG_FILE, $payment_info);
256 
257  trigger_localised_error('SYS0218', E_USER_NOTICE, $protocol.'://'.$url);
258 
259  exit(1);
260 
261  }//end _returnPageNotFound()
262 
263 
273  private function _logIncomingRequestInfo($file_name, $payment_info) {
274  $data = '';
275  foreach ($payment_info as $name => $message) {
276  if ($message == '') {
277  $message = '[Empty]';
278  }
279  $data .= "$name=$message\n";
280  }
281  log_write($data, $file_name, E_USER_NOTICE, FALSE);
282 
283  }//end _logIncomingRequest()
284 
285 
286 }//end class
287 
288 ?>