Building a payment method for ecommerce - question about transaction references

Permalink
I've finished building a payment method for ecommerce, for SecurePay (http://www.securepay.com.au/) and I'm thinking I'll release this on the marketplace. There's already a add-on for this gateway in the marketplace, but my add-on uses a different/newer interface (the 'Direct Post' method that removes the need for PCI compliance).

All is working well, but the only thing I haven't been able to decide on is what to pass the payment gateway in terms of a 'reference id' for a transaction. The reference id is used to identify transactions when you log into the payment gateway's administration. Ideally you want to be able to use this to be able to match up transactions in concrete5 and in the gateway.

I realised that you can't use the Order # that ecommerce generates, as this is created AFTER you process a successful payment (and I don't have a way to the update details for an already processed payment). Using the order ID could work, but this number isn't used anywhere in the ecommerce dashboard pages and when displaying order details. (it's visible on the URL, but that's not user friendly)

So I'm thinking that the most useful and unique value then would be the user's email address. This then can be matched up, and if there are multiple orders from the same customer, the timestamp and value can be used to identify them further.

Can anyone provide any advice here? What do other people use when they build payment methods?

Cheers
-Ryan

mesuva
 
jordanlev replied on at Permalink Best Answer Reply
jordanlev
Hey Ryan,
I haven't done this myself, but I wonder if creating a new order attribute would make sense for this purpose. Then you could use whatever ID number the payment gateway gives you as the reference number, and save it with the order in that custom attribute. This also has the benefit of the user being able to find those numbers when needed (unlike the order ID).

By the way, can you explain how you got the "Direct Post" method to work with the ecommerce workflow? I was thinking about implementing this for Stripe a while back, but never got around to it -- but I was doing a thought experiment on how exactly it could fit into the eCommerce's way of doing things, and never quite figured it out.

-Jordan
mesuva replied on at Permalink Reply
mesuva
Thanks Jordan,

That sounds like an excellent way to manage this, I just didn't think of creating a new order attribute (although I did look for an existing one to use). I sort of forgot that the payment method is still set up as a package, so I can create additional order attributes when it's installed. This particularly gateway doesn't actually generate some sort of transaction ID, you have to supply that yourself, so I may simply use the order ID in some way.

The direct posting model for payment seems to have the same common method of creating a form that posts to a secure address elsewhere, with you being expected to receive back a post or get request, meaning CC details are never posted directly to your site. Around this though, different gateways expect you to do different things, some expect pre and pos-transaction SOAP requests, while others are a bit simpler.

The SecurePay one I've just finished is luckily more on the simpler side. It would actually be a good starting point for another gateway (as it's simpler than the included methods), so I'd be happy to provide you with that if you wanted it.

One major difference with this way of processing is that because you never post CC data to your own site, you can't use any server side validation of credit card numbers or CCV fields. So for my method I've had to add a fair bit of javascript validation to the form, to strip out characters, check the length, do a checksum, etc. I'm sure they all handle it differently but SecurePay will simply display an error message if you pass it a CC number that is invalid data wise, as opposed to do the full redirect, so you have to ensure fields conform to their spec before sending them.

In essence:
- The form() method of the payment method controller is used to set up the post fields for your credit card form. Along with the card fields, it would include hidden fields for your merchant id, payment amount, etc. Along with this, for SecurePay for example, you generate a 'fingerprint' hash, which combines the form values with a private password (known to both you and the gateway). The gateway checks this for form tampering.
- In the form you also specify a return URL. In the concrete5 code, for this I've simply used $this->action('return') to generate this url.
- When you post the form, the gateway will do its thing and then redirect the browser to your return URL, along with either GET or POST data. This data has success or failure codes for you to check. SecurePay at least provides a further fingerprint that you test to check for URL tampering.
- Above where I said $this->action('return'), concrete5 will look to use in the method controller the method action_return(). In this method, you check the response, either finalise the order and do a redirect, or work out the error message and do a redirect back to the credit card form page with the error message to display. I was worried that the user would see a messy url with lots of data in it, but as it's really just a processing step that has no output, they never see it (or it's too fast to see).

So for the success, you would do a redirect like:
// if trans approved... (this is simplified)
$o->setStatus(CoreCommerceOrder::STATUS_AUTHORIZED);
parent::finishOrder($o, 'SecurePay');
$ch = Loader::helper('/checkout/step', 'core_commerce');
$steps = $ch->getSteps()
$ch->setCurrentPagePath('/checkout/payment/form');
$sch = $ch->getNextCheckoutStep();      
$this->redirect($sch->getRedirectURL());


but if it fails
$err = 'You stole this card';
$this->redirect('/checkout/payment/form?error='.urlencode($err));

The error will appear above the credit card form again.

For this, the controller is actually pretty stripped down and simple, with half it to do with managing the config of the gateway, rather than the process itself.

The next gateway I'm working on needs to do extra SOAP requests, but really they'll just occur in the form() and action_return() methods, as they're really the before and after steps of the transaction. I much prefer this method myself as it removes the whole PCI worry.

Hope that's helpful.