Authorize.net returns an error if you try to do a CIM transaction with a $0.00 amount.

This patch just returns successful without calling the gateway when a UC_CREDIT_REFERENCE_TXN has an amount of $0.00.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Island Usurper’s picture

Version: 6.x-2.1 » 6.x-2.2
Status: Needs review » Needs work

At the very least, this change needs to use uc_currency_format() on the $amount instead of just printing "$0.00".

I wonder if there shouldn't be a more generic way to deal with $0 value orders. It seems like there shouldn't necessarily be a payment record at all, but the customer should still receive what they ordered. On the other hand, a lot of people make use of the "payment entered" trigger in Conditional Actions to make important things happen, like granting access to files.

xibun’s picture

I'm also interested in a generic solution. I'm thinking of an order with a 100% discount coupon (= gift). such an order should go automatically into "Payment received" state. but it shouldn't do so quietly - I think it should use a "free" payment method and record the state change and "payment" as usual.

univate’s picture

Having payment gateways able to accept $0 is very useful for recurring payments where the first payment is a trial, but it shouldn't change the payment gateway as that will likely effect the ability to accept future payments.

RSTaylor’s picture

Status: Needs work » Needs review
FileSize
739 bytes

Revised to use uc_currency_format() and log an order comment like the one _uc_authorizenet_cim_profile_charge() would log on success.

webservant316’s picture

what is the end of this discussion? I am currently trying to checkout with a $0 order and when the user chooses the credit card payment option they cannot checkout. It would be fine with me if the authorize.net validation would validate even with a $0 order, or if ubercart realized it did not need to validate. However, a failed authorization is a problem.

What is the recommended solution?

TR’s picture

@webmaster_prwa: You, as a member of the community, have a need for this feature, so you should apply the patch and see if it solves your problem, then provide feedback in this thread. That's what the "needs review" status means.

@RSTaylor: There is an alternative patch in #787856: Can't charge $0 - A valid amount is required.. Can you comment on how that differs from your patch?

webservant316’s picture

sorry bout that. will do.

mr.andrey’s picture

subscribing

EvanDonovan’s picture

Version: 6.x-2.2 » 6.x-2.x-dev

This has become a critical feature for me on a client project. Thus the following questions, from my testing of the patch in #4:

  • I just tested this patch when making an order with a coupon discount of 100%. I still got the following error: "Payment failed for order 153: Credit card payment declined: A valid amount is required." Perhaps $amount is not actually 0.00 in this case?
  • It looks like, in any case, this simply bypasses the usual logic to create the CIM profile, so recurring charges with the CIM profile would not be possible. Is this correct? If so, is there an alternative solution?
TR’s picture

@EvanDonovan: You're the first person to provide any sort of review since the patch was first posted over a year ago. Before anything like this gets committed to Ubercart, it needs to get reviewed and approved by people actually using Authorize.net CIM. I'm not one of those people.

Did you also look at the solution in the duplicate issue #787856: Can't charge $0 - A valid amount is required. ? My question in #6 was how do these patches differ in their approach to solving the problem. If one works and the other doesn't, that's a big difference ...

TR’s picture

Status: Needs review » Needs work

Changing status to reflect comment #9.

EvanDonovan’s picture

@TR: It looks like the patch in #2 of the issue flagged duplicate: a) patches a different function (uc_authorizenet_charge), and b) actually calls _uc_authorizenet_cim_profile_create() to create the CIM profile.

This would seem to be the desired approach, not #4.

EvanDonovan’s picture

I just tested the patch in #2 of the duplicate issue and it worked in Ubercart 2.4 using Authorize.net CIM when a coupon code had been applied that made the order amount $0.

I will confirm later whether it also shows a CIM profile as having been created in Authorize.net. The patch still applies from inside the uc_authorizenet directory, with fuzz, but maybe should be re-rolled as a Git patch and put on here.

EvanDonovan’s picture

I just checked, and can confirm that the patch in #787856-2: Can't charge $0 - A valid amount is required. successfully allows a $0 CIM transaction to create, and the CIM profile to get created. The patch in this issue does neither.

Thus, I think a re-roll of that patch, and adding it back to this issue is in order. As far as I can tell, that patch, with no further work, would be sufficient to resolve this issue. Unfortunately, I don't have time for this at the moment, but may come back to it.

krlucas’s picture

Status: Needs work » Needs review
FileSize
2.23 KB

Note to self, search the issue queue before writing your own patch :)

My patch is very similar but takes into account some things that the others don't:

  • CIM is an optional Auth.net add-on so like "Always create a CIM profile..." this should be an admin setting.
  • AFAIK Auth.net never allows zero amounts for any sort of authorization or capture--so why even bother attempting the charge.
  • The additions simply set the txn_type to UC_CREDIT_REFERENCE_SET allowing the extant logic to work as normal.

Also, so far as I can tell, uc_credit.module squashes any failure message returned by the payment gateway. The fail message is included for consistency and for those implementations that call uc_authnet_charge directly (uc_recurring).

j0rd’s picture

The proper way to do a short free trial (under 2 weeks), is to do a Authorization for the recurring amount, but do not capture the amount until the subscription renews. So say you have a 3 day free trial, when $20 for 1 month after that, the transaction ideally should work as such.

Initial Transaction is a $20 Authorization. After 3 days, if the user has not cancelled, you Capture the $20 Authorization and give them access for the next month. Every month after that, is your standard Auth+Capture transaction.

This ensures that if the subscription were to convert, you can 100% guarantee to capture the $20. This also keeps users which have say only $5 on their credit cards, from being denied a free trial. Both of these are wins for the site operator.

I'm not sure if uc_recurring is smart enough to handle these types of transaction flows though, but it really should. I've discussed this before in the past when Univate was implementing version 2 of uc_subscriptions, but I'm not sure if it was implemented.

Additionally for this to work, each processor would have to implement Capture, Auth and Auth Capture transaction types separately. It's usually only a couple minutes of additional work. Usually the difference between an Auth and a Capture is one field in the query string. If the processor stores the credit card as an ID (like I believe CIM does), you need to start keeping track of that ID as well and send it with the capture. This process is all fairly standard with minor deviations between processors and can easily be abstracted.

As for processor support, in the past when I created a massive proxy biller (integrate with us, then you integrate with ~24 other processors, kinda like recurly.com) we only ran into one processors which had a short Auth to Capture window (I think it was 48 or 72 hours) but most are fairly long (~10 to 30 days). Ideally this Auth to Capture time window would be defined with in the processor implementation as well, so uc_recurring would only allow you to choose an appropriate processor for the length of your trial window. Additionally all processors would need to define if they support individual transaction methods (Auth Only, Capture Only & Auth+Capture). They should also implement Void if possible as well, so you can void the captures of cancelled free trials. Otherwise the processor will do this auto-magically after the Auth window has expired.

Not sure if this is off topic, but I figured I'd let you guys know about the ideal way to handle these types of transactions.

Status: Needs review » Needs work

The last submitted patch, uc_authorizenet-better-zero-amount-handling-691926.patch, failed testing.

univate’s picture

The problem with using doing an authorization for a trial period on a subscription is that if your potential customer is promised a "risk free trial, cancel anytime" its not ideal to hit their credit card and have something show up on in their online banking credit card statements - which often an entry like "pending amount" will appear on their account - so I'm not sure I would agree that this method is alway ideal.

On the other hand authorizations are great to check that the details you have received are actually correct and people aren't supplying bogus details.

As for uc_recurring, I have not made any assumption about the features supported in specific payment gateways. Its really up to each gateway module to define what happens when a subscription is setup and then what happens on renewals. So uc_recurring should not get in your way if you want to setup authorizations to happen the payment gateway level on $0 initial transaction.

mattcasey’s picture

sub

codekarate’s picture

I can confirm that Can't charge $0 - A valid amount is required. appears to be working for me.

mattcasey’s picture

Just one caveat: using CIM with UC Recurring or any automated order process will not go through. The request sent to Authorize.net includes a Credit card of only the last 4 digits. In our case, we allow subscribers to receive credit so sometimes their renewals are free. The OP's solution to just return a message seems to work allright.

mattcasey’s picture

The solution in #60 and #20 almost works, but it will try to create a new profile for existing customers.
Here's what I am using with uc_recurring, use cases are a first month free coupon and subscription gifts that are paid up front but have renewal orders that should still be recorded, line 250 of uc_authorizenet.module:

// Main handler for processing credit card transactions.
function uc_authorizenet_charge($order_id, $amount, $data) {
  // Load the order.
  $order = uc_order_load($order_id);

  // Skip charge if amount is 0.00
  if ($amount == 0) {
    if (!empty($data['ref_id'])) {
      // Return true if a profile already exists
      global $user;
      $comment = '<b>'. t('Reference transaction') .':</b> '. uc_currency_format($amount);
      uc_order_comment_save($order_id, $user->uid, $comment, 'admin');
      return array('success' => TRUE, 'message' => $comment);      
    }
    elseif ($message = _uc_authorizenet_cim_profile_create($order)) {
      // Return the error message if this failed.
      return array('success' => FALSE, 'message' => $message);
    }
    else {
      return array('success' => TRUE, 'message' => t('New customer profile created successfully at Authorize.Net.'));
    }
  }

  // Perform the appropriate action based on the transaction type.
  switch ($data['txn_type']) {
    // Reference transactions are handled through Authorize.Net's CIM.
    case UC_CREDIT_REFERENCE_TXN:
      return _uc_authorizenet_cim_profile_charge($order, $amount, $data);

    // Set a reference only.
    case UC_CREDIT_REFERENCE_SET:
      // Return the error message if this failed.
      if ($message = _uc_authorizenet_cim_profile_create($order)) {
        return array('success' => FALSE, 'message' => $message);
      }
      else {
        return array('success' => TRUE, 'message' => t('New customer profile created successfully at Authorize.Net.'));
      }

    // Accommodate all other transaction types.
    default:
      return _uc_authorizenet_charge($order, $amount, $data);
  }
}
jerry’s picture

Has anyone worked on a similar solution for D7/UC3 yet?

longwave’s picture

https://drupal.org/project/uc_authnet has a D7 release, but I have no idea whether it allows this feature or not. Not sure whether it's worth closing all Authorize.net issues in favour of using that module instead; I personally don't use Authorize.net.

TR’s picture

Version: 6.x-2.x-dev » 8.x-4.x-dev
Issue summary: View changes
Status: Needs work » Postponed

Moving forward, as D6 is obsolete and there's still no working patch here.
The D8 version is blocked on #2656634: Port uc_authorizenet to D8.

TR’s picture

Component: Code » Payment