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