This is a quick for-developers-only guide of how to get your PHP/JS code (Windows) talking to PayTrace’s API. I chose the Client Side Encryption as being our first time out, I wanted the least amount of strictness in PCI compliance. This guide assumes you know PHP, JS, JQuery, a little of what an API does, enough to paste some JSON together and of course your HTML, CSS, etc. You’re a developer, you have all the code provided open source, but this is a quick A-B-C of what code I used and how I got it working on Windows (IIS7) and hurdles I ran into.
All code here is readily available on GitHub, but if you’d like my version of it, download it on my Gist.
Ready to hit Submit?
Before doing this, I had already built out a complete cart system for our customers. They can view invoices, choose what to pay on those invoices, and when totaled, choose to pay by check or credit card. The employee end is nearly identical, except, they must choose which customer to pay for. In both cases, they are at a form ready to hit “Checkout” and complete the transaction. The method below only supports credit card, I used a different solution for checks. There, I will submit that amount and needed billing information to PayTrace and await for the response. The response (payment failure or success) goes directly back to our database.
Get What You Need
- Request a Sandbox Account (it took about an hour for them to respond)
- Login Sandbox Account
- Use the guide for Security Settings
- In your sandbox account Go to: Integration and Download Public Key (.pem)
- Download the PHP Github code
Preparing your Server
SSL TLS 1.2
PayTrace requires SSL TLS 1.2. What if you’ve set up your server with SSL but don’t know if it quite makes it up to grade? Run the website you’ll be using thru SSL Labs testing tool for a quick check to see if you have SSL TLS 1.2. This nifty site will do a scan and give you a letter grade. If you do not make an “A”, it tells you exactly what you are missing. You can work with your Server Admin to bring the server up to standards and move on.
MIME Type
If you’re using client-side encryption you’ll need to save that public key you downloaded to your web server and you’ll need to add its MIME type.
extension: pem
type: application/x-pem-file
If you don’t do this, you’ll likely get Ajax “key not found” errors.
PHP Version
In some initial curl tests I was trying out, I specifically had problems passing the SSL handshake with version 5.3 of PHP. I upgraded to 7.0 and the errors disappeared. Only noting this for those who might have done everything else and are still getting errors.
Test Your Server
- Upload the files you got from GitHub’s PhpJsonApiSamples folder to a test folder on your webserver
- Browse to its /default.html file
- Click KeyedSaleJson
If this runs correctly, you should see in the response:
Keyed sale : Success !
Http Status : 200 OK
Great, your server is up to par. Lets move on to the code.
Utilizing Code from PayTrace
Begin by uploading:
- PhpApiSettings.php
- Utilities.php
- Json.php
- public_key.pem
The first three from the GitHub’s PhpJsonApiSamples to your webserver, for the public_key.pem you log in to the sandbox account / API Integration / download.
You’ll be creating two pages, checkoutInfo.php and checkoutResponse.php. After the user has a total and hits “Checkout”, I send them to checkoutInfo.php to collect billing information. This particular page has:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!-- This is the PayTrace End-to-End Encryption library: -->
<script src="https://api.paytrace.com/assets/e2ee/paytrace-e2ee.js"></script>
<script>
// set the key from an AJAX call
$(document).ready(function(){
paytrace.setKeyAjax('/public_key.pem') ;// set the key from an AJAX call (in this case via a relative URL)
});
$(document).ready( function(){
$("#DemoForm").submit(function(e){
//To prevent the default action of the submit
e.preventDefault();
// Do your validation and if all validations are met,
// Next is to submit the form with paytrace.submitEncrypted.
if ($("#ccNumber").val() && $("#ccCSC").val()) {
//if all validations are met, submit the form to Paytrace library for encyption.
paytrace.submitEncrypted("#DemoForm");
}
else{
alert("CCNumber and CSC number are required ! ");
}
});
});
$(document).ready( function(){
$("#DemoForm").submit(function(){
$.post($(this).attr('action'), $(this).serialize(), function(json) {
alert(json);
}, 'json');
return false;
});
});
</script>
In case you skipped through documentation: PayTrace uses JQuery-1.11.1. I used Google’s CDN in the script above.
In this same form, I also had a place for billing address. I pre-filled it out with their billing address from the account (the random PHP variables you see) but the user can change each field as needed before hitting submit.
My sample form:
<form name="DemoForm" id="DemoForm" action="checkoutResponse.php" method="post">
<label>Company Name:</label>
<input id="name" name="name" class="form-control" type="text" value="<?php echo $name ?>" />
<label>Address Line 1: </label>
<input id="street_address" name="street_address" class="form-control" type="text" value="<?php echo $addr1 ?>" />
<label>Address Line 2:</label>
<input id="street_address2" name="street_address2" class="form-control" type="text" value="<?php echo $addr2 ?>" />
<label>City:</label>
<input id="city" name="city" type="text" class="form-control" value="<?php echo $city ?>" />
<label>State:</label>
<input id="state" name="state" type="text" class="form-control" value="<?php echo $state ?>" />
<label>Zip:</label>
<input id="zip" name="zip" type="text" class="form-control" value="<?php echo $zip ?>" />
<label>Country:</label>
<input id="country" name="country" type="hidden" class="form-control" value="US" />
<label>Payment Amount:</label>
<input id="amount" type="text" readonly name="amount" value="<?php echo $amount ?>" />">
<label>First Name:</label>
<input type="text" name="firstName" id="firstName" class="form-control" />
<label>Last Name:</label>
<input type="text" name="lastName" id="lastName" class="form-control" />
<label>Credit Card Number:</label>
<input id="ccNumber" type="text" class="form-control pt-encrypt" name="ccNumber" placeholder="Credit card number" />
<label>Expiration Month</label>
<select name="expiration_month" id="expiration_month" class="form-control">
<option>01</option>
<option>02</option>
<option>03</option>
<option>04</option>
<option>05</option>
<option>06</option>
<option>07</option>
<option>08</option>
<option>09</option>
<option>10</option>
<option>11</option>
<option>12</option>
</select>
<label>Expiration Year</label>
<select name="expiration_year" id="expiration_year" class="form-control">
<option> <?php echo date("Y")?> </option>
<option> <?php echo date("Y")+1 ?> </option>
<option> <?php echo date("Y")+2 ?> </option>
<option> <?php echo date("Y")+3 ?> </option>
<option> <?php echo date("Y")+4 ?> </option>
<option> <?php echo date("Y")+5 ?> </option>
<option> <?php echo date("Y")+6 ?> </option>
<option> <?php echo date("Y")+7 ?> </option>
<option> <?php echo date("Y")+8 ?> </option>
<option> <?php echo date("Y")+9 ?> </option>
<option> <?php echo date("Y")+10 ?> </option>
<option> <?php echo date("Y")+11 ?> </option>
<option> <?php echo date("Y")+12 ?> </option>
<option> <?php echo date("Y")+13 ?> </option>
<option> <?php echo date("Y")+14 ?> </option>
<option> <?php echo date("Y")+15 ?> </option>
<option> <?php echo date("Y")+16 ?> </option>
<option> <?php echo date("Y")+17 ?> </option>
<option> <?php echo date("Y")+18 ?> </option>
<option> <?php echo date("Y")+19 ?> </option>
<option> <?php echo date("Y")+20 ?> </option>
</select>
<label>Security code:</label>
<input id="ccCSC" type="text" class="form-control pt-encrypt" name="ccCSC" placeholder="Card security code" />
<input type="submit" id="enterPayment" value="Submit Your Payment" name="commit" />
</form>
How It Works
- User fills out billing info on the form in checkoutInfo.php
- The code above brings in PayTrace’s Client Side Encryption library (paytrace-e2ee.js)
- The script captures the users “Submit” form action and stops it to …
- Join it with your specific Public Key (.pem file) and uses the PayTrace library to encrypt the card number and csc number
- ccNumber and csc becomes: encrypted_number and encrypted_csc
- The script then continues on submitting the form to the API, using an encrypted number/csc instead and letting you duck some strict PCI restrictions because you never keep the card nor sent the actual credit card number
Handle the Response
My checkoutInfo.php page submits to checkoutResponse.php. This page preps your form data and sends it as JSON, waiting for a response. Upon response, it displays a pretty checkmark or xmark image (you’ll have to get your own).
On checkoutResponse.php, I used PayTrace’s files:
<?php
include 'PhpApiSettings.php';
include 'Utilities.php';
include 'Json.php';
?>
and then their “KeyedSaleJson.php” file (with my images added and form information from previous page added):
<?php
//call a function of Utilities.php to generate oAuth token
//This sample code doesn't use any 0Auth Library
$oauth_result = oAuthTokenGenerator();
//call a function of Utilities.php to verify if there is any error with OAuth token.
$oauth_moveforward = isFoundOAuthTokenError($oauth_result);
//If IsFoundOAuthTokenError results True, means no error
//next is to move forward for the actual request
if(!$oauth_moveforward){
//Decode the Raw Json response.
$json = jsonDecode($oauth_result['temp_json_response']);
//set Authentication value based on the successful oAuth response.
//Add a space between 'Bearer' and access _token
$oauth_token = sprintf("Bearer %s",$json['access_token']);
// Build the transaction
buildTransaction($oauth_token);
}
//end of main script
function buildTransaction($oauth_token){
// Build the request data
$request_data = buildRequestData();
//call to make the actual request
$result = processTransaction($oauth_token,$request_data, URL_KEYED_SALE );
/*echo "<br>json_response : " . $result['json_response'];
echo "<BR>curl_error : ".$result['curl_error'];
echo "<br>http_status_code :". $result['http_status_code'];
*/
//check the result
verifyTransactionResult($result);
}
function buildRequestData(){
//you can assign the values from any input source fields instead of hard coded values.
$request_data = array(
"amount" => $_POST['amount'],
"credit_card" => array(
"encrypted_number" => $_POST['ccNumber'],
"expiration_month" => $_POST['expiration_month'],
"expiration_year" => $_POST['expiration_year']
),
"encrypted_csc" => $_POST['ccCSC'],
"billing_address" => array(
"name" => $_POST['name'],
"street_address" => $_POST['street_address'],
"street_address2" => $_POST['street_address2'],
"city" => $_POST['city'],
"state" => $_POST['state'],
"zip" => $_POST['zip'],
"country" => $_POST['country']
));
$request_data = json_encode($request_data);
//optional : Display the Jason response - this may be helpful during initial testing.
echo "<h5>";
displayRawJsonRequest($request_data);
echo "<BR><BR></h5>";
return $request_data ;
}
//This function is to verify the Transaction result
function verifyTransactionResult($trans_result){
//Handle curl level error, ExitOnCurlError
if($trans_result['curl_error'] ){
echo "<img src = '/img/xmark.png'>";
echo "<br>Error occcured : ";
echo '<br>curl error with Transaction request: ' . $trans_result['curl_error'] ;
exit();
}
//If we reach here, we have been able to communicate with the service,
//next is decode the json response and then review Http Status code, response_code and success of the response
$json = jsonDecode($trans_result['temp_json_response']);
if($trans_result['http_status_code'] != 200){
if($json['success'] === false){
echo "<img src = '/img/xmark.png'>";
//echo "<br><br>Transaction Error occurred : ";
//Optional : display Http status code and message
//displayHttpStatus($trans_result['http_status_code']);
//Optional :to display raw json response
//displayRawJsonResponse($trans_result['temp_json_response']);
echo "<H1>Payment : FAILED !</h1>";
//to display individual keys of unsuccessful Transaction Json response
displayKeyedTransactionError($json) ;
}
else {
//In case of some other error occurred, next is to just utilize the http code and message.
echo "<img src = '/img/xmark.png'>";
echo "<br><br> Request Error occurred !" ;
displayHttpStatus($trans_result['http_status_code']);
}
}
else
{
// Optional : to display raw json response - this may be helpful with initial testing.
//displayRawJsonResponse($trans_result['temp_json_response']);
// Do your code when Response is available and based on the response_code.
// Please refer PayTrace-Error page for possible errors and Response Codes
// For transation successfully approved
if($json['success']== true && $json['response_code'] == 101){
echo "<img src = '/img/checkmark.png'>";
//echo "<br><br>Keyed sale : Success !";
//displayHttpStatus($trans_result['http_status_code']);
//to display individual keys of successful OAuth Json response
displayKeyedTransactionResponse($json);
}
else{
//Do you code here for any additional verification such as - Avs-response and CSC_response as needed.
//Please refer PayTrace-Error page for possible errors and Response Codes
//success = true and response_code == 103 approved but voided because of CSC did not match.
}
}
}
//This function displays keyed transaction successful response.
function displayKeyedTransactionResponse($json_string){
//optional : Display the output
//echo "<br><br> Keyed Sale Response : ";
//since php interprets boolean value as 1 for true and 0 for false when accessed.
echo "<br>PAYMENT : ";
echo $json_string['success'] ? 'SUCCESS' : 'FAILED';
//echo "<br>response_code : ".$json_string['response_code'] ;
echo "<br>status_message : ".$json_string['status_message'] ;
echo "<br>transaction_id : ".$json_string['transaction_id'] ;
echo "<br>approval_code : ".$json_string['approval_code'] ;
//echo "<br>approval_message : ".$json_string['approval_message'] ;
//echo "<br>avs_response : ".$json_string['avs_response'] ;
//echo "<br>csc_response : ".$json_string['csc_response'] ;
//echo "<br>external_transaction_id: ".$json_string['external_transaction_id'] ;
echo "<br>masked_card_number : ".$json_string['masked_card_number'] ;
}
//This function displays keyed transaction error response.
function displayKeyedTransactionError($json_string){
//optional : Display the output
//echo "<br><br> Keyed Sale Response : ";
//since php interprets boolean value as 1 for true and 0 for false when accessed.
echo "<br>success : ";
echo $json_string['success'] ? 'true' : 'false';
echo "<br>response_code : ".$json_string['response_code'] ;
echo "<br>status_message : ".$json_string['status_message'] ;
//echo "<br>external_transaction_id: ".$json_string['external_transaction_id'] ;
echo "<br>masked_card_number : ".$json_string['masked_card_number'] ;
//to check the actual API errors and get the individual error keys
//echo "<br>API Errors : " ;
foreach($json_string['errors'] as $error =>$no_of_errors )
{
//Do you code here as an action based on the particular error number
//you can access the error key with $error in the loop as shown below.
echo "<br>". $error;
// to access the error message in array assosicated with each key.
foreach($no_of_errors as $item)
{
//Optional - error message with each individual error key.
echo " " . $item ;
}
}
}
?>
At this point, you should be getting success/failure responses. Need some test credit cards?
Adding a Card Scanner / Swiper
If you’re getting successful responses, it’s time to go back to checkoutInfo.php and add the ability to card swipe.
For testing, the card reader / scanner I used was the MagTek Dynamag with USB Keyboard Emulation (Part Number 21073062 – Set at Security Level 2 or 3). We entered into contact with a third party advisor that suggested PayTrace for ease of use and suggested this as the most popular model. It was as easy as plug and play as they promised, but use the USB cord it comes with. I switched it out and cost myself an hour. Also, take note of the status light. If it is amber (as it was with my bad USB cord), it is not ready for action. The light must be green. Test your card reader simply by opening Notepad and swiping. You should string of garbled characters, letters and numbers.
Download Carl Raymond’s JQuery Cardswipe plug-in to help translate that garbled string into data and include it in your checkoutInfo.php file.
<script src="/js/jquery.cardswipe.js"></script>
Then, initialize it to populate your form with the name, card number, etc.:
<script type="text/javascript">
// Called by plugin on a successful scan.
var complete = function (data) {
// Is it a payment card?
if (data.type == "generic")
return;
// Copy data fields to form
$("#firstName").val(data.firstName);
$("#lastName").val(data.lastName);
$("#ccNumber").val(data.account);
$("#expiration_month").val(data.expMonth);
$("#expiration_year").val("20" + data.expYear);
$("#type").val(data.type);
};
// Event handler for scanstart.cardswipe.
var scanstart = function () {
$("#overlay").fadeIn(200);
};
// Event handler for scanend.cardswipe.
var scanend = function () {
$("#overlay").fadeOut(200);
};
// Event handler for success.cardswipe. Displays returned data in a dialog
var success = function (event, data) {
$("#properties").empty();
// Iterate properties of parsed data
for (var key in data) {
if (data.hasOwnProperty(key)) {
var text = key + ': ' + data[key];
$("#properties").append('<div class="property">' + text + '</div>');
}
}
$("#success").fadeIn().delay(3000).fadeOut();
}
var failure = function () {
$("#failure").fadeIn().delay(1000).fadeOut();
}
// Initialize the plugin with default parser and callbacks.
//
// Set debug to true to watch the characters get captured and the state machine transitions
// in the javascript console. This requires a browser that supports the console.log function.
//
// Set firstLineOnly to true to invoke the parser after scanning the first line. This will speed up the
// time from the start of the scan to invoking your success callback.
$.cardswipe({
firstLineOnly: true,
success: complete,
parsers: ["visa", "amex", "mastercard", "discover", "generic"],
debug: false
});
// Bind event listeners to the document
$(document)
.on("scanstart.cardswipe", scanstart)
.on("scanend.cardswipe", scanend)
.on("success.cardswipe", success)
.on("failure.cardswipe", failure)
;
</script>
Of course, on submit, this goes through the same cycle as previously – the PayTrace library takes the card number and csc, encrypts and sends. At this point, you should be swiping and all the form data auto-populating on swipe.
The code is a bit of a mess, but I think it might help some of us who approach mountains of documentation and just want a quick path to follow.