https://developers.affinipay.com/quickstart/quickstart.html
- You can integrate your website or application with AffiniPay, ClientPay, CPACharge, or LawPay to take payments securely using the AffiniPay Payment Platform.
- AffiniPay’s integration tasks are written for developers;
- for a successful integration, you should be familiar with writing code that interacts with REST endpoints using the HTTP protocol.
1 Connect to the AffiniPay Payment Platform
- To take payments for yourself, connect using API keys
- When you run transactions through the AffiniPay Payment Gateway, you need your:
- Public key that identifies you to AffiniPay.
- Secret keys that authenticate API requests made on your behalf.
- Account IDs that specify the credit account or eCheck account associated with each transaction.
2 Create payment forms using hosted fields
https://developers.affinipay.com/collect/create-payment-form-hosted-fields.html
- AffiniPay hosted fields protect your payment page by tokenizing payment information. They provide SAQ-A PCI compliance, the highest level of PCI compliance. Hosted fields are <iframe> elements sourced from AffiniPay’s servers that contain a single corresponding <input> element. These iframes replace the input elements on your form that contain payment information.
- Hosted fields should be integrated into your site by a developer. This integration requires web developer skills as well as the ability to pass information to a backend server and then to AffiniPay. If you have questions, contact AffiniPay Support.
Hosted Fields Versioning
- With version 1.4.0, hosted fields now supports enabling surcharge, disabling and enabling fields, focusing on a field, and clearing a field.
- The AffiniPay hosted fields JavaScript library (
fieldGen.js
) is versioned. The versioning URL is: - https://cdn.affinipay.com/hostedfields/
/fieldGen_ .js - For example, the URL for version 1.4.0 is:
- https://cdn.affinipay.com/hostedfields/1.4.0/fieldGen_1.4.0.js
- For SRI and
integrity
attribute purposes, here are the SHA384 digests: - 1.4.0 Release
integrity="sha384-2LuR5n01lipNZjmgOGn8snFZbtajEtxHUI057u3NHA/Q7rJkVeP0qmLknnZ2YsZ4
To create a payment page with hosted fields:
1: Add a reference to the AffiniPay hosted fields JavaScript library
On your payment page, add a reference to the AffiniPay hosted fields library.
<script src="https://cdn.affinipay.com/hostedfields/1.4.0/fieldGen_1.4.0.js"></script>
The AffiniPay hosted fields library binds the following functions to the window.AffiniPay.HostedFields
namespace:
window.AffiniPay.HostedFields.initializeFields(config, callback)
to instantiate hosted fields. This is the only function you are required to call to use AffiniPay hosted fields.window.AffiniPay.HostedFields.parseException(hostedFieldException)
to parse exception objects thrown by the hosted fields library.window.AffiniPay.HostedFields.exceptionMap(errorCode)
to look up an exception type of an error code thrown by the hosted fields library.
2: Create a Payment Form
Create an HTML form and replace the <input> elements that contain payment information with <div> elements. On each <div>, set an id
that will be used when configuring the hosted field. Your payment page must additionally include all required fields and other payment page requirements.
- For credit card payments, card number and CVV <input> elements must be replaced with hosted fields <div> elements.
- For eCheck payments, account number and routing number <input> elements must be replaced with hosted fields <div> elements.
Here’s an example:
<div>
<label for="my_credit_card_field_id">Credit Card</label>
<div id="my_credit_card_field_id"></div>
</div>
<div>
<label for="exp_month">Exp Month</label>
<input id="exp_month" type="text" name="exp_month">
</div>
<div>
<label for="exp_year">Exp Year</label>
<input id="exp_year" type="text" name="exp_year">
</div>
<div>
<label for="my_cvv_field_id">CVV</label>
<div id="my_cvv_field_id"></div>
</div>
<div>
<label for="postal_code">Zip Code</label>
<input id="postal_code" type="text" name="postal_code">
</div>
<input type="submit" value="Submit" />
3: Create a hosted fields configuration object
The initializeFields
function must be called with a valid config object and a user-defined callback function (in the Create a user-defined callback step). The config
object is the only way to configure hosted fields. The config
object contains keys that apply to all hosted field as well as individual fieldConfig
objects that apply to individual hosted fields.
A valid config
object includes:
- The merchant’s public key, which is required to tokenize payment details on behalf of the merchant. The merchant’s public key is safe to expose in web pages (as opposed to secret keys, which must be safeguarded).
- An optional
input
key that contains styling that applies to all hosted fields. - Two
fieldConfig
objects that contain individual hosted field configurations. ThefieldConfig
objects are passed to theconfig
object as an array under thefields
key.- A credit card payment page must have one
fieldConfig
of typecredit_card_number
and one of typecvv
. - An eCheck payment page must have one
fieldConfig
of typebank_account_number
one of typerouting_number
.
fieldConfig
object, set theselector
key to theid
of the corresponding <div> in the payment form. - A credit card payment page must have one
The window.AffiniPay.HostedFields.initializeFields
function returns an object with the following methods. Be sure to save this object.
- The
getState()
method returns the current tokenization state of hosted fields. - The
getPaymentToken(formData)
method gets a payment token by passing your hosted field tokens and form data to your AffiniPay backend services. This is required for completing a charge. - The
updateSavedPaymentMethod(formData)
uses the the ID of a previously saved payment method from setSavedPaymentMethod (either Card or Bank) and a form data JSON object of fields to be updated and returns a one-time token to be used for updating a saved payment method. - The
updateSavedPaymentMethod(formData, paymentMethodId)
takes the ID of a saved payment method (either Card or Bank) and a form data JSON object of fields to be updated and returns a one-time token to be used for updating a saved payment method. This call doesn’t require a previous call to setSavedPaymentMethod. - The
setSavedPaymentMethod(paymentMethod)
sets a saved payment method. This function will disable hosted fields and attempt to populate your payment form with data from the saved payment method. It can be used with updateSavedPaymentMethod to avoid passing in a paymentMethodId to updateSavedPaymentMethod. - The
clearSavedPaymentMethod()
clears a currently saved payment method. This will enable hosted fields and clear input elements.
Here’s an example:
const creditCardFieldConfig = {
selector: "#my_credit_card_field_id",
input: {
type: "credit_card_number"
}
}
const cvvFieldConfig = {
selector: "#my_cvv_field_id",
input: {
type: "cvv"
}
}
const hostedFieldsConfiguration = {
publicKey: 'm_1234567890',
fields: [
creditCardFieldConfig,
cvvFieldConfig,
]
}
const hostedFieldsCallback = function (state) {
console.log(JSON.stringify(state, null, 2))
}
const hostedFields = window.AffiniPay.HostedFields.initializeFields(hostedFieldsConfiguration, hostedFieldsCallback)
// Save the return value of initializeFields for later use
// hostedFields.getState()
// hostedFields.getPaymentToken(formData)
4: Style the hosted fields
Although it is not required, styling is recommended.
- To style your page and the <iframe> elements (including their size), use your stylesheet.
- For the content inside the hosted fields—the <input> element contained within the <iframe>, provide styling information within the
config
andfieldConfig
objects. Styling within the hosted fields is defined (from highest to lowest precedence) by:fieldConfig
styling,config
styling, hosted fields library defaults, and browser defaults.
Within the css
key for a config
or fieldConfig
object, you can:
- Pass any valid CSS as a JSON object.
- Style input states (such as :focus, :valid, and :invalid) by passing in a nested style object.
- Style placeholder (::placeholder) text by passing in a nested style object.
Here’s an example of hosted field styling:
const hostedFieldsConfiguration = {
publicKey: "m_1234567890",
input: {
css: {
'font-family': 'serif',
'font-size': '22px',
'color': '#0BEEF0', // fieldConfig css will overwrite this value
':focus': { color: 'orange' },
':invalid': { background: 'antiquewhite' },
':valid': { color: 'blanchedalmond' }
}
},
fields: [
{
selector: "#my_credit_card_field_id",
input: {
type: "credit_card_number",
css: {
"border": "1px solid rgb(204, 204, 204)",
"color": "#000",
"font-size": "30px",
"font-weight": "200",
"width": "100%",
"::placeholder": { color: 'green' }
}
}
},
{
selector: "#my_cvv_field_id",
input: {
type: "cvv",
css: {
"border": "1px solid rgb(204, 204, 204)",
"border-style": 'inset',
"color": "blue",
"font-size": "11px",
"font-weight": "400",
"padding": "8px",
':invalid': { color: 'purple' },
"width": "100%"
}
}
}
]
}
5: Create a user-defined callback
The second parameter to window.AffiniPay.HostedFields.initializeFields
is a user-defined callback function. The callback function is called with a single argument state
, which is a JavaScript object containing the tokenization state for all hosted fields. The bulk of your JavaScript logic that interacts with hosted fields is likely to be contained within this callback function.
Here’s an example:
const hostedFieldsCallBack = function(state) {
console.log(state)
}
COPY
The callback function will be called when the following input events occur:
- valid keydown
- paste
- focus
- blur
The callback will also be called in intervals and when trying to recover from network failure.
- interval
- retry
The state argument passed to your callback function has three keys.
isReady
is a flag that will betrue
if all hosted fields have tokenized successfully. After all fields are tokenized, you can callgetPaymentToken(formData)
.target
is the the state of the hosted field that generated the most recent event. This is useful for CSS changes. The target is the only way the parent page knows a user has navigated into and out of a hosted field.fields
is an array of all hosted fields states.
Here’s an example of the state
argument passed to the callback function:
{
"isReady": false,
"target": {
"selector": "#my_credit_card_field_id",
"token": "TZiDwi9kJdhQRBVuL41F7LSspCDP1j7h",
"type": "credit_card_number",
"card": "visa",
"luhn": true,
"error": "",
"focus": true
},
"fields": [
{
"selector": "#my_credit_card_field_id",
"token": "TZiDwi9kJdhQRBVuL41F7LSspCDP1j7h",
"type": "credit_card_number",
"card": "visa",
"luhn": true,
"error": "",
"focus": true
},
{
"selector": "#my_cvv_field_id",
"token": "M9Wte44S2p5smwqmsjFTJNuOTQ4OgSoH",
"type": "cvv",
"error": "",
"focus": false
}
]
}
6: Initialize hosted fields
To initialize hosted fields, call window.AffiniPay.HostedFields.initializeFields(config, callback)
.
const hostedFields = window.AffiniPay.HostedFields.initializeFields(config, callback)
COPY
Tokenization happens automatically and is triggered by events. The token will be visible in the state object passed to your callback function. Do not use or interact with these tokens directly; they appear on the state object in your callback function for visual feedback only.
Tokenization occurs on keydown and paste events if the input passes validation. Additionally tokenization occurs at intervals to ensure tokens have not expired. In the event of a network failure, a tokenization retry will be done with an exponential backoff.
7: Get a payment token
When the state object passed to your callback function has isReady
set to true
, all fields have tokenized successfully and you can call getPaymentToken(formData)
. You must include the required fields in your formData
argument when calling getPaymentToken
. The getPaymentToken(formData)
function trades the hosted field tokens and other required parameters for a payment token from AffiniPay backend services.
When you create a charge, you will pass this payment token to your own backend services, which will make the charge.
Here’s an example:
form.onsubmit = function(event) {
event.preventDefault()
console.log(hostedFields.getState())
if(!hostedFields.getState()) {
//send error
return
}
hostedFields.getPaymentToken({ "postal_code": postal_code, "exp_year": exp_year, "exp_month": exp_month })
.then(function(result) {
console.log(result.id)
// If getPaymentToken returns successfully you may pass your payment token to your backend service.
}).catch(function(err) {
console.log(err)
})
}
Example Credit Card
Example page: credit card
Here’s an example of a payment form that includes hosted fields for credit card payments and the information required to support them.
Use this example as a guideline as you develop your own payment form. It is not intended to be a turnkey solution.
<html>
<head>
<title>Hosted Fields Payment Page</title>
<script src="https://cdn.affinipay.com/hostedfields/1.4.0/fieldGen_1.4.0.js"></script>
<style type="text/css">
form {
width: 500px;
margin: 0 auto;
}
form input, form iframe {
width: 100%;
margin: 5px;
}
form iframe {
border: none;
height: 20px;
}
</style>
</head>
<body>
<form id="form">
<div>
<label for="my_credit_card_field_id">Credit Card</label>
<div id="my_credit_card_field_id"></div>
</div>
<div>
<label for="exp_month">Exp Month</label>
<input id="exp_month" type="text" name="exp_month">
</div>
<div>
<label for="exp_year">Exp Year</label>
<input id="exp_year" type="text" name="exp_year">
</div>
<div>
<label for="my_cvv_field_id">CVV</label>
<div id="my_cvv_field_id"></div>
</div>
<div>
<label for="postal_code">Zip Code</label>
<input id="postal_code" type="text" name="postal_code">
</div>
<input type="submit" value="Submit" />
</form>
<script type="text/javascript">
const style = {
border: "1px solid rgb(204, 204, 204)",
'border-style': 'inset',
color: "#000",
"font-size": "11px",
"font-weight": "400",
padding: "8px",
width: "100%"
};
const hostedFieldsConfiguration = {
publicKey: "m_1234567890",
fields: [
{
selector: "#my_credit_card_field_id",
input: {
type: "credit_card_number",
css: style
}
},
{
selector: "#my_cvv_field_id",
input: {
type: "cvv",
css: style
}
}
]
};
const hostedFieldsCallBack = function(state) {
console.log(state);
};
const hostedFields = window.AffiniPay.HostedFields.initializeFields(
hostedFieldsConfiguration,
hostedFieldsCallBack
);
form.onsubmit = function(event) {
event.preventDefault();
console.log(hostedFields.getState())
const postal_code = document.getElementById('postal_code').value
const exp_year = document.getElementById('exp_year').value
const exp_month = document.getElementById('exp_month').value
if(!hostedFields.getState()){
//send error
return;
}
hostedFields.getPaymentToken({ "postal_code": postal_code, "exp_year": exp_year, "exp_month": exp_month })
.then(function(result){
console.log(result.id)
// If getPaymentToken returns successfully you may pass your payment token to your backend service.
}).catch(function(err){
console.log(err);
});
};
</script>
</body>
</html>
Example Credit Card & Check
Example page: credit card and eCheck payments
Note: If your payment page allows both credit card and eCheck payments, it must have separate config objects, initializeFields calls, and getPaymentToken calls for each.
Here’s an example of a payment form that includes hosted fields for credit card and eCheck payments and the information required to support them.
Use this example as a guideline as you develop your own payment form. It is not intended to be a turnkey solution.
<html>
<head>
<title>Hosted Fields Payment Page</title>
<script src="https://cdn.affinipay.com/hostedfields/1.4.0/fieldGen_1.4.0.js"></script>
<style type="text/css">
form {
margin: 0 auto;
}
form input, form iframe {
width: 100%;
margin: 5px;
}
form iframe {
border: none;
height: 20px;
}
</style>
</head>
<body>
<form id="form" name="form">
<table>
<tr>
<td>
<table width="100%">
<tr>
<td>
<input type="radio" id="card_radio" name="payment_type_radio" value="card">
</td>
<td>
<label for="card_radio">Credit Card</label>
</td>
<td width="20px"></td>
<td>
<input type="radio" id="bank_radio" name="payment_type_radio" value="bank" >
</td>
<td>
<label for="bank_radio">eCheck</label>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<div>
<label for="my_credit_card_field_id">Credit Card</label>
<div id="my_credit_card_field_id"></div>
</div>
<div>
<label for="exp_month">Exp Month</label>
<input id="exp_month" type="text" name="exp_month">
</div>
<div>
<label for="exp_year">Exp Year</label>
<input id="exp_year" type="text" name="exp_year">
</div>
<div>
<label for="my_cvv_field_id">CVV</label>
<div id="my_cvv_field_id"></div>
</div>
<div>
<label for="postal_code">Zip Code</label>
<input id="postal_code" type="text" name="postal_code">
</div>
</td>
<td width="20px">
</td>
<td>
<table width="100%">
<tr>
<td>
<input type="radio" id="business_radio" name="account_holder_type_radio" value="business">
</td>
<td>
<label for="business_radio">Business</label>
</td>
<td>
<input type="radio" id="personal_radio" name="account_holder_type_radio" value="personal">
</td>
<td>
<label for="personal_radio">Personal</label>
</td>
</tr>
</table>
<div>
<label for="my_routing_number_id">Routing Number</label>
<div id="my_routing_number_id"></div>
</div>
<div>
<label for="my_bank_account_number_id">Account Number</label>
<div id="my_bank_account_number_id"></div>
</div>
<table width="100%">
<tr>
<td>
<input type="radio" name='account_type_radio' id="checking_radio" value="checking">
</td>
<td>
<label for="checking_radio">Checking</label>
</td>
<td>
<input type="radio" name='account_type_radio' id="savings_radio" value="savings">
</td>
<td>
<label for="savings_radio">Savings</label>
</td>
</tr>
</table>
<div>
<label for="given_name">First Name</label>
<input id="given_name" type="text" name="given_name">
</div>
<div>
<label for="surname">Last Name</label>
<input id="surname" type="text" name="surname">
</div>
<div>
<label for="business_name">Business Name</label>
<input id="business_name" type="text" name="business_name">
</div>
</td>
</tr>
</table>
<table>
<tr>
<td>
<input type="submit" value="Submit"/>
</td>
</tr>
</table>
</form>
<script type="text/javascript">
const style = {
border: "1px solid rgb(204, 204, 204)",
'border-style': 'inset',
color: "#000",
"font-size": "11px",
"font-weight": "400",
padding: "8px",
width: "100%"
};
const creditCardHostedFieldsConfig = {
publicKey: "m_1234567890",
fields: [
{
selector: "#my_credit_card_field_id",
input: {
type: "credit_card_number",
css: style
}
},
{
selector: "#my_cvv_field_id",
input: {
type: "cvv",
css: style
}
}
]
};
const eCheckHostedFieldsConfig = {
publicKey: 'm_1234567890',
css: style,
fields: [
{
selector: '#my_bank_account_number_id',
placeholder: 'Bank Account Number',
input: {
type: 'bank_account_number',
css: style
}
},
{
selector: '#my_routing_number_id',
input: {
type: 'routing_number',
css: style
}
}
]
};
const hostedFieldsCallBack = function (state) {
console.log(state);
};
const creditCardHostedFields = window.AffiniPay.HostedFields.initializeFields(
creditCardHostedFieldsConfig,
hostedFieldsCallBack
);
const eCheckHostedFields = window.AffiniPay.HostedFields.initializeFields(
eCheckHostedFieldsConfig,
hostedFieldsCallBack
);
const form = document.getElementById('form');
form.onsubmit = function (event) {
event.preventDefault();
console.log(creditCardHostedFields.getState());
console.log(eCheckHostedFields.getState());
if (form['card_radio'].checked && creditCardHostedFields.getState()) {
const postal_code = document.getElementById('postal_code').value
const exp_year = document.getElementById('exp_year').value
const exp_month = document.getElementById('exp_month').value
creditCardHostedFields.getPaymentToken({"postal_code": postal_code, "exp_year": exp_year, "exp_month": exp_month})
.then(function (result) {
console.log(result.id);
// If getPaymentToken returns successfully you may pass your payment token to your backend service.
}).catch(function (err) {
console.log(err);
});
} else if (form['bank_radio'].checked && eCheckHostedFields.getState()) {
const given_name = document.getElementById('given_name').value
const surname = document.getElementById('surname').value
const business_name = document.getElementById('business_name').value
const isBusiness = document.getElementById('business_radio').checked
const isSavings = document.getElementById('savings_radio').checked
const extra_fields = {}
extra_fields.account_holder_type = isBusiness ? 'business' : 'individual'
extra_fields.account_type = isSavings ? 'savings' : 'checking'
if (business_radio.checked) {
extra_fields.name = business_name
} else {
extra_fields.given_name = given_name
extra_fields.surname = surname
}
console.log(JSON.stringify(extra_fields))
eCheckHostedFields.getPaymentToken(extra_fields).then(function (result) {
console.log(result.id);
// If getPaymentToken returns successfully you may pass your payment token to your backend service.
}).catch(function (err) {
console.log(err);
});
} else {
console.log('error');
}
};
</script>
</body>
</html>
3 Create a Charge
Connect to Affinipay Gateway
Collect & Secure Payment Details
Create a Charge
To authenticate, you’ll need the merchant secret key. Using the test-mode secret key and a test account, you can start using the API immediately without actually submitting charges to the payment processing networks. When you’re ready to start processing payments, just switch to your live-mode secret key and a live account.
When you POST to the charges endpoint, you’ll include:
- amount. The total payment amount in cents. For example: $500.00 = 50000.
- method. The ID of the payment method to use to run the payment. In this case, this payment method is the id property received on the one-time token JSON object collected in your payment form.
- account_id. The ID of the AffiniPay merchant account that should receive payment. Because a merchant can have more than one account, you should set this value for each transaction.
Example request
curl -X POST -H "Content-Type:application/json" --user <secret_key>: https://api.affinipay.com/v1/charges -d '
{
"amount":"100",
"method":" . $payment_token_id . ",
"account_id":" ' $lawpay_account_id ' "
}