OK, I'd like to put this problem to bed. There are heaps of patches that half address the problem, and it's all very confusing.

First I'll make these threads duplicate, and point them to here:
http://drupal.org/node/33750
http://drupal.org/node/49933
http://drupal.org/node/51321
http://drupal.org/node/51796

Then I'll follow up with my analysis.

Comments

sime’s picture

Hope the following is straigh-forward enough.

The issue

The original problem is code like this:
module_invoke($txn->payment_method, 'paymentapi', $txn, 'on checkout');

The problem is that $txn->payment_method is set by the _paymentapi operation called "display name". For example ccard sets it to "Credit Card" and autorize_net sets it to "Pay with credit card". Hence the module_invoke() is being asked to invoke functions like. Credit Card_paymentapi()


Solution in Progress
The existing solution (partly patched) is to change:
module_invoke($txn->payment_method, 'paymentapi', $txn, 'on checkout');
into:
module_invoke(payment_get_modulename($txn->payment_method), 'paymentapi', $txn, 'on checkout');

payment_get_modulename() asks each module with a _name() function what the 'display name' of the module is. When a match is made then payment_get_modulename() returns the module name.

payment_get_modulename (actually a patch from here)

function payment_get_modulename($name) {  
    foreach (module_list() as $pm) {
      if (function_exists($pm. '_name')) {
         if($name == ($modulename = module_invoke($pm, 'name', $name)))
            return $modulename;
        }
     }  
  return $name;   
}

Here is an example _name() function:

function paypalpro_name() {
  return t('PayPalPro');
}

OK, the problem I found with the above solution is that 1) most ec modules don't have a _name() function. And 2), _name() does exactly the same thing as _paymentapi(NULL,'display name')

So, rather than going around all the modules ands creating _name() functions. I suggest we just change the payment_get_modulename() to this instead:

function payment_get_modulename(&$display_name) {  
    foreach (module_list() as $module_name) {
      if (function_exists($module_name. '_paymentapi')) {
         if($display_name == module_invoke($module_name, 'paymentapi', NULL, 'display name'))
            return $module_name;
      }
    }  
  return $name;   
}


Alternative fix
Some have mentioned that this could all be solved if the current payment module name was captured in the transaction variable. This requires a couple of changes, although nothing unreasonable and I think it is the way to go. The problem with doing this is that we have an existing solution partly implemented which should be rolled back.
Simon

neclimdul’s picture

Good Post! That sums up a lot of what I've said in some of those post exceedingly well.

I'd like to make 2 more small arguement for said alternative solution. If 2 methods where to use the same display name there could be issues. Granted, this is somewhat contrived. Why are you going to confuse your customer with 2 "Checkout with Credit Card" payment options? But still, its something that breaks the current model.
The second I think I put in one of the above listed posts but I'll rehash it. The function listed above is expensive in comparison. Iterate through all the modules(which on a large site might be quite a few) or just have the variable available to you.

I'll work on a patch for the alternatice solution tomorrow so people can review the changes needed! Lets put this bug to bed!

neclimdul’s picture

oh and one other thing, module_invoke checks if the function_exists so we might as well use:

function payment_get_modulename(&$display_name) {
  foreach (module_list() as $module_name) {
    if ($display_name == module_invoke($module_name, 'paymentapi', NULL, 'display name'))
      return $module_name;
    }
  }
  return $name;
}

or at least use module_hook instead of function_exists for clarity's sake

sime’s picture

Thanks for having a close look.

oh and one other thing, module_invoke checks if the function_exists so we might as well use:

Agreed. Although b/t you and I we have 2 votes for the alternative method ;-)

I'll work on a patch for the alternative solution tomorrow so people can review the changes needed! Lets put this bug to bed!

If you haven't already started, here's what I have worked out so far:

1) line 512 in payment.module (cvs) goes from
$options[$method_value] = '';
to
$options[$name] = '';.
Which results in the radio button having a "value" attribute equivalent to the module name.

2) lines 517 & 523 in payment.module (cvs) goes from
$form['payment_method'] = array(
to
$form['payment_module'] = array(
Which will result in a $_POST var called "payment_module".

3) as soon as the $_POST var hits the next page, whereever that is, a 'payment_method' variable needs to be created so that other code that relies on it doesn't break. At that point we would have within $txn or $edit, $txn->payment_module and $txn->payment_method available for the duration of the cart.

I'm happy to continue to make the patch. Just let me know so we don't duplicate effort.
Simon

neclimdul’s picture

I've got no problem tackling the patch.

There are quite a few references in the store.module to payment_method. I think this is where your suggestion to leave that in the transaction variable might be key. That's is also where its going to be hardest to track whether any changes are going to need to be made I believe.

The reason is that we store the payment_method in the ec_transaction table in the database. If somewhere that's being used to call the api that someone is going to get broken because it's going to be a display name now. However if we decide to hack it and store the payment_module in that field, we're going to run into trouble with outputing history if a payment type module is removed.

So, the decisions we need to make are, are we going to need to call the api later? and if so well need to store the payment_mdoule so do we need to store the payment display name or do we want another solution to the module removal like falling back to the modulename? Also in consideration for these decisions: Is the 4.7 update process a valid reason to change our table layout?

But, other then that I think I've got it working pretty well right now though.

neclimdul’s picture

StatusFileSize
new6.66 KB

OK I think this is a pretty comprehensive patch of all the modules not in the contrib directory to implement the use of payment_module over payment_method. I can make a patch for the contrib modules later but we'll see if this sticks first.

I also to the liberty of fixing up one of the function I found along the way. The fix is both on a minor page and only a cleanup but its in the patch. Here's the summary.
payment_admin_overview has the following code in it:

  foreach (payment_get_methods() as $name) {
    if (function_exists($name. "_paymentapi")) {
      $settings = (function_exists($name. "_settings")) ? "[". l(t("settings"), "admin/settings/$name") ."]" : "";
      $help = $name. "_help";
      $desc = (function_exists($help)) ? $help("admin/modules#description") : "";
      $output .= "<p><strong>$name</strong> $settings<br />$desc</p>";
    }
  }

But, payment_get_methods only returns modules that implement the paymentapi hook. so that if isn't needed. Also it would seem better practice to use module_hook and module_invoke rather than calling the actual function exists functions. The functionality is the same but we end up with the following code:

  foreach (payment_get_methods() as $name) {
    $settings = module_hook($name,'settings') ? "[". l(t("settings"), "admin/settings/$name") ."]" : "";
    $desc = module_invoke($name,'help','admin/modules#description');
    $output .= "<p><strong>$name</strong> $settings<br />$desc</p>";
  }

Like I said, exceedingly minor but better code IMHO.

neclimdul’s picture

And one more piece of reference material for everyone. This is the output of grep -n payment_method */* */*/* after the above patch is applied.
This might be useful for finding issues and for contrib people that want to make the update to the stuff in the patch if/when it goes through.

neclimdul’s picture

StatusFileSize
new2.93 KB

And since I'm forgetful, here's the mentioned file

sime’s picture

Thanks neclimdul. That was a speedy turn-around and it looks very comprehensive. I will test it before tomorrow. I like that you've posted the grep results so that ppl can see what might be effected. (Try this: grep -nR payment_method *)

As for the other change you've added into the patch - I find that a little disconcerting. The change is probably fine, but why not create another thread? Of course, the less patches the better, so maybe it's just my opinion! Either way, it's worth considering that CVS/Drupal newbies like me have to do a double-take and read more carefully. ;-)
Simon

neclimdul’s picture

Status: Active » Needs review
StatusFileSize
new5.6 KB

Yeah your probably right. Trying not to mow over some of the development. Here's the patch with out the function change.

sime’s picture

AFAICT, this patch works. I successfully navigated 4 payment methods (paypal/ccard/authorize_net/exact) through to the payment page. Thanks neclimdul.

I'd like to see this applied/verified soon if possible. This is an important patch IMO.
Simon

gordon’s picture

I have fixed this in a much simpler method. This was actually a bug that I introduced when I updated the payment.module which meant that $txn->payment_method was the display name instead of the module name.

I have made a 1 line fix and the payment_method will hold the payment module.

sime’s picture

Component: other » payment.module
Status: Needs review » Fixed

I feel much better about this now. You've nipped it in the bud. IMO, there will be some further minor changes needed (like removing payment_get_modulename), but that can now be dealt with in simple tickets.
Simon

PS
I can't believe how intense this whole CVS experience is - it's like taking uppers and downers at the same time. I'm taking a couple of days off! X-0

gordon’s picture

I have removed that now.

Anonymous’s picture

Status: Fixed » Closed (fixed)