Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
datacash_processor.inc
1 <?php
18 define('DATACASH_LOG_FILE_PREFIX', 'datacash_transactions_account_');
19 
20 
32 {
33 
34 
40  private static $_URLS = Array(
41  'live' => 'https://mars.transaction.datacash.com/Transaction',
42  'test' => 'https://testserver.datacash.com/Transaction',
43  );
44 
49  private $_client;
50 
55  private $_password;
56 
61  private $_timeout;
62 
67  private $_test_mode = TRUE;
68 
73  private $_log_file_name;
74 
79  private $_config = NULL;
80 
85  private $_request = NULL;
86 
91  private $_response = NULL;
92 
97  private $_success = FALSE;
98 
104  private $_merchant_url;
105 
111  private $_purchase_desc;
112 
118  private $_purchase_time;
119 
124  private $_cardinfo_dir_path;
125 
130  private $_card_type;
131 
136  private $_nonthreed_card_types = Array('American Express', 'Diners Club', 'Duet', 'GE Capital', 'JCB', 'Laser');
137 
143  private static $_MANDATE_THREED_CARD_TYPES = Array('Maestro', 'Switch');
144 
149  private static $_TEST_CARD_NUMBERS = Array(
150  '1000070000000001' => 'VISA - SUCCESS (Status: 1)',
151  '100063000000007' => 'VISA - DECLINED (Status: 7)',
152  '10006500000000' => 'VISA Delta - REFERRED (Status: 7)',
153  '1000010000000007' => 'MASTER - SUCCESS (Status: 1)',
154  '1000350000000106' => 'MASTER - DECLINED (Status: 7)',
155  '1000350000000122' => 'MASTER - REFERRED (Status: 7)',
156  '1000350000000098' => 'MASTER - No tids (Status: 53)',
157  '1000350000000460' => 'MASTER - Timeout (Sleep 600s)',
158  '1000350000000395' => 'MASTER - Missing start (Status: 28)',
159  '1000130000000003' => 'Maestro - SUCCESS (Status: 1)',
160  '1000310000000019' => 'Maestro - DECLINED (Status: 7)',
161  '1000060000000002' => 'Switch - SUCCESS (Status: 1)',
162  '4936000000000000019' => 'Switch - DECLINED (Status: 7)',
163  '633499100000000004' => 'Solo - SUCCESS (Status: 1)',
164  '633499999999999997' => 'Solo - DECLINED (Status: 7)',
165  '1000681548753263' => 'Laser - SUCCESS (Status: 1)',
166  '1000681548753461' => 'Laser - DECLINED (Status: 7)',
167  '3528000000000007' => 'JCB - SUCCESS (Status: 1)',
168  '3528000000000023' => 'JCB - DECLINED (Status: 7)',
169  '1000350000000007' => 'MASTER - AVS/CV2 TEST (Amount: 1010.00 - 1010.14)',
170  );
171 
172 
184  function __construct($client, $password, $datacash_api_path, $cardinfo_dir_path, $timeout = 60, $test_mode = TRUE)
185  {
186  $this->_client = $client;
187  $this->_password = $password;
188  require_once $datacash_api_path;
189  $this->_cardinfo_dir_path = $cardinfo_dir_path;
190  $this->_timeout = $timeout;
191  $this->_test_mode = $test_mode;
192  $this->_log_file_name = DATACASH_LOG_FILE_PREFIX.$this->_client.($test_mode? '_test' : '_live');
193 
194  $this->_createConfigurationDocument();
195  $this->_createRequestDocument();
196 
197  }//end constructor
198 
199 
206  private function _createConfigurationDocument()
207  {
208  $url = $this->_test_mode? self::$_URLS['test'] : self::$_URLS['live'];
209 
210  $config_string = <<<HEREDOC
211 <Configuration>
212  <host>$url</host>
213  <timeout>{$this->_timeout}</timeout>
214  <logging>0</logging>
215  <logfile></logfile>
216 </Configuration>
217 HEREDOC;
218 
219  $config = new DataCash_Document();
220  $config->readDocumentFromString($config_string);
221  $this->_config = $config;
222 
223  //create the global variable $config because the DataCash_Logger class of the Datacash PHP API use it in dc_log()
224  //method and that method is called in the set() method of the Datacash_Document class.
225  if (!isset($GLOBALS['config'])) {
226  $GLOBALS['config'] = $config;
227  }
228 
229  }//end createConfigurationDocument()
230 
231 
238  private function _createRequestDocument()
239  {
240  $request_string = <<<HEREDOC
241 <Request>
243  <client>{$this->_client}</client>
244  <password>{$this->_password}</password>
246 </Request>
247 HEREDOC;
248 
249  $request = new DataCash_Document();
250  $request->readDocumentFromString($request_string);
251  $this->_request = $request;
252 
253  }//end createRequestDocument()
254 
255 
264  public function setCardNumber($card_no)
265  {
266  @$this->_request->set('Request.Transaction.CardTxn.Card.pan', $card_no);
267 
268  }//end setCardNumber()
269 
270 
280  public function setCardExpiryDate($month, $year)
281  {
282  @$this->_request->set('Request.Transaction.CardTxn.Card.expirydate', "$month/$year");
283 
284  }//end setCardExpiryDate()
285 
286 
296  public function setCardStartDate($month, $year)
297  {
298  @$this->_request->set('Request.Transaction.CardTxn.Card.startdate', "$month/$year");
299 
300  }//end setCardStartDate()
301 
302 
311  public function setCardIssueNumber($issue_no)
312  {
313  @$this->_request->set('Request.Transaction.CardTxn.Card.issuenumber', $issue_no);
314 
315  }//end setCardIssueNumber()
316 
317 
326  public function setCardCV2($cv2)
327  {
328  @$this->_request->set('Request.Transaction.CardTxn.Card.Cv2Avs.cv2', $cv2);
329 
330  }//end setCardCV2()
331 
332 
342  public function setStreetAddress($number, $address)
343  {
344  @$this->_request->set('Request.Transaction.CardTxn.Card.Cv2Avs.street_address'.$number, $address);
345 
346  }//end setStreetAddress()
347 
348 
357  public function setPostcode($postcode)
358  {
359  @$this->_request->set('Request.Transaction.CardTxn.Card.Cv2Avs.postcode', $postcode);
360 
361  }//end setPostcode()
362 
363 
370  public function setTransactionTypeAuth()
371  {
372  @$this->_request->set('Request.Transaction.CardTxn.method', 'auth');
373 
374  }//end setTransactionTypeAuth()
375 
376 
385  public function setMerchantReference($merchant_ref)
386  {
387  @$this->_request->set('Request.Transaction.TxnDetails.merchantreference', $this->_getUniqueReferenceNumber($merchant_ref));
388 
389  }//end setMerchantReference()
390 
391 
401  public function setAmount($amount, $currency)
402  {
403  @$this->_request->set('Request.Transaction.TxnDetails.amount', sprintf('%01.2f',$amount), array('currency' => $currency));
404 
405  }//end setAmount()
406 
407 
419  public function setThreeDSecureParams($merchant_url, $purchase_desc, $purchase_time, $extra_nonthreed_card_types)
420  {
421  $this->_merchant_url = $merchant_url;
422  //purchase_desc must be less than 125 characters so it needs to be cut off if longer than 125 characters
423  if (strlen($purchase_desc) > 125) {
424  $purchase_desc = substr($purchase_desc, 0, 125);
425  }
426  $this->_purchase_desc = $purchase_desc;
427  $this->_purchase_time = $purchase_time;
428  $this->_nonthreed_card_types = array_merge($this->_nonthreed_card_types, $extra_nonthreed_card_types);
429 
430  }//end setThreeDSecureParams()
431 
432 
439  public function setThreeDSecure()
440  {
441  @$this->_request->set('Request.Transaction.TxnDetails.ThreeDSecure.verify', 'yes');
442  @$this->_request->set('Request.Transaction.TxnDetails.ThreeDSecure.merchant_url', $this->_merchant_url);
443  @$this->_request->set('Request.Transaction.TxnDetails.ThreeDSecure.purchase_desc', $this->_purchase_desc);
444  @$this->_request->set('Request.Transaction.TxnDetails.ThreeDSecure.purchase_datetime', date('Ymd H:i:s', $this->_purchase_time));
445  //set browser
446  //we assume that all browsers run on PC, we do not support mobile device at the moment
447  @$this->_request->set('Request.Transaction.TxnDetails.ThreeDSecure.Browser.device_category', 0);
448  @$this->_request->set('Request.Transaction.TxnDetails.ThreeDSecure.Browser.accept_headers', array_get_index($_SERVER, 'HTTP_ACCEPT', '*/*'));
449  @$this->_request->set('Request.Transaction.TxnDetails.ThreeDSecure.Browser.user_agent', array_get_index($_SERVER, 'HTTP_USER_AGENT', 'Mozilla/5.0'));
450 
451  }//end setThreeDSecure()
452 
453 
462  public function setStandardPolicy($policy)
463  {
464  @$this->_request->set('Request.Transaction.CardTxn.Card.Cv2Avs.policy', $policy);
465 
466  }//end setStandardPolicy()
467 
468 
479  public function setAuthorization($reference, $pares = NULL, $method = 'threedsecure_authorization_request')
480  {
481  @$this->_request->set('Request.Transaction.HistoricTxn.reference', $reference);
482  @$this->_request->set('Request.Transaction.HistoricTxn.method', $method);
483  if (!is_null($pares)) {
484  @$this->_request->set('Request.Transaction.HistoricTxn.pares_message', $pares);
485  }
486 
487  }//end setAuthorization()
488 
489 
496  public function process()
497  {
498  $cardinfo = @new DataCash_CardInfo(Array('datadir' => $this->_cardinfo_dir_path, 'pan' => $this->_request->get('Request.Transaction.CardTxn.Card.pan')));
499  $invalid = @$cardinfo->validation($this->_request);
500  if (!is_null($invalid)) {
501  $this->_response = $invalid;
502  return FALSE;
503  }
504 
505  //the card is valid, get its scheme
506  $this->_card_type = $cardinfo->scheme();
507 
508  //the card is not in the non 3DS card type list, set 3DS parameters for the request
509  if (!in_array($this->_card_type, $this->_nonthreed_card_types)) {
510  $this->setThreeDSecure();
511  }
512 
513  //send the request
514  $this->_success = $this->_sendRequest();
515 
516  if ($this->_success) {
517  switch (@$this->_response->get('Response.status')) {
518  case 1: //the transaction is accepted
519  $this->_success = 1;
520  break;
521  case 150: //the card is enrolled, redirect it
522  $this->_success = 2;
523  break;
524  case 162: //the card is not enrolled
525  case 159: //no VERes from the directory server
526  case 160: //invalid VERes from the directory server
527  case 186: //invalid VEReq
528  case 187: //the directory server is unable to verify
529  case 158: //3DS is not supported
530  case 163: //3DS Merchant not enabled for scheme acquirer
531  case 164: //3DS Acquirer not supported
532  case 165: //3DS Merchant not enabled for acquirer
533  case 173: //3DS Card not enrolled in Cache
534  $card_type = @$this->_response->get('Response.CardTxn.card_scheme');
535  //if the card type is in the madate 3DS card list, return FALSE; otherwise, send an authorization request without a PARes (for non-enrolled card or card scheme which is not supported by 3-D Secure
536  if (in_array($card_type, self::$_MANDATE_THREED_CARD_TYPES)) {
537  $this->_success = FALSE;
538  } else {
539  //recreate the request
540  $this->_createRequestDocument();
541  //authorize payment
542  $this->authorize(@$this->_response->get('Response.datacash_reference'));
543  }
544  break;
545  default: //the transaction is denied
546  $this->_success = FALSE;
547  }
548 
549  }
550 
551  return $this->_success;
552 
553  }//end process()
554 
555 
565  public function authorize($datacash_ref, $pares = NULL)
566  {
567  //set authorization parameters
568  $this->setAuthorization($datacash_ref, $pares);
569  //send request
570  $this->_success = $this->_sendRequest();
571  if ($this->_success) {
572  $this->_success = @$this->_response->get('Response.status') == 1? 1 : FALSE;
573  }
574 
575  return $this->_success;
576 
577  }//end authorize()
578 
579 
586  private function _sendRequest()
587  {
588  //log the request of the transaction
589  $this->_logRequest();
590 
591  $agent = new DataCash_SSLAgent($this->_config);
592  $success = @$agent->send($this->_request);
593 
594  $response = NULL;
595 
596  if ($success) {
597  $response = $agent->getResponseDocument();
598  } else {
599  $response = new DataCash_Document("Response");
600  @$response->set("Response.status", $agent->errno);
601  @$response->set("Response.reason", $agent->err_str);
602  }
603 
604  $this->_response = $response;
605 
606  //log the transaction response
607  $this->_logResponse();
608 
609  return $success;
610 
611  }//end _sendRequest()
612 
613 
622  function getResponse()
623  {
624  $response = Array();
625 
626  switch ($this->_success) {
627  case 1: //payment is accepted
628  $response['TRANSACTION'] = @$this->_response->get('Response.datacash_reference');
629  $response['TIME'] = @$this->_response->get('Response.time');
630  case FALSE: //error
631  $response['STATUS'] = @$this->_response->get('Response.reason');
632  break;
633  case 2: //redirect user to the Access Control Server (ACS) for card holder verification
634  $response['ACS_URL'] = @$this->_response->get('Response.CardTxn.ThreeDSecure.acs_url');
635  $response['PAREQ_MESSAGE'] = @$this->_response->get('Response.CardTxn.ThreeDSecure.pareq_message');
636  $response['TRANSACTION'] = @$this->_response->get('Response.datacash_reference');
637  break;
638  }
639 
640  return $response;
641 
642  }//end getResponse()
643 
644 
651  public function getMerchantReference()
652  {
653  return @$this->_request->get('Request.Transaction.TxnDetails.merchantreference');
654 
655  }//end getMerchantReference()
656 
657 
664  public static function getTestCardNumbers()
665  {
666  return self::$_TEST_CARD_NUMBERS;
667 
668  }//end getTestCardNumbers()
669 
670 
677  private function _logRequest()
678  {
679  $message = "Request\n";
680  $datacash_ref = @$this->_request->get('Request.Transaction.HistoricTxn.reference');
681  if ($datacash_ref === FALSE) { //a non-historic (auth, pre) request
682  //log 4 last digits of card number
683  $card_no = @$this->_request->get('Request.Transaction.CardTxn.Card.pan');
684  $message .= 'Card number: '.substr($card_no, -4)."\n";
685 
686  //log card type if known
687  if (!empty($this->_card_type)) {
688  $message .= 'Card type: '.$this->_card_type."\n";
689  }
690 
691  //log trasaction type
692  $message .= 'Transaction type: '.@$this->_request->get('Request.Transaction.CardTxn.method')."\n";
693 
694  //log merchant reference
695  $message .= 'Merchant reference: '.$this->getMerchantReference()."\n";
696 
697  //log amount
698  $amount_ele = @$this->_request->get('Request.Transaction.TxnDetails.amount', FALSE);//this second parameter is different from the phpdoc of the Datacash_Document class
699  if ($amount_ele !== FALSE) {
700  $attr_arr = $amount_ele->getAllAttributes();
701  $currency = '';
702  foreach ($attr_arr as $attr) {
703  if ($attr->getName() == 'currency') {
704  $currency = $attr->getValue();
705  break;
706  }
707  }
708  $message .= 'Amount: '.$amount_ele->getText().' '.$currency."\n";
709  }
710 
711  //3-D Secure check
712  $verify = @$this->_request->get('Request.Transaction.TxnDetails.ThreeDSecure.verify');
713  if ($verify === FALSE) {
714  $verify = 'no';
715  }
716  $message .= '3-D Secure check: '.$verify."\n";
717 
718  } else { //a historic request
719  $message .= "Historic reference: $datacash_ref\n";
720  $message .= 'Transaction type: '.@$this->_request->get('Request.Transaction.HistoricTxn.method')."\n";
721  }
722 
723  $this->_log($message);
724 
725  }//end _logRequest()
726 
727 
734  private function _logResponse()
735  {
736  $message = "Response\n";
737  $datacash_ref = @$this->_response->get('Response.datacash_reference');
738  if ($datacash_ref !== FALSE) { //a transaction result returned by Datacash
739  //log the reason message and status
740  $message .= 'Transaction result: '.@$this->_response->get('Response.reason').' (status = '.@$this->_response->get('Response.status').")\n";
741 
742  //if this response is received by a non-historic (auth, pre) request, log the merchant reference so that we can match this response with its request in the log file
743  if (@$this->_request->get('Request.Transaction.HistoricTxn.reference') === FALSE) {
744  $message .= 'Merchant reference: '.$this->getMerchantReference()."\n";
745  }
746 
747  //log Datacash reference
748  $message .= 'Datacash reference: '.$datacash_ref."\n";
749 
750  //log card type if it exists
751  $card_type = @$this->_response->get('Response.CardTxn.card_scheme');
752  if ($card_type !== FALSE) {
753  $message .= "Card type by Datacash: $card_type\n";
754  }
755 
756  //log AVS/CV2 check status
757  $cv2avs_ele = @$this->_response->get('Response.CardTxn.Cv2Avs.cv2avs_status', FALSE);//this second parameter is different from the phpdoc of the Datacash_Document class
758  if ($cv2avs_ele !== FALSE) {
759  $message .= 'AVS/CV2 check: '.$cv2avs_ele->getText()."\n";
760  $attr_arr = $cv2avs_ele->getAllAttributes();
761  foreach ($attr_arr as $attr) {
762  if ($attr->getName() == 'reversal') {
763  $message .= 'Reversal: '.($attr->getValue() == 0? 'failed' : 'successful')."\n";
764  break;
765  }
766  }
767  }
768 
769  } else { //an error occurs
770  $message .= 'HTTP Error: '.@$this->_response->get('Response.reason').' (status = '.@$this->_response->get('Response.status').")\n";
771  }
772 
773  $this->_log($message);
774 
775  }//end _logResponse()
776 
777 
788  private function _log($message, $level = E_USER_NOTICE, $encode=FALSE)
789  {
790  log_write($message, $this->_log_file_name, $level, $encode);
791 
792  }//end log()
793 
794 
803  private function _getUniqueReferenceNumber($ref_no)
804  {
805  $ref_length = strlen($ref_no);
806  //if the reference number is not a hexadecimal number and its length is not between 6 and 30 alphanumeric characters,
807  //convert it to a 32 character hexadecimal number using md5() function so that we can convert its base later
808  if (!is_numeric('0x'.$ref_no) && (($ref_length > 30) || ($ref_length < 6))) {
809  $ref_no = md5($ref_no);
810  $ref_length = strlen($ref_no);
811  } else if (($ref_length > 32) || ($ref_length < 6)) {
812  //if the reference number is a hexadecimal number and (too short (<6) or too long (>32))
813  $ref_no = md5($ref_no);
814  $ref_length = strlen($ref_no);
815  }
816 
817  //ref_length should be between 6 and 32 when it comes here
818  //if ref_length > 30, convert base from 16 (hexadecimal) to 36. We are sure to have the number of base 36 with the
819  //length between 23 and 26 characters because the smallest and biggest number from md5() are 16^29 and 16^33
820  if ($ref_length > 30) {
821  $ref_no = base_convert($ref_no, 16, 36);
822  }
823 
824  return $ref_no;
825 
826  }//end _getUniqueReferenceNumber()
827 
828 
829 }//end class
830 
831 ?>