Vaulting Accounts and Making Payments
Step 1: Create a checkout page
-
Create a simple HTML form to collect:
- First Name
- Last Name
-
Add a script reference to PaySimpleJS to the body of your HTML page.
-
Add a button to your form
Environment
You must reference the PaySimpleJS script for the Sandbox environment when using a Sandbox API key and the Production environment when using a Production API key.
Sandbox:
<script src="https://sandbox-api.paysimple.com/paysimplejs/v1/scripts/client.js"></script>
Production:
<script src="https://api.paysimple.com/paysimplejs/v1/scripts/client.js"></script>
<html>
<head>
<meta charset="utf-8">
<title>PaySimpleJS</title>
</head>
<body>
<form id="sample-form">
<div class="merchant-form">
<p>Merchant Checkout Page</p>
<div class="form-field">
<label for="firstName">First Name</label>
<input type="text" id="firstName" />
</div>
<div class="form-field">
<label for="lastName">Last Name</label>
<input type="text" id="lastName" />
</div>
<div class="form-field">
<label for="email">Email</label>
<input type="email" id="email" />
</div>
</div>
<div class="psjs">
<p>PaySimpleJS Payment Form</p>
<div id="psjs">
<!-- a PaySimpleJS Payment Form will be inserted here -->
</div>
</div>
<div class="merchant-form">
<p>Merchant Checkout Page</p>
<button type="submit" id="submit">Complete Checkout</button>
</div>
</form>
<script src="https://api.paysimple.com/paysimplejs/v1/scripts/client.js"></script>
</body>
</html>
p {
font-family: Arial, sans-serif;
font-weight: bold;
margin: 0 0 10px;
padding: 0;
}
.merchant-form {
border: blue solid 1px;
margin: 15px 0;
padding: 10px;
}
.psjs {
background-color: #f9f9f9;
border: red solid 1px;
margin: 15px 0;
padding: 10px;
}
.form-field {
margin: 10px 0;
}
.form-field label {
display: block;
}
.form-field input {
display: block;
}
Step 2: Create Checkout Token endpoint
On your server, create an endpoint that will request a checkout token from the PaySimple API. The checkout token uniquely identifies your merchant account to PaySimple. While the token expires in 5 minutes, it is recommended you generate a new token on each checkout.
using System;
using System.Configuration;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Mvc;
using PaySimpleSdk.Accounts;
using PaySimpleSdk.Customers;
using PaySimpleSdk.Models;
using PaySimpleSdk.Payments;
namespace PaySimple.NetSDKSample.Web.Controllers
{
public class PaySimpleController : Controller
{
private static readonly IPaySimpleSettings PSSettings = new PaySimpleSettings(ConfigurationManager.AppSettings["PS.ApiKey"], ConfigurationManager.AppSettings["PS.UserId"],
ConfigurationManager.AppSettings["PS.BaseUrl"]);
[HttpPost]
public async Task<ActionResult> CheckoutToken()
{
var paymentService = new PaymentService(PSSettings);
var token = await paymentService.GetCheckoutTokenAsync();
return Json(token);
}
}
}
'use strict';
// Remove this line from a production app, this suppresses
// all certificate errors.
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
const express = require('express');
const bodyParser = require('body-parser');
const request = require('request');
const crypto = require('crypto');
const settings = {
port: 8080,
apiv4url: "https://sandbox-api.paysimple.com/v4",
username: 'YOUR_USER_NAME',
apikey: 'YOUR_API_KEY' // never hard code in production application
};
let app = express();
app.use(express.static('public'));
app.use(bodyParser.json());
// The authorization header for PaySimple 4.0
function getAuthHeader() {
let time = (new Date()).toISOString();
let hash = crypto.createHmac('SHA256', settings.apikey)
.update(time)
.digest('base64');
return 'PSSERVER ' + "accessid=" + settings.username +
"; timestamp=" + time + "; signature=" + hash;
}
// Simple endpoint to communicate with PaySimple 4.0 API to retrieve a JWT
// that can be passed to PaySimple.JS.
app.get('/token', function(req, res) {
let options = {
method: "POST",
uri: settings.apiv4url + '/checkouttoken',
headers: {
Authorization: getAuthHeader()
},
body: {},
json: true,
};
request(options, function(error, response, body) {
if (!error && response && response.statusCode < 300) {
res.json(body.Response);
return;
}
res.status((response && response.statusCode) || 500).send(error);
});
});
Request:
POST /v4/checkouttoken
Response:
{
"JwtToken": "<Checkout Token>",
"Expiration":"2017-02-28T22:03:36Z"
}
Content-Length header must be set
The Content-Length header must be set on every API request. Most frameworks, including the PaySimple .NET SDK, do this automatically. However, some (such as PHP/curl) do not when the body is empty as is the case on the POST /v4/checkouttoken call. Here is an example of how to set this header in PHP/curl:
curl_setopt($curl, CURLOPT_HTTPHEADER, array( "Content-Length: 0" ));
Step 3: Fetch Checkout Token client side
Initialize PaySimpleJS with the Checkout Token you requested in Step 2.
If there are any style or text customizations you'd like to make, pass them in here.
function loadPaysimpleJs(auth) {
var paysimplejs = window.paysimpleJs({
// the element that will contain the iframe
container: document.querySelector('#psjs'),
// checkout_token is in auth
auth: auth,
// allows entry of international postal codes if true
bypassPostalCodeValidation: false,
// Attempts to prevent browsers from using autocompletion to pre-populate
// or suggest input previously stored by the user. Turn on for point of
// sale or kiosk type applications where many different customers
// will be using the same browser to enter payment information.
preventAutocomplete : false,
// customized styles are optional
styles: {
body: {
// set the background color of the payment page
backgroundColor: '#f9f9f9'
}
}
});
};
function getAuth(callback) {
var xhr = new XMLHttpRequest();
// change to address of the endpoint you created in step 2
xhr.open('POST', 'PaySimple/CheckoutToken');
xhr.onload = function (e) {
if (xhr.status < 300) {
var data = JSON.parse(this.response);
callback.call(null, {
token: data.JwtToken
});
return;
}
alert('Failed to get Auth Token: (' + xhr.status + ') '
+ xhr.responseText);
}
xhr.send();
}
getAuth(loadPaysimpleJs);
Step 4: Load the credit card (or ach) form
Set the mode to cc-key-enter
for credit card or ach-key-enter
for ACH:
paysimplejs.send.setMode('cc-key-enter');
Step 5: Handle Errors
Add an event listener to handle errors like the example code below. This example snippet has not been tested.
paysimplejs.on('httpError', function(error) {
// where error = {
// "errorKey": <"timeout" | "bad_request" | "server_error"
// | "unauthorized" | "unknown">,
// "errors": <array of { field: <string>, message: <string> }>,
// "status": <number - http status code returned>
// }
// Add your error handling
});
Step 6: Vault a payment method
Add a callback to complete the account creation process after PaySimpleJS exchanges the card data for a payment token and account. Instantiate the callback by listening to the accountRetrieved
event:
Card Vaulted
Once the form has been submitted and
accountRetrieved
is called, the Card is vaulted in the PaySimple system. Rather than make a payment immediately, you may also store the CustomerId and AccountId in your database for making future payments or updates to the Customer with the PaySimple API.
paysimplejs.on('accountRetrieved', function(accountInfo) {
/* Example accountInfo:
* {
* "account": {
* "id": 7313702
* },
* "customer": {
* "id": 8041997,
* "firstName": "John",
* "lastName": "Snow",
* "email": "[email protected]"
* },
* "paymentToken": "e1f1bb19-9fe4-4c96-a35e-cd921298d8e6"
* }
*/
// The account and customer info is returned in the accountInfo object. Save the
// details of the account and customer for use later
// A payment may also be created using the paymentToken that is returned.
});
Step 7 (Optional): Create make payment endpoint
On your server, create an endpoint that accepts a Payment Token passed from PaySimpleJS and calls the new payment endpoint on PaySimple API 4.0.
[HttpPost]
public async Task<ActionResult> Payment(decimal amount, Account account, Customer customer, string paymentToken)
{
var payment = new Payment
{
Amount = amount,
AccountId = account.Id,
PaymentToken = paymentToken
};
var paymentService = new PaymentService(PSSettings);
payment = await paymentService.CreatePaymentAsync(payment);
return Json(payment);
}
app.post('/payment', function(req, res) {
var requestData = req.body;
let options = {
method: "POST",
uri: settings.apiv4url + '/payment',
headers: {
Authorization: getAuthHeader()
},
body: {
AccountId: requestData.account.id,
PaymentToken: requestData.paymentToken,
// Payment Specific information...
Amount: requestData.amount,
PurchaseOrderNumber: requestData.pono,
Description: requestData.description,
PaymentSubType: 'MOTO'
},
json: true,
};
request(options, function(error, response, body) {
// The return code for /payment will be a 201 (CREATED)
if (!error && response && response.statusCode < 300) {
res.json(body.Response);
return;
}
res.status((response && response.statusCode) || 500).send(error);
});
});
POST /v4/payment
{
"Amount" : 4,
"PaymentToken": "7E732F0E-9FB5-46F3-8320-1E91FEE079EB",
"AccountId" : 234542
}
Step 8 (Optional): Call your payment endpoint
Add a call to your payment endpoint in the callback to the accountRetrieved
event created in Step 6.
paysimplejs.on('accountRetrieved', function(accountInfo) {
/* Example accountInfo:
* {
* "account": {
* "id": 7313702
* },
* "customer": {
* "id": 8041997,
* "firstName": "John",
* "lastName": "Snow",
* "email": "[email protected]"
* },
* "paymentToken": "e1f1bb19-9fe4-4c96-a35e-cd921298d8e6"
* }
*/
// The account and customer info is returned in the accountInfo object. Save the
// details of the account and customer for use later
// A payment may also be created using the paymentToken that is returned.
// Send the accountInfo to your server to collect a payment
// for an existing customer
var xhr = new XMLHttpRequest();
// Replace with url of your endpoint
xhr.open('POST', 'PaySimple/Payment');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function (e) {
if (xhr.status < 300) {
var data = JSON.parse(this.response);
document.getElementById('message')
.innerHTML = 'Successfully created Payment:\nTrace #: ' + data.TraceNumber;
//alert('Successfully created Payment:\nTrace #: ' + data.TraceNumber);
} else {
document.getElementById('message')
.innerHTML = 'Failed to create Payment: (' + xhr.status + ')' + xhr.responseText;
// alert('Failed to create Payment: (' + xhr.status + ') '
// + xhr.responseText);
}
}
accountInfo.amount = document.querySelector('#amount').value;
xhr.send(JSON.stringify(accountInfo));
});
Updated about 2 years ago