I’ve had a lot of fun the past week with the payment modules; not only working on post-transaction actions, but also on the backend API side of things and have ultimately split the way the modules are structured.
Similar to work being done on combining the catalog and administration tool files, there are now two main files to a payment module, one for the catalog side taking care of the initial transaction for a new order, and a second file for the administration tool taking care of installation, removal, and post-transaction actions. The second set of module functions are never used nor needed on the catalog side, so moving these over to the administration tool side cleans up the API and adds in an extra level of security. This also brings in a performance increase on the catalog side, but won’t be seen
(hey, there’s less inclusion of code going on here
)
The modules that communicate with their gateways via a HTTPS request can return the response with just one line of code:
$this->_transaction_response = $this->sendTransactionToGateway('https://test.authorize.net:443/gateway/transact.dll', $post_string);
(the sendTransactionToGateway() class method is part of osC_Payment, so it is available to all payment modules)
The cool thing about this function is that it falls back nicely onto what the server is capable of doing to make that HTTPS request. The first thing that is tried is using the native PHP functions when OpenSSL is built with the PHP installation (PHP 4.3+ feature). If this isn’t available, it falls back to using libcurl, which is “curl” built into the PHP installation, and if the server doesn’t support that, it automatically falls back to using the cURL binary file that should exist on the server.
I really do not like the cURL binary file method as the credit card details are passed to it via exec(), which can then be seen on the server via the “ps” command. If this is the case for your shared webserver, I’d recommend changing hosts if you want to accept credit cards this way, as it can be insecure.*
* Some modules are able to pass the parameters encrypted to “curl” (using exec()) but is unfortunately not common.
One problem that I wanted to tackle properly with the payment modules API update, was to take care of the cat and mouse game of the order being created after a payment request to the gateway has been made, specifically the desire and need of passing the order ID to the gateway for its intended financial transaction.
The problem here is that the order ID does not exist at this stage as the order has not been created yet. It was common to pass just a randomized value to the gateway but had no official relation to the order as this value was not being stored with the creation of the order.
With my thinking cap on, I would have liked to have solved this problem by using database sequences, similar to MySQL’s auto_increment feature, as it is not possible to neatly grab the current (or next) auto_increment value for a specific table before an insert has been performed (argh; cat and mouse game). But regardless of using auto_increment or sequences, the database tables involved of storing a new order would need to be locked so that the relationships between the tables match the order being created (ie, osc_orders and osc_orders_products; by orders_id).
And this is what the ultimate problem is: unfortunately not all hosting providers have granted their customers with LOCK privileges.
No, I did not have to ring every hosting provider in the world to ask if they provide LOCK privileges to their customers or not, but did some research and came across this problem while searching for alternative methods. One of the projects that are experiencing this problem is Drupal:
http://drupal.org/node/39460
They use the more neutral (or standard) approach of using sequences over MySQL’s auto_increment feature, and have been experiencing this problem with their releases.
As osCommerce is not yet officially database independent (although the API for it is just about there), and we are focusing on compatibility with MySQL 3.x -> 5.x with MyISAM and InnoDB tables, I took my thinking cap off and decided to keep it simple, yet still secure, to have this work on each and every MySQL database server out there (thank you MySQL drawbacks!
) and keep everyone happy.
The solution that has been implemented is to create that random order ID value, make it unique, and to store that with the order when it is created. That way there is an official relationship between an order and a transaction that can be seen. Whether or not the ID passed to the payment gateway is the order ID or another ID does not (or should not) really matter; the important thing here is that a relationship between the order and transaction exists, and prevents duplicate orders from being sent (another reason some payment gateways require an order ID to be sent to them).
This has no affect on the post-transaction actions side of things (eg, capturing an authorized transaction), as the gateways return their own transaction ID which is used to identify the transaction for post-transaction actions.
That was my thinking-cap experience for the week, and had fun finalizing the modules I got started on and starting on new ones. The certification from the payment service providers will begin next week, and look forward to the results this will bring.
The communication here to the payment service providers has been great, with all being anxious in seeing and certifying the new modules.
I will post another video of what is possible now with the post-transaction actions in the coming days, so stay tuned!