LawPay Payment Processing 2

Authentication API

https://developers.affinipay.com/reference/api.html#AuthenticationAPI

1 Request token
  • You can access merchant account details after you receive an access token. You must use your partner OAuth application client ID and secret to make this call.
  • This endpoint returns an access_token when you POST a valid grant_type parameter.
  • Note: Before you can call the AffiniPay Payment Gateway API and manage transactions on a merchant’s behalf, your partner OAuth application must use the access token to request gateway credentials.
ParameterDescription
client_idA unique 64-character, alphanumeric string used to identify your partner OAuth application in OAuth 2.0 web flows.
client_secretA unique 64-character, alphanumeric string used to authenticate your partner OAuth application in OAuth 2.0 web flows.
grant_typeSpecifies which grant type to use in the OAuth 2.0 web flow. Set this to authorization_code.
scopeDetermines the level of access your partner OAuth application has to the merchant’s account. Set this to payments.
redirect_uriPer the OAuth 2.0 specification, this is the URI to which the AffiniPay web application will redirect after successful authorization. Your web server must handle redirects to this URI according to the OAuth 2.0 specification.
codeThe authorization_code that the Authentication API sends to the redirect URI you specified in your partner OAuth application after a user logs in and authorizes your application.
curl -X POST -H "Content-Type:application/json" https://api.affinipay.com/oauth/token -d '
{
  "client_id":"Y4QLWPO6wZag2ia8Abw7nbeLUAOgebDlfZGF1KyzgBaqAllzMtYFfP58jRxg5rp5",
  "client_secret":"4EG357enIs4m2SWKi9yfC3fQzIeOZmBTWr96ay47tqN4GUtRPYrWwxwCxwlZJbiC",
  "grant_type":"authorization_code",
  "scope":"payments",
  "redirect_uri":"http://localhost:9292/callback",
  "code":"EiKvFkJu6rcFwOMWSqW8bWIng6EMFVD93duwn1QhgQKDvmpbA97zWFN2AfC5052R"
}'

Or for PHP script

// Generated by curl-to-PHP: http://incarnate.github.io/curl-to-php/
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, 'https://api.affinipay.com/oauth/token');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "\n{\n  \"client_id\":\"Y4QLWPO6wZag2ia8Abw7nbeLUAOgebDlfZGF1KyzgBaqAllzMtYFfP58jRxg5rp5\",\n  \"client_secret\":\"4EG357enIs4m2SWKi9yfC3fQzIeOZmBTWr96ay47tqN4GUtRPYrWwxwCxwlZJbiC\",\n  \"grant_type\":\"authorization_code\",\n  \"scope\":\"payments\",\n  \"redirect_uri\":\"http://localhost:9292/callback\",\n  \"code\":\"EiKvFkJu6rcFwOMWSqW8bWIng6EMFVD93duwn1QhgQKDvmpbA97zWFN2AfC5052R\"\n}");

$headers = array();
$headers[] = 'Content-Type: application/json';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$result = curl_exec($ch);
if (curl_errno($ch)) {
    echo 'Error:' . curl_error($ch);
}
curl_close($ch);

This endpoint returns an access_token when you POST a valid grant_type parameter.

Example Response

{
  "access_token":"Msp2VL7SEGbLxT8UBWww7WUy33hLsg5Yhf5fFu8znSpPh2BbBMvXPyQkZx5TtWHd",
  "token_type":"bearer",
  "scope":"payments",
  "created_at":1464986958
}

You can access merchant account details after you receive an access token. You must use your partner OAuth application client ID and secret to make this call.

Request Gateway Credentials
curl -X GET -H "Authorization: Bearer <access_token>" https://api.affinipay.com/gateway-credentials

This endpoint returns a list of the test and live accounts associated with the authenticated user’s merchant account. Included in this information are the necessary keys for making calls to the AffiniPay Payment Gateway API. Save these credentials securely so your partner OAuth application can manage transactions on behalf of merchant users.

Payment Gateway API – Merchant Management

https://developers.affinipay.com/reference/api.html#PaymentGatewayAPI

Overview
  • The AffiniPay Payment Gateway provides a REST API to facilitate payment processing for Merchants.
  • API supports both credit card payments and electronic check (eCheck) payments, which move funds electronically between banks using routing and account numbers.
    It uses simple URLs and proven HTTP constructs, simplifying integration with third-party applications and existing web sites.
  • All interactions with the Gateway are carried out over a secure connection using TLS 1.2 to safeguard data contents, and all requests are authenticated using HTTP.
  • All request and response content is formatted using JSON, including error information.
Security
  • All merchants are provided with a public key and test- and live-mode secret keys to use when authenticating with the Gateway.
  • All requests to the Gateway are authenticated using HTTP Basic Authentication.
  • To authenticate, you provide either your test- or live-mode secret key in the HTTP Authorization header.
  • basic access authentication is a method for an HTTP user agent (e.g. a web browser) to provide a user name and password when making a request.
    In basic HTTP authentication, a request contains a header field in the form of Authorization: Basic <credentials>, where credentials is the Base64 encoding of ID and password joined by a single colon :
    • In each request, be sure to always base64-encode the public or secret key plus a trailing colon.
      For example, if your public key is: m_cSiyNUh9RBWHfdbFRFGrkA, you’ll base64-encode m_cSiyNUh9RBWHfdbFRFGrkA:. The result is bV9jU2l5TlVoOVJCV0hmZGJGUkZHcmtCOg==, which you’ll use in your request header.
curl --user secret_key: https://https://api.affinipay.com/...
  • Using the curl command line tool used in the API examples, you set this header using the –user flag, followed by the secret key, and then a trailing colon (:).
    • Your secret keys provide access to sensitive operations affecting payments, and therefore must be closely guarded.
    • These keys should never be exposed in any publicly-accessible web pages, JavaScript, etc.
    • If you suspect your test- or live-mode secret keys have been compromised, contact your Gateway account provider immediately to reset your secret keys.
  • In order to protect sensitive information, including your credentials and the contents of API requests and responses, the Gateway only accepts encrypted communications using HTTPS.

Test vs Live Mode

  • The secret key you specify when making an API request affects how the operation is processed.
    • The test-mode secret key only allows you to create new charges and other transactions against TEST merchant bank accounts, while the live-mode secret key only works in conjunction with merchant bank accounts associated with live payment processing networks.
      Attempts to mix secret keys and merchant bank accounts will fail.
  • Similarly, the secret key you provide filters the transactions you see when searching or retrieving specific transactions.
    • The test-mode secret key only returns transactions created against TEST merchant bank accounts, and the live-mode secret key only returns transactions submitted to the payment processing networks.

Restricting Client Access

  • A Merchant that accesses the Gateway via an intermediate application (for example, a hosted web-based application) can set their configuration to restrict the API from accepting requests from any other sources.
    • The IP address restrictions are configured by setting the api_allowed_ip_address_ranges property on the Merchant.
  • The countries from which transaction operations (Authorize, Void, Capture, Refund, and Credit) are allowed may also be restricted to specific countries. Countries are determined by performing a geographic lookup by IP address.
    • Country restrictions are enforced on a per-Account basis via the transaction_allowed_countries property.
Test Card Numbers

Test Card Numbers

Successful charges can be created using the following card numbers in conjunction with the test-mode secret key and a card expiration in the future:

Card NumberCard Type
378282246310005American Express
371449635398431American Express
5105105105105100MasterCard
5499740000000057MasterCard
30569309025904Diners Club
38520000023237Diners Club
6011000990139424Discover
3530111333300000JCB
3566002020360505JCB
4242424242424242Visa
4012888888881881Visa
4400000000000008Visa Debit

Failure scenarios can be tested using the following card numbers and the test-mode secret key:

Card NumberError Code
4000000000000002card_declined
4000000000000044card_declined_insufficient_funds
4000000000000051card_declined_limit_exceeded
4000000000000069card_expired
4000000000000119card_declined_processing_error
4000000000000127card_declined_hold
4242424242424241card_number_invalid
4000000000000135card_type_not_accepted
6011111111111117503 PROCESSOR NOT AVAILABLE
5555555555554444500 SERVER ERROR

CVV and AVS checks can be tested against various account policies using the following card numbers and the test-mode secret key. Note that depending on the policy in effect, the CVV and AVS results may or may not result in an error code being returned. For example, if the CVV policy is set to DISABLED, a CVV Not Matched response will not cause a failure, whereas it would if the CVV policy were set to REQUIRE_MATCH_LENIENT.

Card NumberCVV/AVS Response
4000000000000077CVV Unavailable
4000000000000101CVV Not Matched
4000000000000010AVS Not Matched
4000000000000085AVS Unavailable
4000000000000093AVS Error
4000000000000028If card postal code is null or empty, AVS returns Not Matched. Otherwise, an AVS Postal Code Match is returned.
4000000000000036If card address is null or empty, AVS returns Not Matched. Otherwise an AVS Address Match is returned.

Test Bank Account Numbers

Using a TEST bank account, you can capture eCheck transfers using the routing numbers 000000000 or 000000013 with any account number. The following exceptions return eCheck errors useful for testing purposes:

Routing NumberAccount NumberError Code
0000000131000000001ach_declined
0000000131000000002ach_declined_hold
0000000131000000003ach_declined_duplicate
0000000131000000004ach_invalid_account_number
0000000131000000005ach_insufficient_funds
0000000131000000006ach_account_not_found
0000000131000000007ach_account_closed
0000000131000000008ach_account_frozen
0000000131000000009ach_limit_exceeded
000000001Anyach_invalid_routing_number
0000000131000000004422 INVALID ACCOUNT NUMBER
0000000131000000011503 ACH PROCESSOR NOT AVAILABLE
0000000131000000012500 SERVER ERROR
System Status

System Status

GET /status

Integrating systems can retrieve the current status of the Gateway by issuing a GET request to /status. The operation returns an enumeration indicating the basic status, as detailed in the table below. This operation, which requires no authentication, can be used to check connectivity to the Gateway as well as to identify potential problems that may impact payment processing.

StatusDescription
NORMALThe gateway is functioning normally.
EXCESSIVE_LATENCYAPI request durations are exceeding expected thresholds.
SERVICE_UNRELIABLERecent intermittent failures are impacting payment processing.
SERVICE_UNAVAILABLEThe system us experiencing problems that prevent payment processing.

Example Request

curl https://https://api.affinipay.com/status

Example Response

{
  "status":"NORMAL"
}
Get Current Merchant

Get Current Merchant

GET /v1/merchant

Returns the currently-authenticated merchant. This operation can be used to test authenticating to the Gateway using a Merchant’s credentials. Note that the merchant and eCheck accounts returned are filtered based on whether a live or test-mode secret key was provided. Only TEST accounts are returned if a test-mode secret key is used, while a live-mode secret key only returns live accounts.

curl -X GET --user secret_key: https://https://api.affinipay.com/v1/merchant
Purge Test Data

Purge Test Data

POST /v1/merchant/purge-test-data

Testing your integration via our API can result in a lot of data being generated associated with your account: payment methods, transactions, recurring charges, etc. This data can sometimes make it more difficult to verify that your integration is working as expected. Using the /v1/merchant/purge-test-data endpoint, you can discard all of your test data and return to a clean slate.

curl -X POST -H "Content-Type:application/json" --user secret_key: https://https://api.affinipay.com/v1/merchant/purge-test-data
Payment Gateway API – Transactions

https://developers.affinipay.com/reference/api.html#PaymentGatewayAPI

  • The Gateway’s streamlined transaction API simplifies the process of submitting payments and applying refunds to those payments.
    • The Charge API submits a payment request using a customer’s payment details.
      The Gateway automatically performs funds capture daily and initiates settlement. 
    • Refund can be performed up to six months after the Charge, in any number, up to the amount of the Charge.
  • The API also includes methods for retrieving individual transactions by ID, as well as a search operation that returns paginated results based on criteria specified by the caller.
    • Note: To avoid exposing credentials, always make API calls from the server, not from the browser.
Charges

Charges

POST /v1/charges

  • The /v1/charges API submits a payment request from a customer using the payment method included in the request.
  • If no account ID is specified in the request, the Gateway automatically selects the primary merchant or eCheck account, depending on the type of payment method specified; for example, providing a bank method results in the primary eCheck account being selected. If no matching account is found, or if an account ID is provided but does not match the payment method, the charge is rejected.
  • By default, all successful authorizations are captured automatically daily for all the Merchant’s accounts at a time configured by the Merchant’s Tenant. For credit card payments, separate authorization and capture can be performed by specifying auto_capture: false in the JSON request, resulting in a hold being placed on the customer’s funds without automatic capture. Transactions must be captured or voided using the corresponding APIs; otherwise they are automatically voided by the Gateway after 7 days.

Creating a charge with automatic capture

curl -X POST -H "Content-Type:application/json" --user secret_key: https://api.affinipay.com/v1/charges -d '
  {
      "amount": "100",
      "account_id": "wKgFeDz5HF-BPPl08dcADQ",
      "method": {
          "type": "card",
          "number": "4242424242424242",
          "exp_month": 10,
          "exp_year": 2022,
          "cvv": "123",
          "name": "Some Customer"
      }
  }'

Creating a charge debiting an individuals bank account

curl -X POST -H "Content-Type:application/json" --user secret_key: https://api.affinipay.com/v1/charges -d '
  {
      "amount": "46531",
      "account_id": "7eyWpvRpEeKcOfUBJK8FBw",
      "method": {
          "type": "bank",
          "routing_number": "000000013",
          "account_number": "1100000005",
          "account_type": "CHECKING",
          "given_name": "Amy",
          "surname": "Customer"
      }
  }'


Create payment. Form

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 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/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" crossorigin="anonymous"
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><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 configobject 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. The fieldConfig objects are passed to the config object as an array under the fields key.
    • A credit card payment page must have one fieldConfig of type credit_card_number and one of type cvv.
    • An eCheck payment page must have one fieldConfig of type bank_account_number one of type routing_number.
    In each fieldConfig object, set the selector key to the id of the corresponding <div> in the payment form.

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 and fieldConfigobjects. 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%"
        }
      }
    }
  ]
}

Disable or enable fields

To disable and enable inputs use the following methods, passing the value from the field selector property in your hostedFieldsConfiguration object.

const hostedFields = window.AffiniPay.HostedFields.initializeFields(hostedFieldsConfiguration, hostedFieldsCallback)
hostedFields.disableIframeInput("#my_credit_card_field_id")
hostedFields.enableIframeInput("#my_credit_card_field_id")

Focus a field

To focus an input use the following method, passing the value from the field selector property in your hostedFieldsConfiguration object.

const hostedFields = window.AffiniPay.HostedFields.initializeFields(hostedFieldsConfiguration, hostedFieldsCallback)
hostedFields.focusIframeInput("#my_credit_card_field_id")

Clear a field

To clear an input use the following method, passing the value from the field selector property in your hostedFieldsConfiguration object.

const hostedFields = window.AffiniPay.HostedFields.initializeFields(hostedFieldsConfiguration, hostedFieldsCallback)
hostedFields.clearIframeInput("#my_credit_card_field_id")

To clear all iframes, call hostedFields.clearIframeInput() without any arguments.

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)
}

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 be true if all hosted fields have tokenized successfully. After all fields are tokenized, you can call getPaymentToken(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)

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)
  })
}

COPY

Your payment page must also handle the payment response returned from your web server, displaying any errors or updating the page with a receipt.

Example Page – 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 Page – Credit Card & eCheck

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>