CiviCRM Community Forums (archive)

*

News:

Have a question about CiviCRM?
Get it answered quickly at the new
CiviCRM Stack Exchange Q+A site

This forum was archived on 25 November 2017. Learn more.
How to get involved.
What to do if you think you've found a bug.



  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Developer Discussion (Moderator: Donald Lobo) »
  • Passing additional parameters to payment processor back ends
Pages: [1] 2 3

Author Topic: Passing additional parameters to payment processor back ends  (Read 9607 times)

torenware

  • I post frequently
  • ***
  • Posts: 153
  • Karma: 4
Passing additional parameters to payment processor back ends
August 12, 2009, 07:59:00 pm
I have a customer who needs to pass some custom parameters to their payment processor.  It's a political group, and in order to comply with US federal law, certain information needs to be sent on to the FEC.  For this customer, the way they need to do this is by passing a custom parameter containing the required information to the processor (in their case, Authorize.net).   The folks that actually prepare the federal reports then work directly with Authorize.net to pull down the needed information.

I can see that the processor widgets get invoked by (for instance) a doDirectPayment(&$params) method, which gets the parameters using their canonical CiviCRM names.  The problem is that the $params get pretty heavily filtered, and in effect, the parameters passed to the back ends are hard coded, since CRM_Contribute_Form_Contribution_Confirm->postProcess() does not take any parameters, and constructs the $params array itself.

What's the cleanest way to to make sure a custom key/value pair gets passed to the payment processor?

Donald Lobo

  • Administrator
  • I’m (like) Lobo ;)
  • *****
  • Posts: 15963
  • Karma: 470
    • CiviCRM site
  • CiviCRM version: 4.2+
  • CMS version: Drupal 7, Joomla 2.5+
  • MySQL version: 5.5.x
  • PHP version: 5.4.x
Re: Passing additional parameters to payment processor back ends
August 12, 2009, 08:17:35 pm

i dont think there is a clean way for this in 2.2

some hacks will be needed. Wanna research it and figure out a good clean extensible solution for this? this has come up a few times in the past, but in most cases that data should have been passed thru and we fixed the code

lobo
A new CiviCRM Q&A resource needs YOUR help to get started. Visit our StackExchange proposed site, sign up and vote on 5 questions

torenware

  • I post frequently
  • ***
  • Posts: 153
  • Karma: 4
Re: Passing additional parameters to payment processor back ends
August 12, 2009, 09:01:55 pm
I'd be happy to do that.  I'm assuming that 3.0 won't differ too much from 2.2 in this respect, so it makes sense to do exactly that.

A couple questions relevant to this.

There are several places one could inject additional information into the stream.  The simplest (and the first one I looked at) is using hook_civicrm_validate, which gets passed a writable &$fields parameter.  It would be simple enough to "bless" certain field names, and have the code pass them down.  It would be very simple, say, if a field name like "_user_custom" went to an associative array, allowing code to pass down parameters from the validate hook (or different hook if you think it's saner) all the way down to the payment processor code.

From there, whether or not it makes sense to use these custom fields depends a lot on the payment processor, so I think we'd make it an option for a payment processor implementation to call a custom hook with a signature like this:

Code: [Select]
/**
 * Hook definition for altering payment parameters before talking to a payment processor back end.
 *
 * @param string $processor_name
 *    name of the payment processor invoked ('Dummy', 'AuthorizeNet', etc.)
 * @param string $mode
 *    'test' or 'live'
 * @param array $payment_params
 *    array of params as passed to
 * @param string $op
 *    what operation we are doing on the back end ('direct', 'recurring', etc....)
 * @return void
 */
function hook_civicrm_payment_params($processor_name, $mode, &$payment_params, $op='direct');


This seems flexible enough to handle most cases, and would certainly solve my problem.

If this seems like a reasonable approach, I can implement it against the trunk and back-port it to 2.2 for my own purposes.  I'd be happy to implement the hook calls for Dummy and Authorize.net so we have some reference implementations.

Donald Lobo

  • Administrator
  • I’m (like) Lobo ;)
  • *****
  • Posts: 15963
  • Karma: 470
    • CiviCRM site
  • CiviCRM version: 4.2+
  • CMS version: Drupal 7, Joomla 2.5+
  • MySQL version: 5.5.x
  • PHP version: 5.4.x
Re: Passing additional parameters to payment processor back ends
August 13, 2009, 05:37:06 am

that seems a bit hackish :(

how about just a call before we call any of the payment processor functions and (doDirect/doExpress/doTransfer) and send in the parameters we are sending to the paymentProcessor along with the form object. The hook can then examine the form object and modify the paymentParams as needed

lobo
A new CiviCRM Q&A resource needs YOUR help to get started. Visit our StackExchange proposed site, sign up and vote on 5 questions

torenware

  • I post frequently
  • ***
  • Posts: 153
  • Karma: 4
Re: Passing additional parameters to payment processor back ends
August 13, 2009, 10:17:55 am
It is, a bit, but I'm not sure your approach would work.

The problem is that:

  • You need to call the hook _before_ the upstream code has filtered out the information you need to make it.  I'm pretty sure my custom fields are long gone by the time the processor is called
  • You probably need to call that hook _after_ the processor has translated the parameters into its own key names.

In any case, the sort of things you need to do are very specific to which payment processor is called, and whether the processor is called in live or test mode.

I'll do some quick tests to see if I can get around these constraints with your approach.  If enough of the original data is still around when the processor is called, then the hook can do the calculations I'm doing at validate time, and it'll be fine.  Otherwise, we'll need to protect the additional data somehow (perhaps by preserving the raw, unprocessed params in the the Contribution object, and passing them along with the "cooked" params).

Donald Lobo

  • Administrator
  • I’m (like) Lobo ;)
  • *****
  • Posts: 15963
  • Karma: 470
    • CiviCRM site
  • CiviCRM version: 4.2+
  • CMS version: Drupal 7, Joomla 2.5+
  • MySQL version: 5.5.x
  • PHP version: 5.4.x
Re: Passing additional parameters to payment processor back ends
August 13, 2009, 10:30:24 am

the form has all the original POST variables in its session state. I dont think any information is lost

lobo
A new CiviCRM Q&A resource needs YOUR help to get started. Visit our StackExchange proposed site, sign up and vote on 5 questions

torenware

  • I post frequently
  • ***
  • Posts: 153
  • Karma: 4
Re: Passing additional parameters to payment processor back ends
August 13, 2009, 10:41:11 am
I'll take a look at that; I've been looking in the $fields parameter, which gets cleaned up pretty well.   If the $form object is available and still has the info, then I'll make use of it.

torenware

  • I post frequently
  • ***
  • Posts: 153
  • Karma: 4
Re: Passing additional parameters to payment processor back ends
August 14, 2009, 07:30:18 pm
I've had a chance to run this through the debugger a few times.  The hook would need to look like this, and it needs to be implemented _inside_ of the processor code, for reasons that should be clear in a minute.

Calling conventions need to be something like:

Code: [Select]
/**
 * Hook definition for altering payment parameters before talking to a payment processor back end.
 *
 * @param string $processor_name
 *    name of the payment processor invoked ('Dummy', 'AuthorizeNet', etc.)
 * @param string $mode
 *    'test' or 'live'
 * @param array &$raw_params
 *    array of params as passed to to the processor
 * @params array  &$cooked_params
 *     params after the processor code has translated them into its own key/value pairs
 * @param string $op
 *    what operation we are doing on the back end (for now, only 'params'  -- parameter translation)
 * @return void
 */
function hook_civicrm_payment_params($processor_name, $mode, &$raw_params, &$cooked_params, $op='params');

The problem is that $raw_params gets stripped down to the forms from Contribute page, and there isn't a good way to get additional information to the processor -- everything extraneous gets stripped out long before the processor is invoked.

$cooked_params shows the "logical" parameters for the back-end.  A processor plug-in implementing the hook would need to:

  • Do any translations it needs to do from the key values used by CiviContribute to the key values used by the back-end, and put them into $cooked_params.
  • Call the hook.
  • Take the modified $cooked_params and do whatever final processing is needed to set up the call to the processor

Right now, the only thing I'll implement is this "before" translation.  If anybody has a use case where they need to see the response, it would be fairly trivial to add a new op to the hook, and add that implementation.

This is pretty clean, and does what I need it to do.  You can't call higher than the level of the processor, since translation has not yet occurred.  Also, what you do in a hook implementation would vary a lot by processor, and in general,   most folks only deal with one processor in any case.

Do you have any problems with this specification?
« Last Edit: August 14, 2009, 07:32:38 pm by torenware »

Donald Lobo

  • Administrator
  • I’m (like) Lobo ;)
  • *****
  • Posts: 15963
  • Karma: 470
    • CiviCRM site
  • CiviCRM version: 4.2+
  • CMS version: Drupal 7, Joomla 2.5+
  • MySQL version: 5.5.x
  • PHP version: 5.4.x
Re: Passing additional parameters to payment processor back ends
August 14, 2009, 08:38:35 pm

where does this hook get invoked from a civicrm code perspective. i.e. what file(s) and what are the parameters sent

from what little i know of FEC regulations, u basically need name/address/employer info  recorded. I assume u r collecting this via a profile on the contribution page. All this is data is then part of the form object till the transaction is complete. So i'm a bit confused here. maybe a chat on IRC might help clarify things

lobo
A new CiviCRM Q&A resource needs YOUR help to get started. Visit our StackExchange proposed site, sign up and vote on 5 questions

torenware

  • I post frequently
  • ***
  • Posts: 153
  • Karma: 4
Re: Passing additional parameters to payment processor back ends
August 14, 2009, 08:56:56 pm
In this case, there is existing automation which wants a parameter in a particular format.

Most processors have the concept of custom information.  In the case of Authorize.net, any parameter passed to their back end which is not one of their canonical list will be passed through transparently.  Payflow Pro (which you don't support) has a parameter named "custom".  But the way this is handled is processor dependent.

As for when it would need to be called, take a look in the 2.2 tree in CRM/Core/Payment/AuthorizeNet.php, around line 72:
Code: [Select]
        foreach ( $params as $field => $value ) {              //parameters are put into the object here
            $this->_setParam( $field, $value );
        }

        if ( $params['is_recur'] && $params['installments'] > 1 ) {
            return $this->doRecurPayment( $params );
        }

        $postFields = array( );
        $authorizeNetFields = $this->_getAuthorizeNetFields( );  //translation here, returning cooked params
        foreach ( $authorizeNetFields as $field => $value ) {
            $postFields[] = $field . '=' . urlencode( $value );           //hook must be called before here
        }

Basically, I need to get in after $authorizeNetFields has been generated, but before it gets munged two lines later.

torenware

  • I post frequently
  • ***
  • Posts: 153
  • Karma: 4
Re: Passing additional parameters to payment processor back ends
August 16, 2009, 06:34:57 pm
Lobo,

I've done a trial implementation (for Dummy.php, for now).  I wrote it up on my site, partly to show what I'm thinking of, and partly to explain how to get CiviCRM to call a new hook, which I suspect other developers will be interested in.

I'll put the post up on the CiviCRM blog once you and I agree what this hook needs to look like.  I've implemented it for 2.2, but would be happy to roll a patch for the trunk as well.




Donald Lobo

  • Administrator
  • I’m (like) Lobo ;)
  • *****
  • Posts: 15963
  • Karma: 470
    • CiviCRM site
  • CiviCRM version: 4.2+
  • CMS version: Drupal 7, Joomla 2.5+
  • MySQL version: 5.5.x
  • PHP version: 5.4.x
Re: Passing additional parameters to payment processor back ends
August 25, 2009, 12:40:26 pm

another request for a hook here:

http://forum.civicrm.org/index.php/topic,9521.0.html

i think adding the hook within civicrm (which has a handle on the form object) is better than adding it in the PP code. It also simplifies the code for folks who are wiriting payment processors

lobo
A new CiviCRM Q&A resource needs YOUR help to get started. Visit our StackExchange proposed site, sign up and vote on 5 questions

torenware

  • I post frequently
  • ***
  • Posts: 153
  • Karma: 4
Re: Passing additional parameters to payment processor back ends
August 25, 2009, 01:20:14 pm
It would be better if it would work.  But it won't, unless the PPs are rewritten to allow it.   Also:  each PP handles its parameters at the back end in a different fashion.  You can't do this in a general way for all PP.  I'm not sure if you can do it for _any_ that way.   What I'm doing with Authorized simply cannot be done at the form level, but it's trivial with the hook.

Problem is that a good deal of code in the chain wants to clean up the parameters passed, so what you do at the form level will get removed rather rudely by one level of code or another.  And I suspect that some processors may actually need to remove some parameters, or would behave unpredictably if things were not cleaned up.  So the hook certainly needs to be "processor aware".

dmccarney's use case appears to be very similar to ours (in fact, I'm looking at almost exactly his use case down the line). 

We need this hook, it's simple, and it works, now.  It can easily be ported to 3.x.  Will you take it?

Donald Lobo

  • Administrator
  • I’m (like) Lobo ;)
  • *****
  • Posts: 15963
  • Karma: 470
    • CiviCRM site
  • CiviCRM version: 4.2+
  • CMS version: Drupal 7, Joomla 2.5+
  • MySQL version: 5.5.x
  • PHP version: 5.4.x
Re: Passing additional parameters to payment processor back ends
August 26, 2009, 05:01:34 am
hmm, i'm not very sure whether the two approaches are very different, in fact they seem identical.

Either of the two approaches means changing code in both the core and the payment processors. Out of the 10+ payment processors, we only maintain 3 of them. So chances of the hook making it into all the processors is a bit slim

The interface basically dictates what the parameters we send to the PP function. So the hook can add (or remove/change) parameters. Ultimately the PP needs to recognize (or ignore) these parameters and the hook is probably aware what PP is being used.

In your case, you'll modify the authorize.net code to send in the custom data based on the existence of certain values. In the moneris case, the item will be formatted and sent if present.

The one good thing about calling wiht the form object is you get access to all what the form has, which is not possible if the hook is in the PP code

lobo
A new CiviCRM Q&A resource needs YOUR help to get started. Visit our StackExchange proposed site, sign up and vote on 5 questions

torenware

  • I post frequently
  • ***
  • Posts: 153
  • Karma: 4
Re: Passing additional parameters to payment processor back ends
August 27, 2009, 05:33:04 pm
They are different in one significant aspect:  if you call before you invoke the processor, then you are invoking the hook before the processor code has the opportunity to do its own parameter translation between your parameter names and its parameter names (which are set by the back end you need to call).  The needed data structure will not exist that early.

Where exactly this occurs depends on the back end you are calling.  If your back end does not support this, then there is also no reason to call the hook at all.

Passing the form object might make sense, since it carries a lot of state information that would be hard for the processor code to get at otherwise.   But you'd still need the have this after translation occurs, so you'd need to change the calling conventions for the processors.  This isn't hard to do for all of the processors in the tree, since none of the processors need to _use_ the extra parameter; they only need it defined.

Take a look at the enclosed patch, which is on tonight's trunk.  I've patched CRM_Utils_Hook,  the dummy and Authorized processors, and the civitest sample.  The example implementation is essentially identical to what was needed for my application.

Pages: [1] 2 3
  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Developer Discussion (Moderator: Donald Lobo) »
  • Passing additional parameters to payment processor back ends

This forum was archived on 2017-11-26.