PaymentElements Integration

Collect card and ACH credentials in Preczn-hosted iframes and exchange them for tokens, without sensitive data ever touching your web app.

Overview

Preczn PaymentElements is a client-side browser SDK that renders card and ACH inputs inside Preczn-origin iframes. Sensitive account data is captured, validated, and tokenized entirely within Preczn's infrastructure — your page only receives a token. This significantly reduces your PCI scope and qualifies eligible integrators for SAQ A.

PaymentElements is the recommended successor to PaymentFields when stronger PCI scope reduction is required. Each input field is hosted in its own iframe served by Preczn, and the SDK coordinates them on your behalf so you can style, observe, and tokenize them with a small JavaScript surface.

ℹ️ PaymentElements vs. PaymentFields

Use PaymentFields for a lightweight, drop-in script that intercepts your existing inputs. Use PaymentElements when you need iframed inputs for SAQ A eligibility or stronger isolation between your page and cardholder data.

Implementation Steps

Step 1: Include the Script

Add the secure JavaScript reference to your page's <head> with your public API key:

<script src="https://api.preczn.com/v1/clients/preczn.min.js?resources=paymentelements&publicApiKey=2482re32338cd9z5048v7bhmb5"></script>

The script auto-bootstraps on DOMContentLoaded and exposes its API as window.Preczn.PaymentElements.

Step 2: Define Container Elements

Instead of <input> elements, PaymentElements mounts iframes into container <div> elements that you mark with data-preczn attributes. The SDK auto-discovers them on load and watches the DOM for containers added later.

Card form:

<div data-preczn="number"     data-preczn-label="Card number"></div>
<div data-preczn="expiration" data-preczn-label="Expiration (MM/YY)"></div>
<div data-preczn="cvv"        data-preczn-label="CVV"></div>

ACH form:

<div data-preczn="accountNumber" data-preczn-label="Account number"></div>
<div data-preczn="routingNumber" data-preczn-label="Routing number"></div>
<div data-preczn="accountType"   data-preczn-label="Account type"></div>
<div data-preczn="bankCountry"   data-preczn-label="Bank country"></div>

Supported data-preczn values:

ValueElement renderedPurpose
numberCard number inputCard PAN entry
expirationExpiration inputMM/YY entry
cvvCVV input3–4 digit security code
accountNumberACH inputBank account number
routingNumberACH inputBank routing number
accountTypeNative pickerchecking, savings, etc.
bankCountryNative pickerUS, CA

⚠️ Do not put id or name attributes on these containers, and do not nest them inside a <form> element. PaymentElements iframes never expose values to the parent page; placing them in a form can cause browsers to intercept submission attempts.

The container's width and height determine the iframe's size — apply your own layout, padding, and border styles to the container, and use configure() (Step 3) to style the input inside the iframe.

Step 3: Configure Styles and Placeholders

Call configure() to style the inputs inside the iframes and set placeholder text. Styles are applied across all mounted fields and can be updated at any time.

Preczn.PaymentElements.configure({
  style: {
    base: {
      color: '#1a1a1a',
      fontFamily: 'system-ui, -apple-system, sans-serif',
      fontSize: '16px',
      fontWeight: '400',
      lineHeight: '24px',
      '::placeholder': {
        color: '#9ca3af'
      }
    },
    focus:    { color: '#0f172a' },
    invalid:  { color: '#dc2626' },
    complete: { color: '#16a34a' }
  },
  placeholders: {
    number:        '1234 5678 9012 3456',
    expiration:    'MM / YY',
    cvv:           'CVC'
  }
});

Pseudo-states: base, focus, invalid, complete, empty. The ::placeholder selector accepts color and fontStyle.

Allowed style properties: color, fontFamily, fontSize, fontWeight, fontStyle, fontSmoothing, lineHeight, letterSpacing, textAlign, padding, textTransform.

Constraints (enforced for security):

  • fontFamily must be drawn from: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif, serif, monospace.
  • fontSize must be in the range 8–48px or 0.5–3rem.
  • Properties not on the allowlist (e.g., background, marginTop, url(...)) are silently dropped.

ℹ️ Calling configure() before any container is mounted is fine — the configuration is applied to each field as it mounts. You can call configure() again later to update styles or placeholders live.

Step 4: Listen for Field Events (optional)

Subscribe to per-field events to drive UI affordances (e.g., a brand icon, a "valid" checkmark, an error message under a field):

Preczn.PaymentElements.on('number', 'change', function (state) {
  // state = { empty, complete, brand, error }
  if (state.brand) {
    document.getElementById('brand-icon').className = 'brand-' + state.brand.toLowerCase();
  }
});

Preczn.PaymentElements.on('cvv', 'change', function (state) {
  document.getElementById('cvv-error').textContent = state.error || '';
});

Available events:

EventPayloadFires when
ready{}Field iframe has mounted and is usable
change{ empty, complete, brand?, error? }User input changes the field state
focus{}Field gains focus
blur{}Field loses focus

Remove a handler with Preczn.PaymentElements.off(field, event, handler).

If you need to wait until every field is mounted before enabling a submit button:

Preczn.PaymentElements.ready().then(function () {
  document.getElementById('submit').disabled = false;
});

Step 5: Obtain a Token

When the customer submits the form, request a token. PaymentElements collects values from the iframes, validates them, posts them to Preczn's tokenization endpoint, and invokes your callback with the result.

Define a callback function:

var tokenCallback = function (result, errors) {
  if (errors) {
    console.error('Tokenization failed', errors);
    return;
  }
  var precznToken = result.token;
  // Send precznToken to your server to charge or store
};

Card token:

Preczn.PaymentElements.getSingleUseToken(tokenCallback);

ACH token:

Preczn.PaymentElements.getSingleUseAchToken(tokenCallback);

Utility Functions

FunctionReturns
getCardBrand()Card brand string once a card number has been entered, otherwise null. Possible values: VISA, MASTERCARD, AMEX, DISCOVER/JCB, OTHER.
isValidCardNumber()true once the entered card number passes Luhn and length checks; false if entered but invalid; null if no input has been received yet.
ready()A Promise<void> that resolves once every registered field has mounted.
if (Preczn.PaymentElements.isValidCardNumber()) {
  console.log('Brand:', Preczn.PaymentElements.getCardBrand());
}

Security Notes

  • Always use public API keys (priv_test… / priv_live_…); never use private/secret keys in browser code.
  • Sensitive values (PAN, CVV, account/routing numbers) never enter your page's JavaScript context, DOM, storage, or postMessage channels — they only exist inside Preczn-served iframes.
  • The SDK coordinates with iframes via authenticated cross-frame channels and requires that messages originate from the SDK it boots; spoofed postMessage traffic from other origins is ignored.
  • Your page's Content Security Policy must allow:
    • script-src https://api.preczn.com
    • frame-src https://api.preczn.com
    • connect-src https://api.preczn.com
  • Do not use id, name, or wrap PaymentElements containers in a <form>. Mixing PaymentElements containers with PaymentFields-style inputs in the same form is unsupported and will log a console warning.
  • For a full PCI Shared Responsibility Matrix and SAQ A attestation package, contact your Preczn account team.

Did this page help you?