I am trying to make a module that handles verisign's payflowpro gateway. I've got it working, but I think it's pretty ugly and I'm curious if anyone has any thoughts on security. I got initial code from http://ca.php.net/pfpro and used the ccard.module as my base. We have a few sites on the server but no one has access besides myself (aside from limited FTP and normal http access). I am also in the process of setting up an SSL connection for this process.

If you are going to try it, you have to change the info in the _payflowpro_process function on lines 163-166 and lines 179, 180 and 186

<?php
/*
 * $Id: payflowpro.module,v 1 2006/06/29 by alynner copied from ccard by gordon
 */

function payflowpro_help($section = '') {
  switch ($section) {
    case 'admin/modules#description':
      return t('payflowpro payment processing.<br />Dependency: payment.module');
      break;
    case 'admin/settings/payflowpro':
      return t('Enter the required parameters that have been supplied during the signup process with payflowpro.');
      break;
  }
}

function payflowpro_menu($maycache) {
  if ($maycache) {
    $items[] = array(
      'path' => 'store/payment/payflowpro',
      'title' => t('Enter Credit Card Details'),
      'callback' => 'payflowpro_enter_payment',
      'access' => 1,
      'type' => MENU_CALLBACK
    );
  }
  return $items;
}

function payflowpro_ec_settings() {
  $form = array();
  $form['payflowpro_url'] = array(
    '#type' => 'textfield', 
    '#title' => t('Credit Card Payment Page'), 
    '#default_value' => variable_get('payflowpro_url', url('store/payment/payflowpro', null, null, true)), 
    '#size' => 70, 
    '#maxlength' => 180, 
    '#desciption' => t('URL to be directed to so that the payment can be received.'),
    '#required' => true
   );
  $form['payflowpro_thanks_url'] = array(
    '#type' => 'textfield', 
    '#title' => t('Thank you Page'), 
    '#default_value' => variable_get('payflowpro_thanks_url', url('node', null, null, true)), 
    '#size' => 70, 
    '#maxlength' => 180, 
    '#desciption' => t('URL to be directed once the payment has been entered.'),
    '#required' => true
   );
  return $form;
}

function payflowpro_paymentapi(&$edit, $op, $arg = '') {
  switch ($op) {
    case 'display name':
      return t('Credit Card');
      break;
    case 'payment page':
      if ($edit->gross > 0) {
        return payment_cc_goto($edit, variable_get('payflowpro_url', url('store/payment/payflowpro', null, null, true)));
      }
      break;
  }
}

function payflowpro_ec_transactionapi(&$txn, $op, $a3 = NULL, $a4 = NULL) {
  if ($txn->payment_method != 'payflowpro') return NULL;
  
  switch($op) {
    case 'delete':
      return payment_cc_delete($edit);
      break;
    case 'insert':
    case 'update':
      return payment_cc_save($edit);
      break;
  }
}

/*
 * Internal Functions
 */

function payflowpro_enter_payment() {
  global $user;

  $txnid = $_GET['txnid'];
  $t = store_transaction_load($txnid);
  if (($user->uid != $t->uid && !user_access('administer store')) || $t->payment_status != 1) {
    drupal_access_denied();
    exit();
  }
  return payflowpro_payment_form($t);
}

function payflowpro_payment_form($t) {
  $form['billing'] = array(
    '#type' => 'credit_card',
    '#required' => TRUE,
    '#name' => $t->address['billing']->firstname . ' ' . $t->address['billing']->lastname,
    '#cvnshow' => TRUE
  );
  $form[] = array(
    '#type' => 'submit',
    '#value' => t('submit payment'),
  );
  $form['t'] = array(
    '#type' => 'value',
    '#value' => $t
  );
  return drupal_get_form('payflowpro_payment_form', $form);
}

function payflowpro_payment_form_validate($form_id, &$values) {
  global $form_values; // use global value here so data from processing is available later
  $t =& $form_values['t'];
  if (!valid_credit_card($form_values) || !_payflowpro_process($form_values)) {
	return payment_cc_goto($t, variable_get('payflowpro_url', url('store/payment/payflowpro', null, null, true)));
  }
}

function payflowpro_payment_form_submit($form_id, &$form_values) {
  if (!valid_credit_card($form_values) || !_payflowpro_process($form_values)) {
	$url = url('store/history', null, null, true);
	  drupal_set_message(t('Sorry, your payment has not been accepted'));
	  return $url;
  }
  else {
	  $t =& $form_values['t'];
	  $t->payment_status = payment_get_status_id('completed');
	  $form_values['txnid'] = $t->txnid;
	
	  // check to see if there are any shippable items
	  $has_shippable = false;
	  foreach ($t->items as $p) {
		if (product_is_shippable($p->nid)) {
		  $has_shippable = true;
		  break;
		}
	  }
	  if (!$has_shippable) {
		$form_values['workflow'] = 6;
	  }
	
	  store_transaction_save($t);
	  store_send_invoice_email($t);
	
	  $url = variable_get('payflowpro_thanks_url', url('node', null, null, true));
	  drupal_set_message(t('your payment has been accepted, thank you'));
	 
	  return $url;
	}
}

/*
 * Private functions
 */

function _payflowpro_process(&$edit) {
     
     //enter your payflow account info here
     
     $user          = "USER";
     $vendor        = "VENDOR";
     $partner        = "PARTNER";
     $pwd            = "PASSWORD";

     $host        = "test-payflow.verisign.com";
     $port        = "443";
	 
	 $amt = $edit[t]->gross;
	 $acct = $edit['cardnumber'];
	 $expdate = $edit['expiry']['expmonth'].$edit['expiry']['expyear'];



     // Adjust these next two paths appropriately; i.e. where did
     // you actually put these two files?
       $binary        = '/path/to/pfpro';
       $library        = '/path/to/libpfpro.so';
       $newld          = dirname($library);
       
       // this is necessary to prevent RESULT=-31 errors; make sure
     // it's a *directory* that contains the cert file that came
     // with the SDK, and *don't* put a trailing slash on it
       putenv("PFPRO_CERT_PATH=/path/to/cert");
       
 
       // This next chunk of stuff assumes you're having linker issues, and
       // attempts to fix or work around them.
       // Now, let's fix the linker path:
       $original_ld_path = getenv("LD_LIBRARY_PATH");
       $newld .= ":$original_path";
       putenv("LD_LIBRARY_PATH=$newld");

       // having set the linker to find the library, now call the binary:
       $cmd = "\"TRXTYPE=S&TENDER=C&PWD=$pwd&USER=$user&VENDOR=$vendor";
       $cmd .= "&PARTNER=$partner&ACCT=$acct&EXPDATE=$expdate&AMT=$amt";
       $cmd .= "\" 30";
       $result = exec("$binary $host $port $cmd", $result_array, $exit_value);

       $valArray = explode('&', $result);

       foreach($valArray as $val) {
               $valArray2 = explode('=', $val);
               $pfpro[$valArray2[0]] = $valArray2[1];
       }

       // To look at the array structure
       //print_r($pfpro);
	   //echo "<br />".$cmd."<br />";
	   //print_r($edit);

       // This should be equal to the RESULT that came back. If you
     // get no result string back, but the exit_value here is 126 or 127,
     // you've got linker or permission problems.
      // echo "the exit value of the system call to pfpro was $exit_value";

  if ($pfpro[RESULT] === '0') {
    
	return  true;
  }
  else { 
	return false;
  }

}

/*
 * Remove dependancy on PHP5
 */

if(!function_exists('http_build_query')) {
  function http_build_query( $formdata, $numeric_prefix = null, $key = null ) {
    $res = array();
    foreach ((array)$formdata as $k=>$v) {
      $tmp_key = urlencode(is_int($k) ? $numeric_prefix.$k : $k);
      if ($key) {
        $tmp_key = $key.'['.$tmp_key.']';
      }
      if ( is_array($v) || is_object($v) ) {
        $res[] = http_build_query($v, null, $tmp_key);
      } else {
        $res[] = $tmp_key."=".urlencode($v);
      }
    }
    return implode("&", $res);
  }
}

I wasn't quite sure where the function payflowpro_payment_form_validate came into play because it always said payment was accepted, even if the _payflowpro_process function returned false. That's when I stuck the if statement into the payflowpro_payment_form_submit function and now it seems to do what it needs to do.

please let me know any of your thoughts, concerns, etc.

thanks
alynner

PS I wasn't sure exactly where to post this, so I put in feature requests, hope thats ok

CommentFileSizeAuthor
#12 payflowpro_1.module36.86 KBdouggreen
#10 payflowpro.patch_1.txt50.85 KBAnonymous (not verified)
#7 payflowpro.patch_0.txt46.1 KBAnonymous (not verified)
#6 payflowpro.patch.txt45.7 KBAnonymous (not verified)
#5 payflowpro_0.module7.22 KBalynner
#1 payflowpro.module7.25 KBalynner

Comments

alynner’s picture

StatusFileSize
new7.25 KB

added as a file attachment...

alynner’s picture

I had set up a settings area for the user/partner/pwd for verisign in the settings, but I felt uneasy about the way that it goes into global variables. I'm not sure if this info could be hacked that way. I'm fine with hard coding it into the module for myself, but it might be better to put it into settings for a distributed module.
Any thoughts on this?

alynner

gordon’s picture

Status: Active » Needs work

A couple of things,

I really don't like it exec'ing a program. It is just a payment gateway, you should be able to do this from PHP completely.

Also the settings username,password etc in the source needs to be moved to a page for entry.

Lastly, do you really need the password as you should be able to have someone make a payment with passing a password to the gateway.

alynner’s picture

That's the thing about Payflowpro and probably why nobody has created a module for it here - you have to use the library and binary files to process it. I found the code for that part on the php.net site (http://ca3.php.net/pfpro), I'll take another look to see if I can do it without using exec.

Yes, you do have to enter the password in for the transaction, it's a requirement. I did have the user/partner/pwd on a settings page that worked, but I was concerned about having them in global variables. Should I be concerned, or is that okay? That's what I meant in comment 2.

thanks
alynner

alynner’s picture

StatusFileSize
new7.22 KB

I haven't looked into getting out of using exec() yet, but I did find that this module was putting through each transaction twice. to fix, replace line 117:

- if (!valid_credit_card($form_values) || !_payflowpro_process($form_values)) {
+ if (!valid_credit_card($form_values)) {

instead of making a patch, I just reattached the module.

Anonymous’s picture

Title: Payflow Pro - attempting to create module » Payflow Pro workable, mostly complete module
Component: payment.module » -- other --
Assigned: Unassigned »
Status: Needs work » Needs review
StatusFileSize
new45.7 KB

I needed a Payflow Pro module so I stared from the one alynner posted and basically rewrote the whole thing using the Payflow Pro docs. I need some feedback from people so that I can finish this up and make it conform to some sort of ecommerce payment module pattern.

This module is 90% complete at this point. I'll be working on this and updating it over the next couple of days.

Here is a pasting of the TODO:

* TODO:
*
* - We really need the concept of sub-transactions so that we can do auth/delayed
* pairs for AVS/CVV2 and general conformance to our merchant agreement when we
* ship products.
* - Payflow Pro transaction information should be integrated in some sort of
* display in the admin/store/transactions URL or something. This includes the
* Payflow Pro transaction results table which complicates things because there could
* be more than 1 result per original Payflow Pro transaction
* - Need to finish the confirmation page that shows the card is charged
* - Need to test what happens when we interrupt requests and the user hits submit again
* - More testing all around, some general fit and finish things
* - Create a .install file (I am lazy and tired of writing this module)

Anonymous’s picture

StatusFileSize
new46.1 KB

Updated to fix a PARMLIST encoding bug
Added RESPMSG output to the watchdog in the case of a transaction failure

patricksettle’s picture

Setup evanchsa's version and have had no luck, consistantly coming back with: Transaction error: 1, "User authentication failed." even after all usernames/passwords etc. are varified to be correct. Seen this module listed in several places, is the version listed in responce #7 the most recent version? Or has further development been done on this.

Would be willing to provide some financial motivation for the completion of this module, any takers?

Anonymous’s picture

Sorry, I have a new version that works with a number of changes. I'll post the patch soon, no need for any financial incentives.

Anonymous’s picture

StatusFileSize
new50.85 KB

New version:

- Added ability to add credit card badges that display on the payment page
- Fixed numerous bugs in how transaction information is saved
- Cleaned up the confirmation page so that it is more generic

TODO is now:

+ * - We really need the concept of sub-transactions so that we can do auth/delayed
+ * pairs for AVS/CVV2 and general conformance to our merchant agreement when we
+ * ship products.
+ * - Payflow Pro transaction information should be integrated in some sort of
+ * display in the admin/store/transactions URL or something. This includes the
+ * Payflow Pro transaction results table which complicates things because there could
+ * be more than 1 result per original Payflow Pro transaction
+ * - Need to test what happens when we interrupt requests and the user hits submit again
+ * - More testing all around, some general fit and finish things
+ * - Create a .install file (I am lazy and tired of writing this module)

patricksettle’s picture

Tried the new patch, still getting Transaction error: 1, "User authentication failed." errors, Confrimed all username/password is correct. Is the password supposed to be plain text, or should it be encrypted before being added to the file?

douggreen’s picture

StatusFileSize
new36.86 KB

Does anyone claim ownership of this ever growing module who will put it into CVS and patch appropriately?

I just discovered from PayPal that my client doesn't have PayFlow Pro as the PayPal Manager page claims, but rather has a legacy Cybercash account. So while the test transactions appear to be going through just fine, they never show up in PayPal. So with this realization, I'm not planning on doing any further work on this module. But before I do so, I wanted to contribute back what I have.

Attached is my latest module, with a few patches for storing and displaying the transaction results properly.

sime’s picture

Component: -- other -- » paypalpro

fixing component

alex_b’s picture

Guys, is this thread obsolete?

See E-Commerce PayFlow Pro module by souvent22 here: http://drupal.org/project/payment_payflowpro

brmassa’s picture

Component: paypalpro » new module
Status: Needs review » Fixed

Alexander,

To let the eC dev team focus on core modules, all new payment gateways shuold be, according our comment on http://drupal.org/node/162699, a new module. and external to eC.

We will try to give as much support as possible, but we simply cant afford develop all asked payments...

regards,

massa

alex_b’s picture

Thanks for your quick reaction and clarification. I was just confused about the open patch here and the module on the other side.

And: thanks for the excellent work on the e-commerce suite.

Alex

Anonymous’s picture

Status: Fixed » Closed (fixed)