ACH PaymentFields Integration

Securely collect bank account details using the client-side PaymentFields library

The PaymentFields client-side JavaScript library enables secure collection and tokenization of bank account data directly in the browser. This approach eliminates the need for your servers to handle sensitive banking information, reducing your data security burden and simplifying compliance with NACHA data protection requirements.

How It Works

  1. Customer enters bank account details into your form
  2. PaymentFields encrypts the data client-side
  3. The encrypted data is sent directly to Preczn
  4. A secure token is returned for use in transactions

Integration Steps

Step 1: Include the PaymentFields Script

Add the PaymentFields library to your page's <head> section:

<head>
  <script src="https://api.preczn.com/v1/clients/paymentFields.min.js"></script>
</head>

Step 2: Create the Payment Form

Create a form with the required fields. Each field must use data-preczn attributes to identify the field type.

<form id="paymentForm">
  <!-- Account Type -->
  <label for="accountType">Account Type</label>
  <select data-preczn="accountType">
    <option value="">Select account type</option>
    <option value="personalChecking">Personal Checking</option>
    <option value="personalSavings">Personal Savings</option>
    <option value="corporateChecking">Corporate Checking</option>
    <option value="corporateSavings">Corporate Savings</option>
  </select>

  <!-- Routing Number -->
  <label for="routingNumber">Routing Number</label>
  <input type="text" data-preczn="routingNumber" placeholder="9 digits" maxlength="9">

  <!-- Account Number -->
  <label for="accountNumber">Account Number</label>
  <input type="text" data-preczn="accountNumber" placeholder="4-17 digits">

  <!-- Bank Country (hidden or visible) -->
  <input type="hidden" data-preczn="bankCountry" value="USA">

  <button type="button" onclick="tokenize()">Submit Payment</button>
</form>

Security Requirement: Form controls with data-preczn attributes must NOT have id or name attributes. This prevents sensitive data from being captured by form serialization or logging.

Additional Required Fields

PaymentFields only tokenizes bank account data. You must separately collect and submit these fields with your transaction request:

Account TypeRequired FieldsNotes
Personal (personalChecking, personalSavings)firstName, lastNameCollect using standard form inputs
Corporate (corporateChecking, corporateSavings)accountBusinessNameBusiness name on the account
Braintree processorachMandateCustomer authorization text (required)

These fields should use standard name attributes since they're submitted to your server, not to PaymentFields:

<!-- For personal accounts -->
<input type="text" name="firstName" placeholder="First Name">
<input type="text" name="lastName" placeholder="Last Name">

<!-- For corporate accounts -->
<input type="text" name="accountBusinessName" placeholder="Business Name">

<!-- For Braintree (display authorization text and capture acceptance) -->
<label>
  <input type="checkbox" name="achMandateAccepted" required>
  I authorize [Your Company] to debit my bank account for the amount specified.
</label>

Step 3: Define the Callback Function

Create a callback function to handle the tokenization response:

function tokenCallback(result) {
  if (result.errors) {
    // Handle validation errors
    result.errors.forEach(function(error) {
      console.error('Validation error:', error.field, error.message);
    });
    displayErrors(result.errors);
    return;
  }

  // Success - use the token
  var token = result.token;
  console.log('Token received:', token);

  // Send token to your server for processing
  submitToServer({
    token: token,
    type: result.type,
    last4: result.accountNumber // Last 4 digits only
  });
}

Step 4: Initiate Tokenization

Call the PaymentFields library with your public API key:

function tokenize() {
  Preczn.PaymentFields.getAchToken(
    "pk_your_public_api_key",  // Your public API key
    "paymentForm",             // Form element ID
    tokenCallback              // Callback function
  );
}

Response Format

Successful Tokenization

{
  "type": "personalChecking",
  "accountNumber": "6789",
  "routingNumber": "987654321",
  "bankCountry": "USA",
  "token": "tkn_apkwm89dr8br9zvqw4b3xgktk"
}
FieldDescription
typeAccount type selected by the customer
accountNumberLast 4 digits of the account number (masked)
routingNumberFull routing number
bankCountryBank country code
tokenSecure token for use in transactions

Note: The PaymentFields client library returns accountNumber containing the last 4 digits. API transaction responses use last4 for the same data.

Error Response

{
  "errors": [
    {
      "field": "routingNumber",
      "message": "Routing number must be exactly 9 digits"
    },
    {
      "field": "accountNumber",
      "message": "Account number must be between 4 and 17 digits"
    }
  ]
}

Field Validation Requirements

FieldRequirementError Message
accountTypeMust be one of the valid account types"Account type must be one of: PersonalChecking, PersonalSavings, CorporateChecking, CorporateSavings"
routingNumberExactly 9 numeric digits"Routing number must be exactly 9 digits"
accountNumber4-17 numeric digits"Account number must be between 4 and 17 digits"
bankCountryMust be "USA""Bank country must be one of: USA"

Content Security Policy

If your site uses Content Security Policy headers, add these directives:

script-src: https://api.preczn.com
connect-src: https://api.preczn.com

Complete Example

<!DOCTYPE html>
<html>
<head>
  <title>ACH Payment</title>
  <script src="https://api.preczn.com/v1/clients/paymentFields.min.js"></script>
  <style>
    .error { color: red; font-size: 12px; }
    .form-group { margin-bottom: 15px; }
    input, select { width: 100%; padding: 8px; margin-top: 4px; }
  </style>
</head>
<body>
  <h2>Pay with Bank Account</h2>

  <form id="paymentForm">
    <div class="form-group">
      <label>Account Type</label>
      <select data-preczn="accountType" required>
        <option value="">Select account type</option>
        <option value="personalChecking">Personal Checking</option>
        <option value="personalSavings">Personal Savings</option>
        <option value="corporateChecking">Business Checking</option>
        <option value="corporateSavings">Business Savings</option>
      </select>
      <div class="error" data-error="accountType"></div>
    </div>

    <div class="form-group">
      <label>Routing Number</label>
      <input type="text" data-preczn="routingNumber"
             placeholder="9-digit routing number" maxlength="9" required>
      <div class="error" data-error="routingNumber"></div>
    </div>

    <div class="form-group">
      <label>Account Number</label>
      <input type="text" data-preczn="accountNumber"
             placeholder="Account number" required>
      <div class="error" data-error="accountNumber"></div>
    </div>

    <input type="hidden" data-preczn="bankCountry" value="USA">

    <button type="button" onclick="tokenize()">Pay Now</button>
  </form>

  <div id="result"></div>

  <script>
    function clearErrors() {
      document.querySelectorAll('.error').forEach(function(el) {
        el.textContent = '';
      });
    }

    function displayErrors(errors) {
      errors.forEach(function(error) {
        var el = document.querySelector('[data-error="' + error.field + '"]');
        if (el) {
          el.textContent = error.message;
        }
      });
    }

    function tokenCallback(result) {
      clearErrors();

      if (result.errors) {
        displayErrors(result.errors);
        return;
      }

      // Token received successfully - use safe DOM methods
      var resultEl = document.getElementById('result');
      resultEl.textContent = 'Token received: ' + result.token;
      resultEl.style.color = 'green';

      // Send to your server
      fetch('/api/process-payment', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          token: result.token,
          accountType: result.type,
          accountLast4: result.accountNumber
        })
      });
    }

    function tokenize() {
      clearErrors();
      Preczn.PaymentFields.getAchToken(
        "pk_your_public_api_key",
        "paymentForm",
        tokenCallback
      );
    }
  </script>
</body>
</html>

Best Practices

  1. Always validate client-side first - Check that all required fields are filled before calling getAchToken()
  2. Display clear error messages - Map validation errors to user-friendly messages near the relevant fields
  3. Never store the token in local storage - Send it directly to your server
  4. Use HTTPS - Always serve your payment pages over HTTPS
  5. Handle network errors - Wrap the tokenization call in try-catch for network failure handling