Jump to content
  • Checkout
  • Login
  • Get in touch

osCommerce

The e-commerce.

Eskil

Archived
  • Posts

    4
  • Joined

  • Last visited

Profile Information

  • Real Name
    Eskil Hauge
  • Location
    Norway
  • Website

Eskil's Achievements

  1. This is a lengthy post. This post is meant for anyone who wants to create/implement their custom payment module. The contents of this post hopefully answers many question anyone has. It is the result of my own acquired understanding of how osCommerce payment modules work. I appreciate any comments if you find this post useful. Please reply to this post if you identify any mistakes in my post or want to elaborate on the topic. Background: I originally replied to a post on how to create a module and received some PMs asking me to elaborate on this. In this post I will include that same overview with extended explanations and sample code. This post does not attempt to explain how to write a module, as there are many considerations one needs to take and each payment gateway is different. I will outline the standard functions in a module that are required and when and how they are called/used. I assume we are using a gateway which requires a http-post redirect. First, a simple overview of the standard/required payment module functions (the standard PayPal module or cc module are good references as they implement standard functions and have minimal code): PAYMENT MODULE STRUCTURE Function name Description Calling script class constructor Sets initial member variables and module status (enabled/disabled). Define the form_action_url variable which is your gateway url (that will recieve the POST data). Include any static variables you may require in your code here. update_status() Here you can implement using payment zones (refer to standard PayPal module as reference). Called by module's class constructor, checkout_confirmation.php, checkout_process.php javascript_validation() Here you may define client side javascript that will verify any input fields you use in the payment method selection page. Refer to standard cc module as reference (cc.php). Called by checkout_payment.php selection() This function outputs the payment method title/text and if required, the input fields. Called by checkout_payment.php pre_confirmation_check() Use this function implement any checks of any conditions after payment method has been selected. You most probably don't need to implement anything here. Called by checkout_confirmation.php before any page output. confirmation() Implement any checks or processing on the order information before proceeding to payment confirmation. You most probably don't need to implement anything here. Called by checkout_confirmation.php process_button() Outputs the html form hidden elements sent as POST data to the payment gateway. Called by checkout_confirmation.php before_process() This is where you will implement any payment verification. This function can be quite complicated to implement. I will elaborate in this later in this post. Called by checkout_process.php before order is finalised after_process() Here you may implement any post proessing of the payment/order after the order has been finalised. At this point you now have a reference to the created osCommerce order id and you would typically update any custom database tables you may have for your module. You most probably don't need to implement anything here. Called by checkout_process.php after order is finalised get_error() For more advanced error handling. When your module logic returns any errors you will redirect to checkout_payment.php with some error information. When implemented corretly, this function can be used to genereate the proper error texts for particular errors. The redirect must be formatted like this: tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'payment_error=' . $this->code.'&error='.urlencode('some error'), 'NONSSL', true, false)); I will examplify this in my sample code later in this post (the standard PayPal module does not implement this method). Called by checkout_payment.php check() Standard functionlity for osCommerce to see if the module is installed. install() This is where you define module's configurations (displayed in admin). remove() Standard functionality to uninstall the module. keys() This array must include all the configuration setting keys defined in your install() function. PAYMENT VERIFICATION A good payment gateway (and payment module) should allow for payment verification. Without any form of payment verification your module is open for url-hacking. This is a very simple consept where a malicious user simply pastes the success url of the e-commerce system (in this case checkout_process.php) and the order is completed assuming the payment has been completed. You can try this by installing the standard PayPal module, completing the order as far as not confirming the order. Then paste in the full url for checkout_process.php for your server. I will with the following sample code show how to implement payment verification. There are probably numerous ways this may be done. I assume a simple scenario where the payment information is verified by the gateway by re-submitting the data first recieved from the gateway. The information is first checked by the payment module (simple verification) and then re-submitted back the gateway for verification (advanced verification). A result code is passed back to the module. This communication is passed on an underlying layer using secure sockets (SSL) and is invisible to the user (no re-directs). My sample uses cURL to enable underlying connections over SSL. Note: The module's internal check of the payment data requires to compare the payment amount passed from the gateway with the order amount. To achieve this the $order_totals object must be instantiated from within the module and requires a little hack/work around to be able to complete the order processing: the rest of the code in checkout_process.php from the point where the module's before_process() function is called, must be executed from within this module function. This is related to the chicken&egg problem described here . I have made notes in the sample code where this occurs. (I discovered this problem when tax was added to shipping. The payment amounts would differ.) Listing 1: usage of before_process() to verify a payment. function before_process() { global $HTTP_POST_VARS, $cart, $order, $currency, $currencies, $customer_id; // return error if key values are missing if(!isset($HTTP_POST_VARS['PaymentID']) || !isset($HTTP_POST_VARS['OrderID']) || !isset($HTTP_POST_VARS['Amount']) ) tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'error_message=' . urlencode('No payment information found.'), 'NONSSL', true, false)); $in_paymentID = ''; $in_amount = ''; $in_orderID = ''; $in_currency = ''; // read returned data from gateway $in_paymentID = urldecode($HTTP_POST_VARS['PaymentID']); $in_amount = urldecode($HTTP_POST_VARS['Amount']); $in_orderID = urldecode($HTTP_POST_VARS['OrderID']); $in_currency = urldecode($HTTP_POST_VARS['Currency']); // add PaymentID as comment to order $order->info['comments'] .= "\nPaymentID: ". $in_paymentID; /* From checkout_process.php: create $order_totals object, otherwise $order object will have incorrect data * This is why the rest of checkout_process.php must be included in this function. (see end of function def) * The $order_totalts object needs to be instantiated here and cannot be instantiated twice! */ require(DIR_WS_CLASSES . 'order_total.php'); $order_total_modules = new order_total; $order_totals = $order_total_modules->process(); // Check that values are untampered (simple verification). // Note: get_orderno is an added custom function to generate an orderid as an osc order has no id until it has been completely processed.... if( ($in_amount != number_format($order->info['total'] * $currencies->get_value($currency), $currencies->get_decimal_places($currency), '.', '')) || ($in_orderID != $this->get_orderno($customer_id)) || ($in_currency != $currency) ) ){ tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'error_message=' . urlencode('Data has been tampered! Payment aborted.'), 'NONSSL', true, false)); } /* Verify transaction with gateway. * The actual verification is defined in a separate function (validate_payment()). * This code assumes using cURL for posting data over SSL. */ $this->validate_payment( $HTTP_POST_VARS ); // Processing continues here if there is no error /* Here we continue the processing that otherwise is done by checkout_process.php. * Include everything in checkout_process starting from line #57 * Alternatively extract that code into an include file (i.e include('module_checkout_process.inc.php'); ) */ include('sample_module_checkout_process.inc.php'); } Lising 2: The payment validation method /* Payment verification * Note that this processing is an example only. All gateways are different. * This function shows how to post data back to the gateway. * * The recieved payment data are posted back to the gateway to verify that they are correct. * Gateway verifies amount, currency, transaction_id and order_id. * Gateway returns 0 for success. * * Also make note of how the errors are handled. * The error information can include your own codes that later are interpreted by the module's get_error() function. */ function validate_payment( $request ){ $postfields = null; foreach($request as $key=>$value){ if($key && $value){ $postfields.=$key."=".urlencode($value)."&"; } } curl_setopt($ch, CURLOPT_URL, 'https://insert.gateway.verification.url.here'); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); curl_setopt($ch, CURLOPT_HEADER, 0); // ignore http headers curl_setopt($ch, CURLOPT_TIMEOUT, 10); //returns error 28 curl_setopt($ch, CURLOPT_FAILONERROR, 1); //returns error 22 on HTTP error (http statuscode >= 300) curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // Post data back to gateway $result = curl_exec($ch); // check for error if (curl_errno($ch) != CURLE_OK) { $result['errno'] = curl_errno($ch); $result['errstr'] = curl_error($ch); } curl_close ($ch); // process verification result // if result is array then a CURL error has occurred if(is_array($result) && isset($result['errno'])){ $errornumber = $result['errno']; $errortext = $result['errstr']; $this->luup_debug('Curl or socket error: ', $errornumber . ' ' . $errortext); switch($errornumber){ case CURLE_HTTP_RETURNED_ERROR: // HTTP error tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'payment_error=' . $this->code.'&error='.urlencode($errortext), 'NONSSL', true, false)); break; case CURLE_OPERATION_TIMEOUTED: tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'payment_error=' . $this->code.'&error='.urlencode('TIMEOUT'), 'NONSSL', true, false)); break; case 0: // connection failure tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'payment_error=' . $this->code.'&error='.urlencode('CONNECT_FAIL'), 'NONSSL', true, false)); break; default: // curl/socket error tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'payment_error=' . $this->code.'&error='.urlencode($errornumber.': '.$errortext), 'NONSSL', true, false)); break; } } else{ // is the payment verified? if($result != '0'){ // payment is not ok tep_redirect(tep_href_link(FILENAME_CHECKOUT_PAYMENT, 'payment_error='.$this->code.'&error='.urlencode($result), 'NONSSL', true, false)); } // payment is ok return true; } Listing 3: Formatting errors // displays/formats error function get_error() { global $HTTP_GET_VARS, $language; // ensure we have all texts defined require_once(DIR_WS_LANGUAGES . $language . '/modules/payment/'.$this->code.'.php'); $error = ''; $error_text['title'] = MODULE_PAYMENT_SAMPLE_TEXT_ERROR_TITLE; if(isset($HTTP_GET_VARS['error'])) $error = urldecode($HTTP_GET_VARS['error']); // otherwise default error is displayed switch($error){ case 'TIMEOUT': $error_text['error'] = MODULE_PAYMENT_SAMPLE_TEXT_ERROR_TIMEOUT; break; case 'CONNECT_FAIL': $error_text['error'] = MODULE_PAYMENT_LUUP_SAMPLE_ERROR_CONNECT_FAIL; break; /* include additional handling for gateway specific errors here */ default: //other error $error_text['error'] = MODULE_PAYMENT_SAMPLE_TEXT_ERROR_UNKNOWN ." ($error)"; break; } return $error_text; } Developing a payment module may be a complicated and time-consuming task. I hope this post helps you to understand how the payment process works and how the payment module's functions are used - and in the end makes it easier for you to write your own custom payment module. :thumbsup: Eskil
  2. I have painfully been where you are - I will share with you what I have learnt - hopefully others will also find this post useful. Here's an overview of the checkout process and call to payment module's functions (sequentally): (calling script : module function : description) 1. checkout_payment.php : javascript_validation() : generates client side js code for input validation 2. checkout_payment.php : selection() : Renders input fields and/or text fields 3. checkout_confirmation.php : pre_confirmation() : Any verification required before outputting payment summary 4. checkout_conformation.php : confirmation() : Any verification required before outputting form post data 5. checkout_confirmation.php : process_button() : outputs module's form post data 6. checkout_process.php : before_process() : Any action, e.g payment verification before order is completed 7. checkout_process.php : after_process() : Any action, e.g db inserts/updates after order is completed This what i can remember on the fly - please forgive me if anything is incorrect...! There is a bit more complexity that I could add, but it would only add confusion in this post. My tip would be to look at the osCommerce team's PayPal contribution. Make particular note of how they included most of the code to checkout_process.php in the modules before_process() function (because the order object is created after the payment module object - chicken&egg problem...). Hope this helps - this information has been aqcuired through close scrutiny of the process + trial and failure while developing osc payment modules for LUUP (www.luup.com). There is certainly a lack of team/community documentation - I might write a proper payment module development guide one day if I find the time... Eskil
  3. Please note that the LUUP contribution has disappeared from the contributions... We are about to release version 3.0 of the module - completely re-written, using web services so that the user does not leave the site during the checkout process. The payment module is tested, but awaiting some core system functionality before release. If you are interested, please contact LUUP www.luup.com to try it out. The new contribution will be submitted to contributions when released.
  4. I've recently written a payment module for my company's payment solution. (Contopronto - search the contributions) I used the before_process() to perform the verify transaction (callback) procedure. It is called by checkout_process.php before processing the order. ONE PROBLEM: one of our merchants added tax to shipping. So before order_totals->process is called, tax is missing from shipping and the total amount ($order->info['total']) does not match the transaction amount and the verification fails... still trying to work out a solution here. Comments appreciated... I also stumbled on the orderid problem - solved this by using $customer_id Not unique for the order, but at least some reference... Eskil
×
×
  • Create New...