Payment Gateway Developer Documentation
Recurring Payments
Recurring Payments
Building a Website Integration with Source Allies' Payment Gateway - Recurring Payments
Related Pages:
- [Payment Gateway Developer Documentation Overview](/payment-gateway/developer)
- [Credit Card and eCheck subscriptions](/payment-gateway/developer/cc-and-echeck)
- [Google Pay subscriptions](/payment-gateway/developer/google-pay)
- [Apple Pay subscriptions](/payment-gateway/developer/apple-pay)
- \@sourceallies/payment-gateway
- NPM package
- Developer Documentation
### Submitting Recurring Payments
#### Introduction
These directions were derived from Authorize.Net's Accept.js API Documentation for hosting your own payment form. If you find any discrepency between this guide and theirs, please notify us at: hello@sourceallies.com.
During this tutorial, you will be adding on to a simple form which will submit test calls to Payment Gateway in our development environment. For these test subscriptions, use the card numbers provided by Authorize.Net in their Testing Guide.
This tutorial will take you through submitting credit card subscriptions. To submit bank subscriptions, you will follow a similar approach, but with minor differences. These include having different or additional form fields and supplying different data to Authorize.Net as seen in the `bankData` in this tutorial.
The form looks as follows (the `value` property on the inputs are set to an Authorize.Net test card for your convenience):
```html
<form action="javascript:handleSubmit()">
<label for="invoice_number">Invoice Number</label>
<input id="invoice_number" type="text" value="1">
<label for="amount">Amount</label>
<input id="amount" type="text" value="1.00">
<label for="email_address"></label>
<input type="email" id="email_address" value="email@email.com"></label>
<label for="cardholder-name">Cardholder Name</label>
<input id="cardholder-name" type="text" value="FirstName LastName">
<label for="card-number">Card Number</label>
<input id="card-number" type="text" maxlength="16" minlength="16" value="4111111111111111">
<label for="card-expiration">Card Expiration (eg: 01/21)</label>
<input id="card-expiration" type="text" maxlength="5" minlength="4" value="01/23">
<label for="card-code">Card Code</label>
<input id="card-code" type="text" maxlength="4" minlength="3" value="123">
<label for="street-address">Street Address</label>
<input id="street-address" type="text" value="104 First Street">
<label for="city">City</label>
<input id="city" type="text" value="Urbandale">
<label for="state">State</label>
<input id="state" type="text" value="IA">
<label for="zipcode">Zip Code</label>
<input id="zipcode" type="text" minlength="5" value="12345">
<label id="frequency_label" for="frequency">Frequency</label>
<select type="text" id="frequency" value="1">
<option value="1">Monthly</option>
<option value="3">Quarterly</option>
<option value="6">Every Six Months</option>
<option value="12">Annually</option>
</select>
<label id="payment_number_label" for="payment_number">Number of Payments</label>
<input type="text" id="payment_number" value="1">
<label id="first_payment_date_label" for="first_payment_date">Start Date (mm/dd/yyyy)</label>
<!-- Please note that an input of type date has a date picker in Chrome and Firefox, not in Safari or IE -->
<!-- Also, Chrome and Firefox will store the value as yyyy-mm-dd, where as Safari and IE will store the value as typed by the user -->
<input type="date" id="first_payment_date" title="Pick a date for payments to start." placeholder="mm/dd/yyyy" pattern="^(((0?[1-9]|1[012])\/(0?[1-9]|1\d|2[0-8])|(0?[13456789]|1[012])\/(29|30)|(0?[13578]|1[02])\/31)\/(19|[2-9]\d)\d{2}|0?2\/29\/((19|[2-9]\d)(0[48]|[2468][048]|[13579][26])|(([2468][048]|[3579][26])00)))$" class="s" value="06/04/2020" />
<button type="submit">Submit</button>
</form>
<script src="https://jstest.authorize.net/v1/Accept.js"></script>
<script src="https://unpkg.com/@sourceallies/payment-gateway@latest/dist/payment-gateway.umd.js"></script>
```
#### Getting Started
To begin, import Accept.js by adding a script tag to your payment page.
For test environment:
```html
<script src="https://jstest.authorize.net/v1/Accept.js"></script>
```
For production environment:
```html
<script src="https://js.authorize.net/v1/Accept.js"></script>
```
You will also need to include our @sourceallies/payment-gateway NPM package for this tutorial. The developer documentation for @sourceallies/payment-gateway can be found here. Add the following script tag to your payment page:
```html
<script src="https://unpkg.com/@sourceallies/payment-gateway@latest/dist/payment-gateway.umd.js">
</script>
```
You will want to replace `@latest` in the URL with the current version which can be found on the NPM repository here or in the badge below. Including this package allows you to access all of the functions for interfacing with Payment Gateway.
[![npm version](https://badge.fury.io/js/%40sourceallies%2Fpayment-gateway.svg)](https://badge.fury.io/js/%40sourceallies%2Fpayment-gateway)
#### Implementation
##### Obtaining an Authorize.Net Cipher Key
To begin integrating Payment Gateway into your website, you must obtain an Authorize.Net cipher key in your function that is ran while handling a payment submission (`handleSubmit`). In order to obtaion an Authorize.Net cipher key, you will need your partner name and gateway name. You will create an object from your partner name and gateway name that has the following structure:
```js
// Your information to link the subscriptions to your Payment Gateway account.
const gatewayData = {
partnerName: "YOUR PARTNER NAME",
gatewayName: "YOUR GATEWAY NAME"
};
```
Along with your `gatewayData` you will need to specify an `environment` variable that indicates what environment of Payment Gateway to make HTTP requests to. This variable can equal `"DEV"` or `"PROD"`. The environment variable will have the following signature:
```js
const environment = "DEV"; // Specifies the environment to be used when Payment Gateway makes requests. This can be set to "DEV" or "PROD".
```
The final step for recieving an Authorize.Net cipher key is to call the function `paymentGateway.getMerchantDetails` with a callback function (that takes in the `XMLHttpRequest` object, we will explain this further), the `gatewayData` variable and the `environment` variable as arguments. This function is included in the Payment Gateway JavaScript package included in your HTML file. All of this will be done in the `handleSubmit` function. Your HTML file will now look like this:
> All functions starting with `paymentGateway.` are included in the Payment Gateway JavaScript package.
```html
<form action="javascript:handleSubmit()">
<label for="invoice_number">Invoice Number</label>
<input id="invoice_number" type="text" value="1">
<label for="amount">Amount</label>
<input id="amount" type="text" value="1.00">
<label for="email_address"></label>
<input type="email" id="email_address" value="email@email.com"></label>
<label for="cardholder-name">Cardholder Name</label>
<input id="cardholder-name" type="text" value="FirstName LastName">
<label for="card-number">Card Number</label>
<input id="card-number" type="text" maxlength="16" minlength="16" value="4111111111111111">
<label for="card-expiration">Card Expiration (eg: 01/21)</label>
<input id="card-expiration" type="text" maxlength="5" minlength="4" value="01/23">
<label for="card-code">Card Code</label>
<input id="card-code" type="text" maxlength="4" minlength="3" value="123">
<label for="street-address">Street Address</label>
<input id="street-address" type="text" value="104 First Street">
<label for="city">City</label>
<input id="city" type="text" value="Urbandale">
<label for="state">State</label>
<input id="state" type="text" value="IA">
<label for="zipcode">Zip Code</label>
<input id="zipcode" type="text" minlength="5" value="12345">
<label id="frequency_label" for="frequency">Frequency</label>
<select type="text" id="frequency" value="1">
<option value="1">Monthly</option>
<option value="3">Quarterly</option>
<option value="6">Every Six Months</option>
<option value="12">Annually</option>
</select>
<label id="payment_number_label" for="payment_number">Number of Payments</label>
<input type="text" id="payment_number" value="1">
<label id="first_payment_date_label" for="first_payment_date">Start Date (mm/dd/yyyy)</label>
<!-- Please note that an input of type date has a date picker in Chrome and Firefox, not in Safari or IE -->
<!-- Also, Chrome and Firefox will store the value as yyyy-mm-dd, where as Safari and IE will store the value as typed by the user -->
<input type="date" id="first_payment_date" title="Pick a date for payments to start." placeholder="mm/dd/yyyy" pattern="^(((0?[1-9]|1[012])\/(0?[1-9]|1\d|2[0-8])|(0?[13456789]|1[012])\/(29|30)|(0?[13578]|1[02])\/31)\/(19|[2-9]\d)\d{2}|0?2\/29\/((19|[2-9]\d)(0[48]|[2468][048]|[13579][26])|(([2468][048]|[3579][26])00)))$" class="s" value="06/04/2020" />
<button type="submit">Submit</button>
</form>
<script src="https://jstest.authorize.net/v1/Accept.js"></script>
<script src="https://unpkg.com/@sourceallies/payment-gateway@latest/dist/payment-gateway.umd.js"></script>
<script>
// Your information to link the subscriptions to your Payment Gateway account.
const gatewayData = {
partnerName: "YOUR PARTNER NAME",
gatewayName: "YOUR GATEWAY NAME"
};
// Specifies the environment to be used when Payment Gateway makes requests. This can be set to "DEV" or "PROD".
const environment = "DEV";
/**
* Handles the response of the HTTP request to obtain an Authorize.Net cipher key. This will be expanded on in the following section.
*/
function handleGetMerchantDetails(xmlHttpRequest) {
if (xmlHttpRequest && xmlHttpRequest.status && xmlHttpRequest.status === 200) { // OK response from Payment Gateway.
const responseBody = JSON.parse(xmlHttpRequest.response);
console.log(responseBody);
} else {
// You would end up in this error block if the response from Payment Gateway does not have a status of "200".
// You will want to handle this case with custom error handling for your website.
console.error("Bad response.");
}
}
/**
* Ran when the user clicks the submit button.
*/
function handleSubmit() {
paymentGateway.getMerchantDetails(handleGetMerchantDetails, gatewayData, environment);
}
</script>
```
`responseBody` will look similar to this:
```json
{
"isTestMode": false,
"processors": [{
"name": "First Data Nashville"
}],
"merchantName": "fwHGwSdCaR",
"gatewayId": "565697",
"marketTypes": [
"eCommerce"
],
"productCodes": [
"CNP"
],
"paymentMethods": [
"AmericanExpress",
"DinersClub",
"Discover",
"EnRoute",
"JCB",
"Mastercard",
"Visa"
],
"currencies": [
"USD"
],
"publicClientKey": "9aptdYwtHt2F22XLRgr4B9AM4Pkt5eb6b6MC9d2Nn3m3YEptx3RFFuXmpYWDLHev",
"messages": {
"resultCode": "Ok",
"message": [{
"code": "I00001",
"text": "Successful."
}]
}
}
```
##### Encrypting Payment Data
If `xmlHttpRequest.status` is `200`, we will need to check `responseBody.messages.resultCode`. If this does not equal `"OK"`, there was an error processing your information. In this case, you would want to handle the error on your website. The result codes for Payment Gateway can be found by logging into your Payment Gateway account here and the result codes for Authorize.Net can be found here. If we get an `"OK"` result code from Payment Gateway, we can call the function `paymentGateway.encryptPaymentData` passing in the JSON parsed body, your Authorize.Net API key, and the payment data. The payment data can be either your card or bank data and are formatted as follows:
```js
const cardData = {
cardNumber: "4111111111111111",
cardExpiration: "01/2030",
cardCode: "123",
fullName: "First Last",
zip: "50000"
};
const bankData = {
accountNumber: "111111111111",
routingNumber: "111111111",
nameOnAccount: "First Last",
accountType: "checking" // This can be "checking", "savings", or "businessChecking".
};
```
The returned data from `paymentGateway.encryptPaymentData` will then be used in the function `Accept.dispatchData`. Your form should now look like this:
```html
<form action="javascript:handleSubmit()">
<label for="invoice_number">Invoice Number</label>
<input id="invoice_number" type="text" value="1">
<label for="amount">Amount</label>
<input id="amount" type="text" value="1.00">
<label for="email_address"></label>
<input type="email" id="email_address" value="email@email.com"></label>
<label for="cardholder-name">Cardholder Name</label>
<input id="cardholder-name" type="text" value="FirstName LastName">
<label for="card-number">Card Number</label>
<input id="card-number" type="text" maxlength="16" minlength="16" value="4111111111111111">
<label for="card-expiration">Card Expiration (eg: 01/21)</label>
<input id="card-expiration" type="text" maxlength="5" minlength="4" value="01/23">
<label for="card-code">Card Code</label>
<input id="card-code" type="text" maxlength="4" minlength="3" value="123">
<label for="street-address">Street Address</label>
<input id="street-address" type="text" value="104 First Street">
<label for="city">City</label>
<input id="city" type="text" value="Urbandale">
<label for="state">State</label>
<input id="state" type="text" value="IA">
<label for="zipcode">Zip Code</label>
<input id="zipcode" type="text" minlength="5" value="12345">
<label id="frequency_label" for="frequency">Frequency</label>
<select type="text" id="frequency" value="1">
<option value="1">Monthly</option>
<option value="3">Quarterly</option>
<option value="6">Every Six Months</option>
<option value="12">Annually</option>
</select>
<label id="payment_number_label" for="payment_number">Number of Payments</label>
<input type="text" id="payment_number" value="1">
<label id="first_payment_date_label" for="first_payment_date">Start Date (mm/dd/yyyy)</label>
<!-- Please note that an input of type date has a date picker in Chrome and Firefox, not in Safari or IE -->
<!-- Also, Chrome and Firefox will store the value as yyyy-mm-dd, where as Safari and IE will store the value as typed by the user -->
<input type="date" id="first_payment_date" title="Pick a date for payments to start." placeholder="mm/dd/yyyy" pattern="^(((0?[1-9]|1[012])\/(0?[1-9]|1\d|2[0-8])|(0?[13456789]|1[012])\/(29|30)|(0?[13578]|1[02])\/31)\/(19|[2-9]\d)\d{2}|0?2\/29\/((19|[2-9]\d)(0[48]|[2468][048]|[13579][26])|(([2468][048]|[3579][26])00)))$" class="s" value="06/04/2020" />
<button type="submit">Submit</button>
</form>
<script src="https://jstest.authorize.net/v1/Accept.js"></script>
<script src="https://unpkg.com/@sourceallies/payment-gateway@latest/dist/payment-gateway.umd.js"></script>
<script>
// Your information to link the payments to your Payment Gateway account.
const gatewayData = {
partnerName: "YOUR PARTNER NAME",
gatewayName: "YOUR GATEWAY NAME"
};
// Specifies the environment to be used when Payment Gateway makes requests. This can be set to "DEV" or "PROD".
const environment = "DEV";
// Your key to access Authorize.Net's API.
const authorizeNetAPIKey = "YOUR AUTHORIZE.NET API KEY";
/**
* This function is incomplete and will be expanded on in the following section.
*/
function submitSubscriptionRequest(response) {
console.log(response);
};
/**
* Handles the response of the HTTP request to obtain an Authorize.Net cipher key.
*/
function handleGetMerchantDetails(xmlHttpRequest) {
if (xmlHttpRequest && xmlHttpRequest.status && xmlHttpRequest.status === 200) {
const responseBody = JSON.parse(xmlHttpRequest.response);
if (responseBody.messages.resultCode !== "OK") {
console.error("Bad response."); // Replace this with custom error handling for your website.
return; // This breaks out of the current function so the code below is not ran.
}
// This can be either cardData or bankData. This paymentData object is the cardData type.
// The different structures for these objects are shown above.
const paymentData = {
cardNumber: document.getElementById("card-number").value,
cardExpiration: document.getElementById("card-expiration").value,
cardCode: document.getElementById("card-code").value,
fullName: document.getElementById("cardholder-name").value,
zip: document.getElementById("zipcode").value
};
// Encrypts the payment data so it is secure when sending it to Payment Gateway.
const secureData = paymentGateway.encryptPaymentData(responseBody, authorizeNetAPIKey, paymentData);
Accept.dispatchData(secureData, submitSubscriptionRequest);
} else {
// You would end up in this error block if the response from Payment Gateway does not have a status of "200".
// You will want to handle this case with custom error handling for your website.
console.error("Bad response.");
}
}
function handleSubmit() {
paymentGateway.getMerchantDetails(handleGetMerchantDetails, gatewayData, environment);
}
</script>
```
Accept.js will perform some basic card validation on the submitted information. Authorize.Net describes this process as follows:
> While Accept.js validates the data, it doesn’t do any authorization or validation of the card. Those steps will happen later in the process when you submit the payment nonce as part of a subscription request.
If the user supplies what could be a valid card (for the test SDK, only Authorize.net specific valid Credit Card numbers will work, one of which is `4111111111111111`). The Accept.js call will return a response similar to this:
```json
{
"opaqueData": {
"dataDescriptor": "COMMON.ACCEPT.INAPP.PAYMENT",
"dataValue": "eyJjb2RlIjoiNTBfMl8wNjAwMDUzNTMzN0U0MzYwRjM1N..."
},
"messages": {
"resultCode": "Ok",
"message": [
{
"code": "I00001",
"text": "Successful."
}
]
}
}
```
However, if the user supplies credentials that are not plausible, Accept.js will return an error response. The following is an example return for when the credit card number is invalid and the expiration date has passed:
```json
{
"messages": {
"resultCode": "Error",
"message": [
{
"code": "E_WC_05",
"text": "Please provide valid credit card number."
},
{
"code": "E_WC_08",
"text": "Expiration date must be in the future."
}
]
}
}
```
##### Submitting a Subscription Request to Payment Gateway
Now that the payment nonce has been obtained (the `opaqueData` object), it is time to build a subscription request and process it with Payment Gateway. This can be done using the functions `paymentGateway.buildRequestData` and `paymentGateway.processSubscriptionRequest`. `paymentGateway.buildRequestData` takes in the response from `Accept.dispatchData`, the payment amount, the bill to information, the customer information, and the order information. `paymentGateway.processSubscriptionRequest` takes in a callback function, the request data returned from `paymentGateway.buildRequestData`, gateway data, the payment schedule, and the environment.
The `billTo`, `customer`, `order`, and `paymentSchedule` objects have the following structures:
```javascript
// Only firstName and lastName are required for billTo, but passing in all properties is strongly recommended
const billTo = {
firstName: "First",
lastName: "Last",
address: "1234 Address Street",
city: "City",
state: "State",
zip: "50000",
country: "Country"
};
const customer = {
email: "Email"
};
const order = {
description: "Description",
invoiceNumber: "SOME INVOICE NUMBER" // This is not required. If you would like to include it, you can randomly generate it.
};
const paymentSchedule = {
interval: {
length: "24",
unit: "MONTHS"
},
startDate: "mm/dd/yyyy",
totalOccurrences: "5"
};
```
Once the `requestData` is built, it is passed into `paymentGateway.processSubscriptionRequest`. The `subscriptionRequest` object is built inside this function and then sent to Payment Gateway for processing. The final structure of the request looks like this:
```javascript
const requestData = paymentGateway.buildRequestData(response, amount, billTo, customer, order);
paymentGateway.processSubscriptionRequest(callback, requestData, gatewayData, paymentSchedule, environment.toUpperCase());
```
> `paymentGateway.processSubscriptionRequest` will add a default line item to the request body sent to Authorize.Net. You can specify your own line items by passing in an additional parameter to `paymentGateway.processSubscriptionRequest`. See @sourceallies/payment-gateway documentation for more information.
Your form should now look like this:
```html
<form action="javascript:handleSubmit()">
<label for="invoice_number">Invoice Number</label>
<input id="invoice_number" type="text" value="1">
<label for="amount">Amount</label>
<input id="amount" type="text" value="1.00">
<label for="email_address"></label>
<input type="email" id="email_address" value="email@email.com"></label>
<label for="cardholder-name">Cardholder Name</label>
<input id="cardholder-name" type="text" value="FirstName LastName">
<label for="card-number">Card Number</label>
<input id="card-number" type="text" maxlength="16" minlength="16" value="4111111111111111">
<label for="card-expiration">Card Expiration (eg: 01/21)</label>
<input id="card-expiration" type="text" maxlength="5" minlength="4" value="01/23">
<label for="card-code">Card Code</label>
<input id="card-code" type="text" maxlength="4" minlength="3" value="123">
<label for="street-address">Street Address</label>
<input id="street-address" type="text" value="104 First Street">
<label for="city">City</label>
<input id="city" type="text" value="Urbandale">
<label for="state">State</label>
<input id="state" type="text" value="IA">
<label for="zipcode">Zip Code</label>
<input id="zipcode" type="text" minlength="5" value="12345">
<label id="frequency_label" for="frequency">Frequency</label>
<select type="text" id="frequency" value="1">
<option value="1">Monthly</option>
<option value="3">Quarterly</option>
<option value="6">Every Six Months</option>
<option value="12">Annually</option>
</select>
<label id="payment_number_label" for="payment_number">Number of Payments</label>
<input type="text" id="payment_number" value="1">
<label id="first_payment_date_label" for="first_payment_date">Start Date (mm/dd/yyyy)</label>
<!-- Please note that an input of type date has a date picker in Chrome and Firefox, not in Safari or IE -->
<!-- Also, Chrome and Firefox will store the value as yyyy-mm-dd, where as Safari and IE will store the value as typed by the user -->
<input type="date" id="first_payment_date" title="Pick a date for payments to start." placeholder="mm/dd/yyyy" pattern="^(((0?[1-9]|1[012])\/(0?[1-9]|1\d|2[0-8])|(0?[13456789]|1[012])\/(29|30)|(0?[13578]|1[02])\/31)\/(19|[2-9]\d)\d{2}|0?2\/29\/((19|[2-9]\d)(0[48]|[2468][048]|[13579][26])|(([2468][048]|[3579][26])00)))$" class="s" value="06/04/2020" />
<button type="submit">Submit</button>
</form>
<script src="https://jstest.authorize.net/v1/Accept.js"></script>
<script src="https://unpkg.com/@sourceallies/payment-gateway@latest/dist/payment-gateway.umd.js"></script>
<script>
// Your information to link the payments to your Payment Gateway account.
const gatewayData = {
partnerName: "YOUR PARTNER NAME",
gatewayName: "YOUR GATEWAY NAME"
};
// Specifies the environment to be used when Payment Gateway makes requests. This can be set to "DEV" or "PROD".
const environment = "DEV";
// Your key to access Authorize.Net's API.
const authorizeNetAPIKey = "YOUR AUTHORIZE.NET API KEY";
/**
* Handles the result from submitting the subscription with Payment Gateway.
*/
function handlePaymentGatewayProcessSubscriptionResponse(xmlHttpRequest) {
if (xmlHttpRequest && xmlHttpRequest.status && xmlHttpRequest.status === 200) {
const responseBody = JSON.parse(xmlHttp.response);
console.log(responseBody); // Replace this with custom response handling that checks for an "OK" responseCode.
} else {
// You would end up in this error block if the response from Payment Gateway does not have a status of "200".
// You will want to handle this case with custom error handling for your website.
console.error("Error when processing the subscription.");
}
}
/**
* Submits the subscription request to Payment Gateway.
*/
function submitSubscriptionRequest(response) {
if (response.messages.resultCode === "Error") {
console.error(response.messages.message);
return;
}
const amount = document.getElementById("amount").value
const billTo = {
firstName: document.getElementById("first_name").value,
lastName: document.getElementById("last_name").value,
address: document.getElementById("address").value,
city: document.getElementById("city").value,
state: document.getElementById("state").value,
zip: document.getElementById("zip_code").value,
country: document.getElementById("country").value
};
const customer = {
email: document.getElementById("email").value
};
const order = {
description: "Description",
invoiceNumber: "SOME INVOICE NUMBER" // This is not required. If you would like to include it, you can randomly generate it.
};
const paymentSchedule = {
interval: {
length: document.getElementById("frequency").value,
unit: "MONTHS"
},
startDate: document.getElementById("first_payment_date").value,
totalOccurrences: document.getElementById("payment_number").value
};
const requestData = paymentGateway.buildRequestData(response, amount, billTo, customer, order);
paymentGateway.processSubscriptionRequest(
handlePaymentGatewayProcessSubscriptionResponse,
requestData,
gatewayData,
paymentSchedule,
environment.toUpperCase() /*,
optionally pass in lineItems */
);
};
/**
* Handles the response of the HTTP request to obtain an Authorize.Net cipher key.
*/
function handleGetMerchantDetails(xmlHttpRequest) {
if (xmlHttpRequest && xmlHttpRequest.status && xmlHttpRequest.status === 200) {
const responseBody = JSON.parse(xmlHttpRequest.response);
if (responseBody.messages.resultCode !== "OK") {
console.error("Bad response."); // Replace this with custom error handling for your website.
return; // This breaks out of the current function so the code below is not ran.
}
// This can be either cardData or bankData. This paymentData object is the cardData type.
// The different structures for these objects are shown above.
const paymentData = {
cardNumber: document.getElementById("card-number").value,
cardExpiration: document.getElementById("card-expiration").value,
cardCode: document.getElementById("card-code").value,
fullName: document.getElementById("cardholder-name").value,
zip: document.getElementById("zipcode").value
};
// Encrypts the payment data so it is secure when sending it to Payment Gateway.
const secureData = paymentGateway.encryptPaymentData(responseBody, authorizeNetAPIKey, paymentData);
Accept.dispatchData(secureData, submitSubscriptionRequest);
} else {
// You would end up in this error block if the response from Payment Gateway does not have a status of "200".
// You will want to handle this case with custom error handling for your website.
console.error("Bad response.");
}
}
function handleSubmit() {
paymentGateway.getMerchantDetails(handleGetMerchantDetails, gatewayData, environment);
}
</script>
```
A good subscription will return something like this:
```json
{
"refId": null,
"messages": {
"resultCode": "OK",
"message": [
{
"code": "I00001",
"text": "Successful."
}
]
},
"sessionToken": null,
"subscriptionId": "6563673",
"profile": {
"customerProfileId": "1511529929",
"customerPaymentProfileId": "1511415094",
"customerAddressId": null
}
}
```
Declined subscriptions will look something like this:
```json
{
"refId": null,
"messages": {
"resultCode": "ERROR",
"message": [
{
"code": "E00012",
"text": "You have submitted a duplicate of Subscription 6563720. A duplicate subscription will not be created."
}
]
},
"sessionToken": null,
"subscriptionId": null,
"profile": null
}
```
Notice that in both cases, the result is located in the `messages` object. This message block will indicate if there is something wrong with the submitted subscription request.
Error codes in the `message` array starting with SAI are from Source Allies. All other error codes are from Authorize.Net. For help with their response codes, see Authorize.Net's Response Codes page.
> It has been noted that the SDK for Authorize.Net sometimes responds with __Invalid OTS Token. This generally means something went wrong with Authorize.Net. A solution has been found during some instances of this error by adding a `setTimeout` or `sleep` up to 4-5 seconds after making the call to `Accept.dispatchData` and before calling `paymentGateway.processSubscriptionRequest`.