INFRA-132 - Fix spacing of @return tag in comments
[civicrm-core.git] / CRM / Core / Payment / PayflowPro.php
1 <?php
2 /*
3 +----------------------------------------------------------------------------+
4 | PayflowPro Core Payment Module for CiviCRM version 4.6 |
5 +----------------------------------------------------------------------------+
6 | Licensed to CiviCRM under the Academic Free License version 3.0 |
7 | |
8 | Written & Contributed by Eileen McNaughton - 2009 |
9 +---------------------------------------------------------------------------+
10 */
11
12 /**
13 * Class CRM_Core_Payment_PayflowPro
14 */
15 class CRM_Core_Payment_PayflowPro extends CRM_Core_Payment {
16 // (not used, implicit in the API, might need to convert?)
17 const
18 CHARSET = 'UFT-8';
19
20 /**
21 * We only need one instance of this object. So we use the singleton
22 * pattern and cache the instance in this variable
23 *
24 * @var object
25 * @static
26 */
27 static private $_singleton = NULL;
28
29 /*
30 * Constructor
31 *
32 * @param string $mode
33 * The mode of operation: live or test.
34 *
35 * @return void
36 */
37 /**
38 * @param $mode
39 * @param $paymentProcessor
40 */
41 public function __construct($mode, &$paymentProcessor) {
42 // live or test
43 $this->_mode = $mode;
44 $this->_paymentProcessor = $paymentProcessor;
45 $this->_processorName = ts('Payflow Pro');
46 }
47
48 /*
49 * This function sends request and receives response from
50 * the processor. It is the main function for processing on-server
51 * credit card transactions
52 */
53 /**
54 * This function collects all the information from a web/api form and invokes
55 * the relevant payment processor specific functions to perform the transaction
56 *
57 * @param array $params
58 * Assoc array of input parameters for this transaction.
59 *
60 * @return array
61 * the result in an nice formatted array (or an error object)
62 * @abstract
63 */
64 public function doDirectPayment(&$params) {
65 if (!defined('CURLOPT_SSLCERT')) {
66 CRM_Core_Error::fatal(ts('PayFlowPro requires curl with SSL support'));
67 }
68
69 /*
70 * define variables for connecting with the gateway
71 */
72
73 // Are you using the Payflow Fraud Protection Service?
74 // Default is YES, change to NO or blank if not.
75 //This has not been investigated as part of writing this payment processor
76 $fraud = 'NO';
77 //if you have not set up a separate user account the vendor name is used as the username
78 if (!$this->_paymentProcessor['subject']) {
79 $user = $this->_paymentProcessor['user_name'];
80 }
81 else {
82 $user = $this->_paymentProcessor['subject'];
83 }
84
85 // ideally this id would be passed through into this class as
86 // part of the paymentProcessor
87 //object with the other variables. It seems inefficient to re-query to get it.
88 //$params['processor_id'] = CRM_Core_DAO::getFieldValue(
89 // 'CRM_Contribute_DAO_ContributionP
90 //age',$params['contributionPageID'], 'payment_processor_id' );
91
92 /*
93 *Create the array of variables to be sent to the processor from the $params array
94 * passed into this function
95 *
96 */
97
98 $payflow_query_array = array(
99 'USER' => $user,
100 'VENDOR' => $this->_paymentProcessor['user_name'],
101 'PARTNER' => $this->_paymentProcessor['signature'],
102 'PWD' => $this->_paymentProcessor['password'],
103 // C - Direct Payment using credit card
104 'TENDER' => 'C',
105 // A - Authorization, S - Sale
106 'TRXTYPE' => 'S',
107 'ACCT' => urlencode($params['credit_card_number']),
108 'CVV2' => $params['cvv2'],
109 'EXPDATE' => urlencode(sprintf('%02d', (int) $params['month']) . substr($params['year'], 2, 2)),
110 'ACCTTYPE' => urlencode($params['credit_card_type']),
111 'AMT' => urlencode($params['amount']),
112 'CURRENCY' => urlencode($params['currency']),
113 'FIRSTNAME' => $params['billing_first_name'],
114 //credit card name
115 'LASTNAME' => $params['billing_last_name'],
116 //credit card name
117 'STREET' => $params['street_address'],
118 'CITY' => urlencode($params['city']),
119 'STATE' => urlencode($params['state_province']),
120 'ZIP' => urlencode($params['postal_code']),
121 'COUNTRY' => urlencode($params['country']),
122 'EMAIL' => $params['email'],
123 'CUSTIP' => urlencode($params['ip_address']),
124 'COMMENT1' => urlencode($params['contributionType_accounting_code']),
125 'COMMENT2' => $mode,
126 'INVNUM' => urlencode($params['invoiceID']),
127 'ORDERDESC' => urlencode($params['description']),
128 'VERBOSITY' => 'MEDIUM',
129 'BILLTOCOUNTRY' => urlencode($params['country']),
130 );
131
132 if ($params['installments'] == 1) {
133 $params['is_recur'] == FALSE;
134 }
135
136 if ($params['is_recur'] == TRUE) {
137
138 $payflow_query_array['TRXTYPE'] = 'R';
139 $payflow_query_array['OPTIONALTRX'] = 'S';
140 $payflow_query_array['OPTIONALTRXAMT'] = $params['amount'];
141 //Amount of the initial Transaction. Required
142 $payflow_query_array['ACTION'] = 'A';
143 //A for add recurring (M-modify,C-cancel,R-reactivate,I-inquiry,P-payment
144 $payflow_query_array['PROFILENAME'] = urlencode('RegularContribution');
145 //A for add recurring (M-modify,C-cancel,R-reactivate,I-inquiry,P-payment
146 if ($params['installments'] > 0) {
147 $payflow_query_array['TERM'] = $params['installments'] - 1;
148 //ie. in addition to the one happening with this transaction
149 }
150 // $payflow_query_array['COMPANYNAME']
151 // $payflow_query_array['DESC'] = not set yet Optional
152 // description of the goods or
153 //services being purchased.
154 //This parameter applies only for ACH_CCD accounts.
155 // The
156 // $payflow_query_array['MAXFAILPAYMENTS'] = 0;
157 // number of payment periods (as s
158 //pecified by PAYPERIOD) for which the transaction is allowed
159 //to fail before PayPal cancels a profile. the default
160 // value of 0 (zero) specifies no
161 //limit. Retry
162 //attempts occur until the term is complete.
163 // $payflow_query_array['RETRYNUMDAYS'] = (not set as can't assume business rule
164
165 switch ($params['frequency_unit']) {
166 case '1 week':
167 $params['next_sched_contribution_date'] = mktime(0, 0, 0, date("m"), date("d") + 7,
168 date("Y")
169 );
170 $params['end_date'] = mktime(0, 0, 0, date("m"), date("d") + (7 * $payflow_query_array['TERM']),
171 date("Y")
172 );
173 $payflow_query_array['START'] = date('mdY', $params['next_sched_contribution_date']);
174 $payflow_query_array['PAYPERIOD'] = "WEEK";
175 $params['frequency_unit'] = "week";
176 $params['frequency_interval'] = 1;
177 break;
178
179 case '2 weeks':
180 $params['next_sched_contribution_date'] = mktime(0, 0, 0, date("m"), date("d") + 14, date("Y"));
181 $params['end_date'] = mktime(0, 0, 0, date("m"), date("d") + (14 * $payflow_query_array['TERM']), date("Y ")
182 );
183 $payflow_query_array['START'] = date('mdY', $params['next_sched_contribution_date']);
184 $payflow_query_array['PAYPERIOD'] = "BIWK";
185 $params['frequency_unit'] = "week";
186 $params['frequency_interval'] = 2;
187 break;
188
189 case '4 weeks':
190 $params['next_sched_contribution_date'] = mktime(0, 0, 0, date("m"), date("d") + 28, date("Y")
191 );
192 $params['end_date'] = mktime(0, 0, 0, date("m"), date("d") + (28 * $payflow_query_array['TERM']), date("Y")
193 );
194 $payflow_query_array['START'] = date('mdY', $params['next_sched_contribution_date']);
195 $payflow_query_array['PAYPERIOD'] = "FRWK";
196 $params['frequency_unit'] = "week";
197 $params['frequency_interval'] = 4;
198 break;
199
200 case '1 month':
201 $params['next_sched_contribution_date'] = mktime(0, 0, 0, date("m") + 1,
202 date("d"), date("Y")
203 );
204 $params['end_date'] = mktime(0, 0, 0, date("m") +
205 (1 * $payflow_query_array['TERM']),
206 date("d"), date("Y")
207 );
208 $payflow_query_array['START'] = date('mdY', $params['next_sched_contribution_date']);
209 $payflow_query_array['PAYPERIOD'] = "MONT";
210 $params['frequency_unit'] = "month";
211 $params['frequency_interval'] = 1;
212 break;
213
214 case '3 months':
215 $params['next_sched_contribution_date'] = mktime(0, 0, 0, date("m") + 3, date("d"), date("Y")
216 );
217 $params['end_date'] = mktime(0, 0, 0, date("m") +
218 (3 * $payflow_query_array['TERM']),
219 date("d"), date("Y")
220 );
221 $payflow_query_array['START'] = date('mdY', $params['next_sched_contribution_date']);
222 $payflow_query_array['PAYPERIOD'] = "QTER";
223 $params['frequency_unit'] = "month";
224 $params['frequency_interval'] = 3;
225 break;
226
227 case '6 months':
228 $params['next_sched_contribution_date'] = mktime(0, 0, 0, date("m") + 6, date("d"),
229 date("Y")
230 );
231 $params['end_date'] = mktime(0, 0, 0, date("m") +
232 (6 * $payflow_query_array['TERM']),
233 date("d"), date("Y")
234 );
235 $payflow_query_array['START'] = date('mdY', $params['next_sched_contribution_date']
236 );
237 $payflow_query_array['PAYPERIOD'] = "SMYR";
238 $params['frequency_unit'] = "month";
239 $params['frequency_interval'] = 6;
240 break;
241
242 case '1 year':
243 $params['next_sched_contribution_date'] = mktime(0, 0, 0, date("m"), date("d"),
244 date("Y") + 1
245 );
246 $params['end_date'] = mktime(0, 0, 0, date("m"), date("d"),
247 date("Y") +
248 (1 * $payflow_query_array['TEM'])
249 );
250 $payflow_query_array['START'] = date('mdY', $params['next_sched_contribution_date']);
251 $payflow_query_array['PAYPERIOD'] = "YEAR";
252 $params['frequency_unit'] = "year";
253 $params['frequency_interval'] = 1;
254 break;
255 }
256 }
257
258 CRM_Utils_Hook::alterPaymentProcessorParams($this, $params, $payflow_query_array);
259 $payflow_query = $this->convert_to_nvp($payflow_query_array);
260
261 /*
262 * Check to see if we have a duplicate before we send
263 */
264 if ($this->_checkDupe($params['invoiceID'])) {
265 return self::errorExit(9003, 'It appears that this transaction is a duplicate. Have you already submitted the form once? If so there may have been a connection problem. Check your email for a receipt. If you do not receive a receipt within 2 hours you can try your transaction again. If you continue to have problems please contact the site administrator.');
266 }
267
268 // ie. url at payment processor to submit to.
269 $submiturl = $this->_paymentProcessor['url_site'];
270
271 $responseData = self::submit_transaction($submiturl, $payflow_query);
272
273 /*
274 * Payment successfully sent to gateway - process the response now
275 */
276 $result = strstr($responseData, "RESULT");
277 $nvpArray = array();
278 while (strlen($result)) {
279 // name
280 $keypos = strpos($result, '=');
281 $keyval = substr($result, 0, $keypos);
282 // value
283 $valuepos = strpos($result, '&') ? strpos($result, '&') : strlen($result);
284 $valval = substr($result, $keypos + 1, $valuepos - $keypos - 1);
285 // decoding the respose
286 $nvpArray[$keyval] = $valval;
287 $result = substr($result, $valuepos + 1, strlen($result));
288 }
289 // get the result code to validate.
290 $result_code = $nvpArray['RESULT'];
291 /*debug
292 echo "<p>Params array</p><br>";
293 print_r($params);
294 echo "<p></p><br>";
295 echo "<p>Values to Payment Processor</p><br>";
296 print_r($payflow_query_array);
297 echo "<p></p><br>";
298 echo "<p>Results from Payment Processor</p><br>";
299 print_r($nvpArray);
300 echo "<p></p><br>";
301 */
302
303 switch ($result_code) {
304 case 0:
305
306 /*******************************************************
307 * Success !
308 * This is a successful transaction. PayFlow Pro does return further information
309 * about transactions to help you identify fraud including whether they pass
310 * the cvv check, the avs check. This is stored in
311 * CiviCRM as part of the transact
312 * but not further processing is done. Business rules would need to be defined
313
314 *******************************************************/
315 $params['trxn_id'] = $nvpArray['PNREF'] . $nvpArray['TRXPNREF'];
316 //'trxn_id' is varchar(255) field. returned value is length 12
317 $params['trxn_result_code'] = $nvpArray['AUTHCODE'] . "-Cvv2:" . $nvpArray['CVV2MATCH'] . "-avs:" . $nvpArray['AVSADDR'];
318
319 if ($params['is_recur'] == TRUE) {
320 $params['recur_trxn_id'] = $nvpArray['PROFILEID'];
321 //'trxn_id' is varchar(255) field. returned value is length 12
322 }
323 return $params;
324
325 case 1:
326 return self::errorExit(9008, "There is a payment processor configuration problem. This is usually due to invalid account information or ip restrictions on the account. You can verify ip restriction by logging // into Manager. See Service Settings >> Allowed IP Addresses. ");
327
328 case 12:
329 // Hard decline from bank.
330 return self::errorExit(9009, "Your transaction was declined ");
331
332 case 13:
333 // Voice authorization required.
334 return self::errorExit(9010, "Your Transaction is pending. Contact Customer Service to complete your order.");
335
336 case 23:
337 // Issue with credit card number or expiration date.
338 return self::errorExit(9011, "Invalid credit card information. Please re-enter.");
339
340 case 26:
341 return self::errorExit(9012, "You have not configured your payment processor with the correct credentials. Make sure you have provided both the <vendor> and the <user> variables ");
342
343 default:
344 return self::errorExit(9013, "Error - from payment processor: [" . $result_code . " " . $nvpArray['RESPMSG'] . "] ");
345 }
346
347 return self::errorExit(9014, "Check the code - all transactions should have been headed off before they got here. Something slipped through the net");
348 }
349
350 /**
351 * Checks to see if invoice_id already exists in db
352 *
353 * @param int $invoiceId
354 * The ID to check.
355 *
356 * @return bool
357 * True if ID exists, else false
358 */
359 public function _checkDupe($invoiceId) {
360 //copied from Eway but not working and not really sure it should!
361 $contribution = new CRM_Contribute_DAO_Contribution();
362 $contribution->invoice_id = $invoiceId;
363 return $contribution->find();
364 }
365
366 /*
367 * Produces error message and returns from class
368 */
369 /**
370 * @param null $errorCode
371 * @param null $errorMessage
372 *
373 * @return object
374 */
375 public function &errorExit($errorCode = NULL, $errorMessage = NULL) {
376 $e = CRM_Core_Error::singleton();
377 if ($errorCode) {
378 $e->push($errorCode, 0, NULL, $errorMessage);
379 }
380 else {
381 $e->push(9000, 0, NULL, 'Unknown System Error.');
382 }
383 return $e;
384 }
385
386
387 /*
388 * NOTE: 'doTransferCheckout' not implemented
389 */
390 /**
391 * @param array $params
392 * @param $component
393 *
394 * @throws Exception
395 */
396 public function doTransferCheckout(&$params, $component) {
397 CRM_Core_Error::fatal(ts('This function is not implemented'));
398 }
399
400 /*
401 * This public function checks to see if we have the right processor config values set
402 *
403 * NOTE: Called by Events and Contribute to check config params are set prior to trying
404 * register any credit card details
405 *
406 * @param string $mode
407 * The mode we are operating in (live or test) - not used.
408 *
409 * returns string $errorMsg if any errors found - null if OK
410 *
411 */
412
413 // function checkConfig( $mode ) // CiviCRM V1.9 Declaration
414
415 // CiviCRM V2.0 Declaration
416 /**
417 * This function checks to see if we have the right config values
418 *
419 * @internal param string $mode the mode we are operating in (live or test)
420 *
421 * @return string
422 * the error message if any
423 */
424 public function checkConfig() {
425 $errorMsg = array();
426 if (empty($this->_paymentProcessor['user_name'])) {
427 $errorMsg[] = ' ' . ts('ssl_merchant_id is not set for this payment processor');
428 }
429
430 if (empty($this->_paymentProcessor['url_site'])) {
431 $errorMsg[] = ' ' . ts('URL is not set for %1', array(1 => $this->_paymentProcessor['name']));
432 }
433
434 if (!empty($errorMsg)) {
435 return implode('<p>', $errorMsg);
436 }
437 else {
438 return NULL;
439 }
440 }
441 //end check config
442
443 /*
444 * convert to a name/value pair (nvp) string
445 */
446 /**
447 * @param $payflow_query_array
448 *
449 * @return array|string
450 */
451 public function convert_to_nvp($payflow_query_array) {
452 foreach ($payflow_query_array as $key => $value) {
453 $payflow_query[] = $key . '[' . strlen($value) . ']=' . $value;
454 }
455 $payflow_query = implode('&', $payflow_query);
456
457 return $payflow_query;
458 }
459
460 /*
461 * Submit transaction using CuRL
462 * @submiturl string Url to direct HTTPS GET to
463 * @payflow_query value string to be posted
464 *
465 */
466 /**
467 * @param $submiturl
468 * @param $payflow_query
469 *
470 * @return mixed|object
471 */
472 public function submit_transaction($submiturl, $payflow_query) {
473 /*
474 * Submit transaction using CuRL
475 */
476
477 // get data ready for API
478 $user_agent = $_SERVER['HTTP_USER_AGENT'];
479 // Here's your custom headers; adjust appropriately for your setup:
480 $headers[] = "Content-Type: text/namevalue";
481 //or text/xml if using XMLPay.
482 $headers[] = "Content-Length : " . strlen($data);
483 // Length of data to be passed
484 // Here the server timeout value is set to 45, but notice
485 // below in the cURL section, the timeout
486 // for cURL is 90 seconds. You want to make sure the server
487 // timeout is less, then the connection.
488 $headers[] = "X-VPS-Timeout: 45";
489 //random unique number - the transaction is retried using this transaction ID
490 // in this function but if that doesn't work and it is re- submitted
491 // it is treated as a new attempt. PayflowPro doesn't allow
492 // you to change details (e.g. card no) when you re-submit
493 // you can only try the same details
494 $headers[] = "X-VPS-Request-ID: " . rand(1, 1000000000);
495 // optional header field
496 $headers[] = "X-VPS-VIT-Integration-Product: CiviCRM";
497 // other Optional Headers. If used adjust as necessary.
498 // Name of your OS
499 //$headers[] = "X-VPS-VIT-OS-Name: Linux";
500 // OS Version
501 //$headers[] = "X-VPS-VIT-OS-Version: RHEL 4";
502 // What you are using
503 //$headers[] = "X-VPS-VIT-Client-Type: PHP/cURL";
504 // For your info
505 //$headers[] = "X-VPS-VIT-Client-Version: 0.01";
506 // For your info
507 //$headers[] = "X-VPS-VIT-Client-Architecture: x86";
508 // Application version
509 //$headers[] = "X-VPS-VIT-Integration-Version: 0.01";
510 $ch = curl_init();
511 curl_setopt($ch, CURLOPT_URL, $submiturl);
512 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
513 curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
514 curl_setopt($ch, CURLOPT_HEADER, 1);
515 // tells curl to include headers in response
516 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
517 // return into a variable
518 curl_setopt($ch, CURLOPT_TIMEOUT, 90);
519 // times out after 90 secs
520 if (ini_get('open_basedir') == '' && ini_get('safe_mode') == 'Off') {
521 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
522 }
523 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'verifySSL'));
524 // this line makes it work under https
525 curl_setopt($ch, CURLOPT_POSTFIELDS, $payflow_query);
526 //adding POST data
527 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'verifySSL') ? 2 : 0);
528 //verifies ssl certificate
529 curl_setopt($ch, CURLOPT_FORBID_REUSE, TRUE);
530 //forces closure of connection when done
531 curl_setopt($ch, CURLOPT_POST, 1);
532 //data sent as POST
533
534 // Try to submit the transaction up to 3 times with 5 second delay. This can be used
535 // in case of network issues. The idea here is since you are posting via HTTPS there
536 // could be general network issues, so try a few times before you tell customer there
537 // is an issue.
538
539 $i = 1;
540 while ($i++ <= 3) {
541 $responseData = curl_exec($ch);
542 $responseHeaders = curl_getinfo($ch);
543 if ($responseHeaders['http_code'] != 200) {
544 // Let's wait 5 seconds to see if its a temporary network issue.
545 sleep(5);
546 }
547 elseif ($responseHeaders['http_code'] == 200) {
548 // we got a good response, drop out of loop.
549 break;
550 }
551 }
552
553 /*
554 * Transaction submitted -
555 * See if we had a curl error - if so tell 'em and bail out
556 *
557 * NOTE: curl_error does not return a logical value (see its documentation), but
558 * a string, which is empty when there was no error.
559 */
560 if ((curl_errno($ch) > 0) || (strlen(curl_error($ch)) > 0)) {
561 curl_close($ch);
562 $errorNum = curl_errno($ch);
563 $errorDesc = curl_error($ch);
564
565 //Paranoia - in the unlikley event that 'curl' errno fails
566 if ($errorNum == 0) {
567 $errorNum = 9005;
568 }
569
570 // Paranoia - in the unlikley event that 'curl' error fails
571 if (strlen($errorDesc) == 0) {
572 $errorDesc = "Connection to payment gateway failed";
573 }
574 if ($errorNum = 60) {
575 return self::errorExit($errorNum, "Curl error - " . $errorDesc .
576 " Try this link for more information http://curl.haxx.se/d
577 ocs/sslcerts.html"
578 );
579 }
580
581 return self::errorExit($errorNum, "Curl error - " . $errorDesc .
582 " processor response = " . $processorResponse
583 );
584 }
585
586 /*
587 * If null data returned - tell 'em and bail out
588 *
589 * NOTE: You will not necessarily get a string back, if the request failed for
590 * any reason, the return value will be the boolean false.
591 */
592 if (($responseData === FALSE) || (strlen($responseData) == 0)) {
593 curl_close($ch);
594 return self::errorExit(9006, "Error: Connection to payment gateway failed - no data
595 returned. Gateway url set to $submiturl");
596 }
597
598 /*
599 * If gateway returned no data - tell 'em and bail out
600 */
601 if (empty($responseData)) {
602 curl_close($ch);
603 return self::errorExit(9007, "Error: No data returned from payment gateway.");
604 }
605
606 /*
607 * Success so far - close the curl and check the data
608 */
609 curl_close($ch);
610 return $responseData;
611 }
612 //end submit_transaction
613
614 /**
615 * @param int $recurringProfileID
616 * @param int $processorID
617 *
618 * @throws Exception
619 */
620 public function getRecurringTransactionStatus($recurringProfileID, $processorID) {
621 if (!defined('CURLOPT_SSLCERT')) {
622 CRM_Core_Error::fatal(ts('PayFlowPro requires curl with SSL support'));
623 }
624
625 /*
626 * define variables for connecting with the gateway
627 */
628
629 //if you have not set up a separate user account the vendor name is used as the username
630 if (!$this->_paymentProcessor['subject']) {
631 $user = $this->_paymentProcessor['user_name'];
632 }
633 else {
634 $user = $this->_paymentProcessor['subject'];
635 }
636 //$recurringProfileID = "RT0000000001";
637 // c $trythis = $this->getRecurringTransactionStatus($recurringProfileID,17);
638
639 /*
640 *Create the array of variables to be sent to the processor from the $params array
641 * passed into this function
642 *
643 */
644
645 $payflow_query_array = array(
646 'USER' => $user,
647 'VENDOR' => $this->_paymentProcessor['user_name'],
648 'PARTNER' => $this->_paymentProcessor['signature'],
649 'PWD' => $this->_paymentProcessor['password'],
650 // C - Direct Payment using credit card
651 'TENDER' => 'C',
652 // A - Authorization, S - Sale
653 'TRXTYPE' => 'R',
654 'ACTION' => 'I',
655 //A for add recurring
656 //(M-modify,C-cancel,R-reactivate,
657 //I-inquiry,P-payment
658 'ORIGPROFILEID' => $recurringProfileID,
659 'PAYMENTHISTORY' => 'Y',
660 );
661
662 $payflow_query = $this->convert_to_nvp($payflow_query_array);
663 echo $payflow_query;
664 $submiturl = $this->_paymentProcessor['url_site'];
665 //ie. url at payment processor to submit to.
666 $responseData = self::submit_transaction($submiturl, $payflow_query);
667 /*
668 * Payment successfully sent to gateway - process the response now
669 */
670
671 $result = strstr($responseData, "RESULT");
672 $nvpArray = array();
673 while (strlen($result)) {
674 // name
675 $keypos = strpos($result, '=');
676 $keyval = substr($result, 0, $keypos);
677 // value
678 $valuepos = strpos($result, '&') ? strpos($result, '&') : strlen($result);
679 $valval = substr($result, $keypos + 1, $valuepos - $keypos - 1);
680 // decoding the respose
681 $nvpArray[$keyval] = $valval;
682 $result = substr($result, $valuepos + 1, strlen($result));
683 }
684 // get the result code to validate.
685 $result_code = $nvpArray['RESULT'];
686 print_r($responseData);
687
688 //RESPMSG=Invalid Profile ID: Invalid recurring profile ID
689 //RT0000000001
690 }
691 }