-
Notifications
You must be signed in to change notification settings - Fork 63
Creating a Web Store 3 Payment Module
###Introduction
This guide will walk you through the process of creating a payment module for the new Web Store 3 system. Payment modules are considered "extensions", Yii-parlance for an add-in structure which the framework can find and use to perform a specific task. Most of the instructions below follow closely with developing any Yii extension, with some specifics to integrate into the payment mechanism that has been developed for Web Store.
###Structure
All payment modules contain a minimum of two files. The first file is the module logic itself, shown above a "authorizedotnetaim.php". It is a class file which extends the Payment class that already exists in Web Store. This file is responsible for communicating with the payment processor and receiving a response, then passing this response back to Web Store. The class will always have access to the contents of the shopping cart, as well as all the information the user has entered on the checkout form. Any of these elements can be used in the logic of the processor.
The second file is a form definition (above as models/authorizedotnetaimAdminForm.php), which is used in Admin Panel to allow the customer to configure the module. The form definition contains any variables that are required from the customer, and these variables are also available to the module. Most typically, you will have fields for the merchant account ID and password (or security keys), as well as some fields required by Web Store such as a display name and restrictions. More information is below when we discuss this piece in detail.
All of Web Store's core files are in /core/protected/extensions/wspayment and you can use any of these files for reference. However, your own custom work should be placed in /custom/extensions/payment (create it if it doesn't exist) and never under /core.
The directory structure of these files is very specific. Your module will need a unique name, which is usually a form of the processor name. For example, if you are building a module for BitCoin, then your name might be bitcoin. This name is used not only for your class definition, but also the directory name and the filename as well. In this case, our class file would be:
class bitcoin extends WsPayment
{
protected $defaultName = "BitCoin";
protected $version = 1.0;
protected $uses_credit_card = true;
protected $apiVersion = 1;
public $advancedMode=true;
public function run()
{
//actual logic here
//return an array with our information
}
}
This file would be named bitcoin.php and would be in a folder called bitcoin in the /custom/extensions/payment folder (where all custom payment modules are installed). Don't worry about the specifics just yet, we'll take this line by line below.
PLEASE NOTE: The name of the class, as well as the filename, cannot contain spaces or punctuation, and WE HIGHLY RECOMMEND making your name all lower case. For a processor name such as First National, this would mean a name like firstnational would be valid, but FirstNational or first-national would not.
###Workflow
When Web Store attempts to run a payment authorization, it expects to find certain functions within the module to handle this process. For example, in the Cart controller itself where this attempt is made, we find the line:
$arrPaymentResult = Yii::app()->getComponent($modulename)->setCheckoutForm($model)->setSubForm($paymentSubformModel)->run();
This stackable command runs a series of functions in your module. In order, the system attempts to:
- Locate the component by its name (this is your filename)
- Pass the checkout form model to the payment module
- Pass any subform model to the payment module (this is optional, we'll explain more below)
- Call the run() function to actually perform the authorization attempt
You will notice that it expects an array to be passed back, which contains the details (success or failure) of the authorization attempt. The format of this array will be discussed in detail later.
You don't have to worry about the setCheckoutForm() or setSubForm() functions. setCheckoutForm() is actually a part of the WsPayment class, so it's inherited already in your file and unless you have an unusual need, you should never have to do anything with it. We simply include it here to show how the Checkout information is being passed so it's available. In fact, in nearly all cases, the only part you have to worry about is the run() function, which is the function you must write. You can have run() call whatever other functions you want, but run() is the only function Web Store cares about.
###From Simple to Complex
Below is an actual functional payment module, which represents the simplest end of the spectrum.
<?php
class cashondelivery extends WsPayment
{
protected $defaultName = "Cash on Delivery";
protected $version = "1.0";
/**
* The run() function is called from Web Store to run the process.
* The return array should have two elements: the first is true/false if the transaction was successful. The second
* string is either the successful Transaction ID, or the failure Error String to display to the user.
* @return array
*/
public function run()
{
$arrReturn['success']=true;
$arrReturn['amount_paid']=0;
$arrReturn['result']=$this->defaultName; //transaction ID or error string
$arrReturn['api'] = 1;
//This module just returns a success
return $arrReturn;
}
}
This module is the Cash On Delivery payment module. Let's go through this line by line.
- Line 3: Our class name, cashondelivery which follows our required formatting. This file is also named cashondelivery.php and is in a directory named cashondelivery under extensions/wspayment
- Line 5: $defaultName should be defined as the English-language readable name for this module. This name will appear in Admin Panel, and is the default name also shown to the user on checkout unless overridden by our Admin PAnel options
- Line 6: $version which is a version number for our module. As you do new features and bug fixes in future versions, this should be incremented so our Admin Panel updating works correctly. For your first version, set this to 1.0
- Line 13: This is the beginning of our run() function. This function must exist and is the function called by Web Store.
Here, the run() function makes no actual calls to any third party processors, because in this case, we always want to mark the order as Successful. (In most modules, "success" means a successful authorization by the processor. "Successful" in this case means that the module returns a true value so the order will be downloaded into LightSpeed.) For Cash On Delivery, Purchase Order or similar modules, "success"" is always returned even though no money has changed hands at this point.
###The return array
Success or failure from a payment module is returned in an array. There are several fields which can be used in the array, some of which are required and some of which will depend on conditions. The following array items are possible:
element | required | possible values |
---|---|---|
success | yes | true, false |
amount_paid | yes | Actual amount paid. This value will be passed as a "credit" to LightSpeed to apply to the order. For modules that actually do authorization, should be value of the capture. For others, generally 0. |
result | yes | For authorization modules, should be the Authorization Code which is returned by the merchant processor (i.e. A78SA6V9Z). For any other module, it is generally convention to pass back the name of the module. In the case of a DECLINE from a merchant processor, this will be the text string of the decline reason, which is shown to the user on the checkout form. |
jump_url | no | Used for "Simple Integration" modules, that take the customer away from Web Store to a third-party site to capture payment, then return the user back. The first generation of PayPal is the most classic example of this. Simple Integration modules do not require an SSL certificate because Web Store doesn't actually capture any confidential information. This is explained further below in the section on Simple Integration. Default: no |
jump_form | no | Form definition used when jump_url is used for Simple Integration modules. Explained further below. |
api | yes | Internal API version of Web Store module specifications. This allows us to update our specifications without breaking earlier modules. At this time, should always be 1. |
payment_date | no | The date of the payment, in YYYY-MM-DD HH:MM:SS format. If not specified, the current system date/time are recorded. If using a date from a merchant processor, be aware of timezone differences. |
Once you have the required elements in your array, pass back the entire array as your result. In our example above, Lines 16 to 18 define our successful transaction and pass back this array.
###Using cart and checkout form information
For modules that need to actually get an authorization code from a payment processor, it is necessary to pass information about the transaction. Of course, this includes the amount of the transaction and the credit card number, but also can contain customer name and address information if you are doing Address Verification Service, or even the cart items themselves if your processor requires it. All of the information is available to you through the WsPayment class you have inherited. Let's look at an example of a module that passes some cart information.
class firstnationalaim extends WsPayment
{
protected $defaultName = "First National";
protected $version = 1.0;
protected $uses_credit_card = true;
protected $apiVersion = 1;
public $advancedMode = true;
/**
* The run() function is called from Web Store to run the process.
* @return array
*/
public function run() {
$checkout_values = array (
"x_card_num" => _xls_number_only($this->CheckoutForm->cardNumber), //AAAABBBBCCCCDDDD
"x_exp_date" => $this->CheckoutForm->cardExpiryMonth . "-" . $this->CheckoutForm->cardExpiryYear, //MM-YYYY
"x_description" => $this->objCart->id_str,
"x_amount" => round($this->objCart->total,2)
);
$checkout_fields = "";
foreach( $checkout_values as $key => $value )
$checkout_fields .= "$key=" . urlencode( $value ) . "&";
$ch = curl_init("http://www.example.com/processor.php");
curl_setopt($ch, CURLOPT_HEADER, 0); // set to 0 to eliminate header info from response
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // Returns response data instead of TRUE(1)
curl_setopt($ch, CURLOPT_POSTFIELDS, rtrim( $checkout_fields, "& " )); // use HTTP POST to send form data
$resp = curl_exec($ch); //execute post and get results
curl_close ($ch);
//Make decisions based on $resp
$resp_vals = array();
parse_str($resp, $resp_vals);
if($resp_vals['trnApproved'] != '1' ) {
//unsuccessful
$arrReturn['success']=false;
$arrReturn['amount_paid']=0;
$arrReturn['result'] = Yii::t('global',urldecode($resp_vals['messageText']));
Yii::log("Declined: ".urldecode($resp_vals['messageText']), 'error', 'application.'.__CLASS__.".".__FUNCTION__);
} else {
//We have success
$arrReturn['success']=true;
$arrReturn['amount_paid']= ($resp_vals['authCode'] == "TEST" ? 0.00 : $resp_vals['trnAmount']);
$arrReturn['result']=$resp_vals['authCode'];
$arrReturn['payment_date']=$resp_vals['trnDate'];
}
}
}
This module is more complicated than our original example, but is still a complete working example. Let's take a look at the additional items
- Line 5: $uses_credit_card = true; is a special flag we set to true here, which causes Checkout to display a credit card entry form. This form takes the card number, expiration date, CVV value, and Name of Cardholder, all of which are available to us in the module. This saves us from having to write a custom form for a traditional set of fields. (If for whatever reason you needed a custom form for this information, you can use a SubForm, explained further below.)
- Line 7: $advancedMode = true; is a flag that defines this module as an Advanced Module, one that takes the payment on the checkout page and does not use an SSL certificate. This flag is only used for Admin Panel which groups advanced and, it won't affect operation but it may confuse the store owner by showing your module in the wrong location in admin panel.
- Lines 16-20: Here we can see an example of using some of the fields from CheckoutForm and our shopping Cart. In this case, we're using the cardNumber, Expiration, id_str (this is our WO-12345 web order number), and the amount of the cart. In the index at the bottom, we have included a complete list of these fields but in reality they're simply our model definitions. You can reference CheckoutForm at /protected/models/forms/BaseCheckoutForm.php to see all the field names available (denoted as variables at the top). We have included the most common fields you will need in our index below.
- Lines 24-26, we combine the fields we need into a single POST string in preparation for our cURL call
- Lines 28-34, using cURL, we send the cart information to our merchant processor URL and wait for the reply, populated in $resp on Line 32
- Lines 37-38, we parse the $resp string into an array, $resp_vals so we can better evaluate our response
- Lines 40-51, based on our approval or decline response, we fill out our return array and return it at the end of the function
The exact format of our POST string, and the format of the response back will entirely depend on the processor you're communicating with. However, this example shows the most typical way to communicate and parse a response, so that you can mark and order as successfully paid or not.
Moving beyond this, your run() function can certainly pass control to other functions within your file, or even in other files if you include them in your module. Many processors have pre-written samples, API libraries or class files already available for integrators, and it may be easier to simply include these in your directory and reference them. All these decisions will be entirely dependent on the situation. However, at the end, Web Store simply expects to call run() and get back an array telling it what happened. What occurs between those two events is entirely at your discretion.
###The Admin Panel configuration form
All modules require a configuration panel. At a minimum, there are some Web Store required fields for any module that must exist, even if you do not have any of your own to add. For example, it is possible to restrict a module to a geographic area (i.e. to only allow the WorldPay module to be shown to European customers, for example), and these fields are part of the Admin Panel definition.
Because Web Store is built using the MVC Framework, we take advantage of this by using a CForm definition to control the admin panel page. This results in much less work because you don't have to write any of the logic to display, or save any of the fields. You simply have to define their names and labels, along with any restrictions you require (number only, 40 character limit, etc), and Web Store takes care of the rest.
The Admin Panel form is a second file which exists in your module directory, and it follows a very specific name. It is the name of your module, with the words AdminForm appended, and it must exist in a subdirectory called models in your module directory. (We realize the words model and module are very close, please don't get confused as you read this.) Admin Panel expects to find this model definition and will not function without it.
The simplest way to explain this is to first show you what the form looks like in Admin Panel, and then show you the behind-the-scenes definition form that creates it. Here is the PayPal Pro admin form as displayed to the user:
As you can see, we have 10 fields which are shown (plus our Active switch that is added by Web Store automatically, and is not a part of our form definition). Now, here is the AdminForm model:
<?php
class paypalproAdminForm extends CFormModel
{
public $label;
public $api_username;
public $api_password;
public $api_signature;
public $api_username_sb;
public $api_password_sb;
public $api_signature_sb;
public $live;
public $restrictcountry;
public $ls_payment_method;
/**
* Declares the validation rules.
*/
public function rules()
{
return array(
array('label,live,restrictcountry,ls_payment_method','required'),
array('api_username,api_password,api_signature,api_username_sb,api_password_sb,api_signature_sb','safe'),
);
}
/**
* Declares customized attribute labels.
* If not declared here, an attribute would have a label that is
* the same as its name with the first letter in upper case.
*/
public function attributeLabels()
{
return array(
'label'=>'Label',
'api_username'=>'API Username',
'api_password'=>'API Password',
'api_signature'=>'API Signature',
'api_username_sb'=>'API Username (Sandbox)',
'api_password_sb'=>'API Password (Sandbox)',
'api_signature_sb'=>'API Signature (Sandbox)',
'live'=>'Deployment Mode',
'restrictcountry'=>'Only allow this processor',
'ls_payment_method'=>'LightSpeed Payment Method',
);
}
public function getAdminForm()
{
return array(
'elements'=>array(
'label'=>array(
'type'=>'text',
'maxlength'=>64,
),
'api_username'=>array(
'type'=>'text',
'maxlength'=>64,
),
'api_password'=>array(
'type'=>'text',
'maxlength'=>64,
),
'api_signature'=>array(
'type'=>'text',
'maxlength'=>64,
),
'live'=>array(
'type'=>'dropdownlist',
'items'=>array('live'=>'Live','test'=>'Sandbox'),
),
'api_username_sb'=>array(
'type'=>'text',
'maxlength'=>64,
),
'api_password_sb'=>array(
'type'=>'text',
'maxlength'=>64,
),
'api_signature_sb'=>array(
'type'=>'text',
'maxlength'=>64,
),
'restrictcountry'=>array(
'type'=>'dropdownlist',
'items'=>Country::getAdminRestrictionList(),
),
'ls_payment_method'=>array(
'type'=>'text',
'maxlength'=>64,
),
),
);
}
}
The important thing to remember here is that this file is a very standard Yii format, and is not unique to Web Store. For example, if you compare the examples on Yii's site at http://www.yiiframework.com/doc/guide/1.1/en/form.model and compare them to this file, they should look identical. This means that the questions of "What can I include" or how restrictions and labeled are handled within Yii's documentation, not Web Store specifically.
There are four parts to this file, so let's go over them individually.
The top part, Lines 4-13 define all the fields that will be displayed to the user, defined as PHP public variables. You can name these fields anything you wish, and they will all be accessible from within your module using $this->config[]. For example, we see $api_username as a defined variable here. In the module itself, we can reference this as $this->config['api_username'] in our code. Each variable is an array name on our config object.
NOTE: The variables $label, $restrictcountry; and $ls_payment_method MUST must exist in any payment module definition. Label is used for customer display on checkout, Restrict Country is used to restrict a payment to only be used in certain geographic region, and LS Payment Method is used to match to a Payment Method in LightSpeed when the order is downloaded. The payment module will not function if any of these fields are missing.
The second section is rules(). These are all standard Yii structures as mentioned above. We use Yii's structure to define what fields are "required" and what are optional (defined as "safe" here). Any variable defined must be listed in one of these two lines, or it won't be saved when the user clicks Save. If you have created a variable but it's always a blank value when attempting to use it in your module, it means you've forgotten to add it to the rules. Anything marked as required here will show with a red asterisk on the display (see example above) and if left blank, the module will refuse to Save.
The third section is labeling, which controls how each variable is displayed in Admin Panel. You can see here that each field has a full English name which is displayed to the user. This section is technically optional, any missing variables will be displayed just using the variable name itself in Admin Panel.
Finally, we have the form definition itself, which is used to display the form. This function returns an array of the form definition, with each element is in a nested array. Simply copy/paste the definitions to add your own. Each variable name is specified, along with the type (such as text, or dropdown list), along with any additional parameters. Text can have a limit, and dropdowns have the items which appear in the dropdown. This type of form definition in Yii is called Form Builder and you can read additional information at http://www.yiiframework.com/doc/guide/1.1/en/form.builder including all the form options available to you.
As with any programming, it is simpler to copy an existing file and modify it for your own uses. Look through the other modules in Web Store and find one that's closest to your end result, and begin with a copy of it.
##Simple Integration
NOTE: This section is only necessary for Simple Integation modules. If your module contacts the processor via cURL and processes the response immediately, none of this section applies to you.
A Simple Integration module is a module which passes control to a third-party processor, taking the customer away from Web Store. This is most commonly done for security reasons, so that the information is entered securely on another site, when the Web Store has not had an SSL certificate installed. PayPal is the most visible example of this type of workflow, since the customer is redirected to PayPal's site to log in, confirm the transaction, and is then returned to Web Store and shown the final receipt. (In some cases, the customer may close their browser and not return to Web Store after payment, so your module should not count on that event for any critical step.)
Simple Integration modules are good for Web Store owners who do not wish to pay for or install an SSL security certificate, or have merchant processors that require full PCI compliance. However, customers may be confused by being redirected to an unfamiliar site for entering their credit card number, which may result in cart abandonment and hurt sales. These factors should be evaluated when deciding to use a Simple module.
###The Jump Form
Simple Integration modules work by building an HTML form, which is used to submit the information to the third party site. The Web Store payment module uses two array values for this, jump_url which is the URL the form will be submitted to, and jump_form which is the HTML definition of the form itself. Additionally, the payment module will require one additional function unique to Simple Integration modules, gateway_response_process() which handles the return processing once the payment has been completed. This has to be a separate function because the processor will generally submit information to a special Return URL, which may be a separate process than the customer checkout. An example of this is PayPal's IPN process, which independently calls your Web Store to mark a transaction as paid. This prevents a customer from "spoofing" a URL in their browser to trick Web Store into erroneously marking an order as complete which could cost the store money.
Below is an example of a Simple Module, in this case the Authorize.net SIM module:
public function run()
{
$auth_net_login_id = $this->config['login'];
$auth_net_tran_key = $this->config['trans_key'];
/**
* This option, and the commented $ret['live']->AddItem('dev' , 'dev') above, are only for API development work.
* Regular Authorize.net customers will only use "live" and "test" modes through their account, which can be
* chosen through the Web Admin panel.
*
*/
if($this->config['live'] == 'test')
$auth_net_url = "https://test.authorize.net/gateway/transact.dll";
else
$auth_net_url = "https://secure.authorize.net/gateway/transact.dll";
$str = "";
$str .= "<FORM action=\"$auth_net_url\" method=\"POST\">";
$str .= $this->InsertFP($auth_net_login_id, $auth_net_tran_key, round($this->objCart->Total,2), $this->objCart->currency);
$str .= _xls_make_hidden('x_invoice_num', $this->objCart->id_str);
$str .= _xls_make_hidden('x_first_name', $this->CheckoutForm->contactFirstName);
$str .= _xls_make_hidden('x_last_name', $this->CheckoutForm->contactLastName);
$str .= _xls_make_hidden('x_company', $this->CheckoutForm->contactCompany);
$str .= _xls_make_hidden('x_address', ($this->CheckoutForm->billingAddress2 != '' ?
$this->CheckoutForm->billingAddress1 . " " . $this->CheckoutForm->billingAddress2 : $this->CheckoutForm->billingAddress1));
$str .= _xls_make_hidden('x_city', $this->CheckoutForm->billingCity);
$str .= _xls_make_hidden('x_state', $this->CheckoutForm->billingState);
$str .= _xls_make_hidden('x_zip', $this->CheckoutForm->billingPostal);
$str .= _xls_make_hidden('x_country', $this->CheckoutForm->billingCountry);
$str .= _xls_make_hidden('x_phone', _xls_number_only($this->CheckoutForm->contactPhone));
$str .= _xls_make_hidden('x_email', $this->CheckoutForm->contactEmail);
$str .= _xls_make_hidden('x_cust_id', "WC-" . $this->objCart->customer_id);
$str .= _xls_make_hidden('x_ship_to_first_name', $this->CheckoutForm->shippingFirstName);
$str .= _xls_make_hidden('x_ship_to_last_name', $this->CheckoutForm->shippingLastName);
$str .= _xls_make_hidden('x_ship_to_company', $this->CheckoutForm->shippingCompany);
$str .= _xls_make_hidden('x_ship_to_address', $this->CheckoutForm->shippingAddress1 . " " . $this->CheckoutForm->shippingAddress2);
$str .= _xls_make_hidden('x_ship_to_city', $this->CheckoutForm->shippingCity);
$str .= _xls_make_hidden('x_ship_to_state', $this->CheckoutForm->shippingState);
$str .= _xls_make_hidden('x_ship_to_zip', $this->CheckoutForm->shippingPostal);
$str .= _xls_make_hidden('x_ship_to_country', $this->CheckoutForm->shippingCountry);
$str .= _xls_make_hidden('x_description', _xls_get_conf( 'STORE_NAME' , "Online") . " Order");
$str .= _xls_make_hidden('x_login', $auth_net_login_id);
$str .= _xls_make_hidden('x_type', 'AUTH_CAPTURE');
$str .= _xls_make_hidden('x_currency_code', $this->objCart->currency); //trying to get currency code to submit
$str .= _xls_make_hidden('x_amount', round($this->objCart->Total,2));
$str .= _xls_make_hidden('x_show_form', 'PAYMENT_FORM');
$str .= _xls_make_hidden('x_relay_url', Yii::app()->controller->createAbsoluteUrl('/cart/payment/'.$this->modulename));
$str .= _xls_make_hidden('x_relay_response', 'TRUE');
$str .= _xls_make_hidden('x_cancel_url', Yii::app()->controller->createAbsoluteUrl('cart/restore', array('getuid'=>$this->objCart->linkid)));
$str .= _xls_make_hidden('x_header_html_payment_form', str_replace("\"","'",
CHtml::image(Yii::app()->controller->createAbsoluteUrl(_xls_get_conf('HEADER_IMAGE')),_xls_get_conf('STORE_NAME'),array('style'=>'max-width:580px'))
));
//if($this->config['live'] == 'test')
//$str .= _xls_make_hidden('x_test_request', 'TRUE');
$str .= ('</FORM>');
if(_xls_get_conf('DEBUG_PAYMENTS' , false))
_xls_log(get_class($this) . " sending ".$this->objCart->id_str." in ".$this->config['live']." mode ".$str,true);
$arrReturn['api'] = $this->apiVersion;
$arrReturn['jump_form']=$str;
return $arrReturn;
}
This module gets quite complex, because there are many fields from both Checkout and the cart that must be combined into a single HTML form, to be ultimately sent to Authorize.Net. In this case, we're building a long $str variable which includes hidden HTML form variables containing all our fields. (The exact specifications will be unique to each processor.) We can also see some Yii functions such as createAbsoluteUrl() which are automatically building our return URLs.
For example on Line 56, we specify the Relay URL which for Authorize.net is the URL that the success message is submitted to. Note here that we're using a URL including /cart/payment/ followed by the name of our module (authorizenetaim in this case). This means if our store is at http://store.example.com our return URL will be http://store.example.com/cart/payment/authorizenetaim and their system will submit back a form with the success details. You don't have to worry about the URL structure, you can simply copy/paste this same code to your own module. But what happens when their system accesses that special URL?
In this case, Web Store automatically will process the incoming messages and pass it back to your module (because of the appended name), and instead of looking for run(), it will look for gateway_response_process() and forward anything received from the service.
###Gateway Response
The response function evaluates what is being returned. In fact, it also returns an array() very similar to the one we normally get from run() on Advanced Integration modules. Let's look at a sample:
public function gateway_response_process() {
if(_xls_get_conf('DEBUG_PAYMENTS' , false))
Yii::log(get_class($this) . " Transaction ".print_r($_POST,true), CLogger::LEVEL_ERROR, get_class($this));
$x_response_code = Yii::app()->getRequest()->getPost('x_response_code');
$x_invoice_num = Yii::app()->getRequest()->getPost('x_invoice_num');
$x_MD5_Hash = Yii::app()->getRequest()->getPost('x_MD5_Hash');
$x_amount = Yii::app()->getRequest()->getPost('x_amount');
$x_trans_id = Yii::app()->getRequest()->getPost('x_trans_id');
if(empty($x_response_code) || empty($x_invoice_num))
return false;
if($x_response_code != 1){
// failed order
Yii::log(get_class($this) . " failed order payment received ".print_r($_POST,true), CLogger::LEVEL_ERROR, get_class($this));
return false;
}
$objCart = Cart::LoadByIdStr($x_invoice_num);
$url = Yii::app()->createAbsoluteUrl('cart/receipt',array('getuid'=>$objCart->linkid));
$arrReturn = array();
$arrReturn['success'] = true;
$arrReturn['order_id'] = $x_invoice_num;
$arrReturn['amount'] = !empty($x_amount) ? $x_amount : 0;
$arrReturn['data'] = !empty($x_trans_id) ? $x_trans_id : '';
$arrReturn['output'] = "<html><head><meta http-equiv=\"refresh\" content=\"0;url=$url\"></head><body><a href=\"$url\">" .
Yii::t('global','Redirecting to your receipt')."...</a></body></html>";
return $arrReturn;
}
We see in this case that we get our $_POST variables that have been submitted back to us by Authorize.net, and use this information to determine what to do next. We use our passed back Invoice (Web Order) to look up our cart ($objCart = Cart::LoadByIdStr). If our response code is invalid, we know the transaction failed and we simply return a false. Otherwise, we again return an array with our success.
Note here we have two additional fields in our array:
element | required | value |
---|---|---|
order_id | yes | our WO-12345 number, which should be passed back from the processor. Because this process may be independent, we cannot rely on our session information. |
output | yes | Because some Simple Integration processors will pass back immediately after payment, our processing here must provide them with a URL to redirect to. In this case, we build a $url to our receipt, and then use an HTML form definition in output which will be sent to the customer's browser. |
As with all beginning programming projects, it is far simpler to copy something that exists and change it for your own purposes than to create from the beginning. If you need to create a Simple Integration module, we recommend making copies of either the Authorize.net SIM or PayPal module and changing the functions to suit your own purposes. (Don't forget to change the class and filenames). Many of the lines, especially dealing with URLs and Yii functions can be left alone, and you only have to worry about adding or removing fields that your processor requires.