LawPay Pament Integration

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

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. 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 fieldConfig 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 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)

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 ' "
}