QUESTION
I get an ERROR 3370 from Sage Pay when trying to repeat transactions of type AUTHORISE (but not PAYMENT). I am using Server Integration with PHP/cURL.
ANSWER
This is most likely because you need to send CoF values during your initial AUTHENTICATE transaction. This establishes the initial record as a valid "Credentital on File" and allows you to carry out REPEAT transactions later.
In a nutshell, the initial AUTHENTICATE record must be created with:
COFUsage = FIRST
InitiatedType = CIT
MITType = UNSCHEDULED
HERE IS THE FULL PHP/CURL CODE THAT IS NOW WORKING FOR ME:
STEP 1: AUTHENTICATE
Assuming you are using Server Integration and the customer is "in session" your initial cURL code will look something like this:
// Initialise cURL
$curl = curl_init();
// Set the Sage Pay server URL
$serverLive="https://live.sagepay.com/gateway/service/authorise.vsp";
// Connect to the Sage Pay server
curl_setopt($curl, CURLOPT_URL, $serverLive);
// Set cURL to post variables
curl_setopt($curl, CURLOPT_POST, 1);
// Declare account variables for posting to the Sage Pay server
$VPSProtocol="4.0"; // Must be 4.0 for CoF usage
$Vendor="......."; // Your Sage Pay Vendor Name
// Declare product variables for posting to the Sage Pay server
$Amount=0.01; // This transaction will be for 1 pence
$Currency="GBP";
$Description="......."; // Product description
// Declare URL of your callback page for posting to the Sage Pay server
$NotificationURL="https://www.......";
// Create a unique 16-character VendorTxCode for posting to the Sage Pay server
$UserID=9999; // Unique user ID
$salt="d5s63ffd6s7fgdhs55377yrwesr24553"; // Encryption key
$VendorTxCode=substr(strtoupper(md5(date("U").$UserID.$salt)), 0, 16);
// Declare other variables to pass to Sage Pay (ie. customer name, email, billing address etc)
// These will have been entered via a form on your website
$CustomerName=".......";
$CustomerEmail=".......";
$BillingFirstnames=".......";
$BillingSurname=".......";
$BillingAddress1=".......";
$BillingCity=".......";
$BillingPostCode=".......";
$BillingCountry="GB";
$BillingPhone=".......";
$DeliveryFirstnames=".......";
$DeliverySurname=".......";
$DeliveryAddress1=".......";
$DeliveryCity=".......";
$DeliveryPostCode=".......";
$DeliveryCountry="GB";
$DeliveryPhone=".......";
Now is probably a good time to INSERT these variables into your MySQL database. Create a table called "sagepay" with field/values as follows:
- userID = $UserID
- VendorTxCode = $VendorTxCode
- TxType = AUTHENTICATE
- Amount = $Amount
- Description = $Description
You should also have the following fields in your sagepay database table. These will start off empty and will be UPDATE(d) later:
- SecurityKey
- VPSTxId
- TxAuthNo
- RelatedVendorTxCode
- RelatedSecurityKey
- RelatedVPSTxId
- RelatedTxAuthNo
- SchemeTraceID
- ACSTransID
- DSTransID
- Status
Now post your variables to the Sage Pay server via cURL. Your cURL post should send a $COFUsage value of FIRST to correspond with this being an initial AUTHENTICATE transaction and $InitiatedType must be set to CIT to indicate the customer is "in session" (ie. they are going to enter their payment details on your website in a moment):
// Post the variables to the Sage Pay server
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query(array('Profile' => 'LOW', 'VPSProtocol' => $VPSProtocol, 'Vendor' => $Vendor, 'TxType' => 'AUTHENTICATE', 'VendorTxCode' => $VendorTxCode, **'Apply3DSecure' => '1', 'COFUsage' => 'FIRST', 'InitiatedType' => 'CIT',** 'Amount' => $Amount, 'Currency' => $Currency, 'Description' => $Description, 'CustomerName' => $CustomerName, 'CustomerEMail' => $CustomerEmail, 'BillingFirstnames' => $BillingFirstnames, 'BillingSurname' => $BillingSurname, 'BillingAddress1' => $BillingAddress1, 'BillingCity' => $BillingCity, 'BillingPostCode' => $BillingPostCode, 'BillingCountry' => $BillingCountry, 'BillingPhone' => $BillingPhone, 'DeliveryFirstnames' => $DeliveryFirstnames, 'DeliverySurname' => $DeliverySurname, 'DeliveryAddress1' => $DeliveryAddress1, 'DeliveryCity' => $DeliveryCity, 'DeliveryPostCode' => $DeliveryPostCode, 'DeliveryCountry' => $DeliveryCountry, 'DeliveryPhone' => $DeliveryPhone, 'NotificationURL' => $NotificationURL', 'Status' => 'OK'
)));
// This is supposed to speed things up (not sure if it does!)
curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
// Request a response from cURL
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
The above code will create an AUTHENTICATE record on the Sage Pay server and return a cURL string containing values that you will use to UPDATE your previously created database record. These variables will be reused when you create the AUTHORISE transaction (see Step 2):
// Get server response
$response = curl_exec($curl);
// Close cURL
curl_close ($curl);
Convert the $response string into an array called $results[]:
// Convert $response string into an array called $results[]
$results = [];
foreach (explode("\n", $response) as $line)
{
list ($key, $value) = explode('=', $line, 2);
$results[$key] = trim($value); // Trim to remove white space
}
The following variables are contained in the $results[] array. Make sure you UPDATE your initial AUTHENTICATE database record with these values. You will need to recall them when you come to AUTHORISE the transaction and take the first payment:
$SecurityKey=$results['SecurityKey']; // Save this to your database
$VPSTxId=$results['VPSTxId']; // Save this to your database
$results[] will also contain the following variable:
$NextURL=$results['NextURL'];
Once an AUTHENTICATE record is created on the Sage Pay server, you can display a card payment page to your customer using an iFrame with the source URL set to $NextURL
<iframe name="my_iframe" src="<?= $NextURL ?>" width='100%' height='520'></iframe>
At this point the customer will enter their card details and, if everything is in order, the transaction will be completed and the iFrame will update with your $NotificationURL
Sage Pay will pass the following variables to your $NotificationURL in the $_REQUEST[] array. It is a good idea to "trim" these values to ensure no white space creeps in:
$Status=trim($_REQUEST['Status']); // This should be "OK"
$TxAuthNo=trim($_REQUEST['TxAuthNo']);
$ACSTransID=trim($_REQUEST['ACSTransID']);
$DSTransID=trim($_REQUEST['DSTransID']);
UPDATE your "sagepay" database table with these values and then display your "Thank You" message.
STEP 2: AUTHORISE
Now you have an AUTHENTICATE record on the Sage Pay server, and a corresponding record in your MySQL database table, you must AUTHORISE the transaction to take the initial payment. This involves creating a new AUTHORISE record on the Sage Pay server and in your MySQL database.
First, create a new 16-character $VendorTxCode for the AUTHORISE record.
Then, SELECT and transfer the values from the original AUTHENTICATE record into "related" variables for resubmission to Sage Pay:
// Declare "related" variables
$RelatedVendorTxCode = $VendorTxCode; // The original VendorTxCode you created
$RelatedVPSTxId = $VPSTxId; // The $VPSTxId returned in $results[] array
$RelatedSecurityKey = $SecurityKey; // The $SecurityKey returned in $results[] array
Now is a good time to INSERT the $VendorTxCode and these "related" variables as a new record in your MySQL database. Set the TxType field for this new record to AUTHORISE.
Next, pass your variables to Sage Pay via cURL to create an AUTHORISE record on the Sage Pay server. Note, the $InitiatedType is now MIT to indicate the customer is no longer "in session", but the $COFUsage value still needs to be FIRST:
// Create an AUTHORISE record
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query(array('VPSProtocol' => $VPSProtocol, 'VendorTxCode' => $VendorTxCode, 'Vendor' => $Vendor, 'TxType' => 'AUTHORISE', **'Apply3DSecure' => '1', 'COFUsage' => 'FIRST', 'InitiatedType' => 'MIT', 'MITType' => 'UNSCHEDULED',** 'Amount' => $Amount, 'Description' => $Description, 'RelatedVPSTxId' => $RelatedVPSTxId, 'RelatedVendorTxCode' => $RelatedVendorTxCode, 'RelatedSecurityKey' => $RelatedSecurityKey, 'RelatedTxAuthNo' => $RelatedTxAuthNo)));
Sage Pay will respond again with a string ($response). Convert $response into a $results[] array as before. These are some of the variables you will end up with:
$TxAuthNo
$VPSTxId
$SecurityKey
$SchemeTraceID
$ACSTransID
$DSTransID
$Status
UPDATE the AUTHORISE record in your MySQL database table with these variables. You will need these variables for when you come to REPEAT the transaction.
$TxAuthNo is an authorisation code which is only returned if the AUTHORISE record is successfully created on the Sage Pay server (ie. if $Status is "OK").
$SchemeTraceID is your CoF "token" for repeating successful AUTHORISE transactions.
STEP 3: REPEAT
You can REPEAT against AUTHORISE transactions using the following cURL code. Note, the $COFUsage value now changes from FIRST to SUBSEQUENT and you must send the $SchemeTraceID (token) to verify the transaction as a valid "Credential on File". The "related" variables are those of the original AUTHORISE record (ie. $VendorTxCode, $TxAuthNo, $VPSTxId, and $SecurityKey):
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query(array('VPSProtocol' => $VPSProtocol, 'Vendor' => $Vendor, 'TxType' => 'REPEAT', 'VendorTxCode' => $VendorTxCode, **'Apply3DSecure' => '1', 'COFUsage' => 'SUBSEQUENT', 'InitiatedType' => 'MIT', 'MITType' => 'UNSCHEDULED', 'SchemeTraceID'=> $SchemeTraceID,** 'Amount' => $Amount, 'Currency' => $Currency, 'Description' => $Description, 'RelatedVPSTxId' => $RelatedVPSTxId, 'RelatedVendorTxCode' => $RelatedVendorTxCode, 'RelatedSecurityKey' => $RelatedSecurityKey, 'RelatedTxAuthNo' => $RelatedTxAuthNo )));