Skip to content

Embedded Forms

Embedded forms allow you to collect payments directly on your website while keeping sensitive card data off your servers.

How It Works

  1. Load the GovPayPlan JavaScript SDK
  2. Create a payment form container
  3. Initialize the secure payment fields
  4. Handle form submission
  5. Process the payment

Benefits

  • Seamless experience: Payers stay on your site
  • Full customization: Match your design
  • Reduced PCI scope: Card data handled securely
  • Better conversion: No redirect friction

Quick Start

Step 1: Include the SDK

html
<script src="https://js.govpayplan.com/v1/sdk.js"></script>

Step 2: Create Form Container

html
<form id="payment-form">
  <div id="card-element"></div>
  <div id="card-errors"></div>
  <button type="submit">Pay $100.00</button>
</form>

Step 3: Initialize the SDK

javascript
const gpp = GovPayPlan.init({
  publishableKey: 'gpp_pub_abc123'
});

const cardElement = gpp.elements.create('card');
cardElement.mount('#card-element');

Step 4: Handle Submission

javascript
const form = document.getElementById('payment-form');

form.addEventListener('submit', async (e) => {
  e.preventDefault();

  const { token, error } = await gpp.createToken(cardElement);

  if (error) {
    document.getElementById('card-errors').textContent = error.message;
    return;
  }

  // Send token to your server
  await fetch('/api/charge', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      token: token.id,
      amount: 10000 // cents
    })
  });
});

Step 5: Process on Server

javascript
// Your server endpoint
app.post('/api/charge', async (req, res) => {
  const { token, amount } = req.body;

  const payment = await govpayplan.payments.create({
    amount: amount,
    currency: 'usd',
    source: token,
    description: 'Permit Fee'
  });

  res.json({ success: true, payment });
});

Element Types

Card Element

All-in-one card input:

javascript
const card = gpp.elements.create('card');

Split Elements

Individual fields for custom layouts:

javascript
const cardNumber = gpp.elements.create('cardNumber');
const cardExpiry = gpp.elements.create('cardExpiry');
const cardCvc = gpp.elements.create('cardCvc');

Styling Elements

Using Options

javascript
const card = gpp.elements.create('card', {
  style: {
    base: {
      fontSize: '16px',
      color: '#333',
      fontFamily: 'Arial, sans-serif',
      '::placeholder': {
        color: '#aab7c4'
      }
    },
    invalid: {
      color: '#dc3545',
      iconColor: '#dc3545'
    }
  }
});

Available Style Properties

PropertyDescription
colorText color
fontSizeFont size
fontFamilyFont family
fontWeightFont weight
letterSpacingLetter spacing
lineHeightLine height
::placeholderPlaceholder styles

CSS Classes

The element container receives classes based on state:

ClassWhen Applied
.gpp-emptyNo input
.gpp-focusElement focused
.gpp-invalidValidation error
.gpp-completeValid and complete

Event Handling

Change Events

javascript
cardElement.on('change', (event) => {
  if (event.error) {
    errorDiv.textContent = event.error.message;
  } else {
    errorDiv.textContent = '';
  }

  // Enable submit when complete
  submitBtn.disabled = !event.complete;
});

Focus Events

javascript
cardElement.on('focus', () => {
  // Element received focus
});

cardElement.on('blur', () => {
  // Element lost focus
});

Form Validation

Built-in Validation

The SDK validates:

  • Card number (Luhn check)
  • Expiration date
  • CVV format

Custom Validation

javascript
form.addEventListener('submit', async (e) => {
  e.preventDefault();

  // Custom validation
  const amount = document.getElementById('amount').value;
  if (amount < 1) {
    showError('Amount must be at least $1');
    return;
  }

  // Proceed with payment
  const { token, error } = await gpp.createToken(cardElement);
  // ...
});

ACH/Bank Payments

Bank Account Element

javascript
const bankAccount = gpp.elements.create('bankAccount');
bankAccount.mount('#bank-element');

Collecting Bank Info

javascript
const { token, error } = await gpp.createToken(bankAccount, {
  accountHolderName: 'John Doe',
  accountHolderType: 'individual' // or 'company'
});

Error Handling

Common Errors

CodeDescription
invalid_numberCard number invalid
invalid_expiryExpiration date invalid
invalid_cvcCVV invalid
card_declinedCard was declined
processing_errorTemporary error

Display Errors

javascript
cardElement.on('change', (event) => {
  const errorElement = document.getElementById('card-errors');

  if (event.error) {
    errorElement.textContent = event.error.message;
    errorElement.style.display = 'block';
  } else {
    errorElement.style.display = 'none';
  }
});

Security

Tokenization

Card data is tokenized client-side. Tokens:

  • Are single-use
  • Expire after 10 minutes
  • Cannot be used to retrieve card details

PCI Compliance

Using embedded forms reduces your PCI scope to SAQ A-EP:

  • Card data never touches your servers
  • Secure iframe handles sensitive input
  • All data encrypted in transit

GovPayPlan - Secure Payment Processing for Government Agencies