Payment Information
In this document we are going to show you how to collect, store, and use payment information on Invoiced through your own website or application.
Introduction
When implementing Invoiced into your app or website you often want control over the billing and payment UIs in order to provide customers with a seamless experience. Invoiced v2 tokenization allows you to collect and vault payment information using your own forms while still relying on Invoiced to manage payments. As a result of letting Invoiced handle payment information you can also enjoy simplified PCI compliance.
The way that it works is that within your customer's web browser you can tokenize your customer's payment information that can later be passed to our API in order to save the payment information for future billing or payments. The v2 tokenization system is compatible with Flywire Payments and has been approved by Flywire security.
The key to a secure implementation is to use the Invoiced v2 tokenization scheme. Tokenization gives you a randomly generated token ID that you can pass to your servers that references the payment information collected. The benefit of this approach is to prevent you from directly handling sensitive payment information.
With v2 tokenization, the payment information is sent to Invoiced on the client-side using our JavaScript library through secure iframe containers. When the payment information is sent in you receive a short-lived, single-use token that references the captured payment information, which can be a card or bank account. You can then use that payment info token in our API to vault it on a customer's account or use it to process a payment.
It's important to consider that payment info tokens will only work with your Invoiced account and last for a maximum of 2 hours. Once a token is used it cannot be re-used. As such you should not be storing tokens on your end.
Invoiced v2 Tokenization Guide
Overview
This guide covers setting up Invoiced v2 tokenization for Flywire Payments. The v2 implementation is compatible with Flywire Payments and has been approved by Flywire security.
Choose your path:
- First-time setup? → Follow the First-Time SetupFirst-Time Setup section
- Migrating from v1? → Follow the Migration from v1 section
First-Time Setup
If you're implementing Invoiced tokenization for the first time, follow these steps:
Step 1: Provide Domain Allowlist
For security, provide the Flywire Invoiced team with a complete list of domains where you'll collect payment information:
- Your main website domain
- Any subdomains (e.g., checkout.yourwebsite.com)
Example domains to include:
- https://yourwebsite.com
- https://www.yourwebsite.com
- https://checkout.yourwebsite.com
Step 2: Get Your Credentials
Contact the Flywire Invoiced team to receive:
- Publishable Key - Unique to your company
- Client Key - For Flywire Invoiced integration
- Domain Allowlist Setup - You'll need to provide your domains (see Step 1)
Step 3: Add the Script Tag
Add the Invoiced v2 script to your HTML page:
<script src="https://tokenization.invoiced.com/invoiced.js" crossorigin="anonymous"></script>⚠️ Important: The crossorigin="anonymous" attribute is required.
Step 4: Configure Invoiced
Initialize Invoiced with your credentials:
Invoiced.create(
{
publishableKey: "YOUR_PUBLISHABLE_KEY", // From Flywire Invoiced team
clientKey: "YOUR_CLIENT_KEY", // From Flywire Invoiced team
payFacEnv: "live",
invoicedUrl: "https://tknz.invoiced.com",
},
{
styles: styleObject, // Your styling configuration
},
handleCardToken, // Success handler for card tokenization
handleACHToken // Success handler for ACH tokenization
);Migration from v1
If you're upgrading from Invoiced v1, here's what you need to change:
What Changes
- Domain Allowlist - Provide your domains to the Flywire Invoiced team (see Step 1: Provide Domain AllowlistFirst-Time Setup - Step 1)
- Get New Credentials - Contact the Flywire Invoiced team for:
- New Publishable Key
- New Client Key
- Confirmation that your domains are allowlisted
- Script URL - Update to the v2 endpoint:
<!-- OLD v1 Script (Remove this) -->
<script src="https://js.invoiced.com/invoiced.js"></script>
<!-- NEW v2 Script (Use this) -->
<script src="https://tokenization.invoiced.com/invoiced.js" crossorigin="anonymous"></script>- Configuration - Update your Invoiced.create() config:
Invoiced.create(
{
publishableKey: "YOUR_NEW_PUBLISHABLE_KEY", // ← Get new key from Flywire Invoiced team
clientKey: "YOUR_CLIENT_KEY", // ← Get new key from Flywire Invoiced team
payFacEnv: "live",
invoicedUrl: "https://tknz.invoiced.com", // ← Updated URL (was different in v1)
},
// ... rest of your config stays the same
);What Stays the Same
✅ Code structure - Your existing implementation structure remains unchanged ✅ Styling configuration - Your styleObject works exactly the same ✅ Event handlers - Your success/error handlers don't need changes ✅ Tokenization methods - Invoiced.card.tokenize() and Invoiced.bankAccount.tokenize() work the same ✅ Form structure - Your HTML form structure remains identical
Complete Example
Here's a complete working example:
<!DOCTYPE html>
<html>
<head>
<title>Invoiced v2 Tokenization</title>
<script src="https://tokenization.invoiced.com/invoiced.js" crossorigin="anonymous"></script>
<style>
.iframe-container {
height: 40px;
width: 300px;
border: 1px solid #ccc;
box-sizing: border-box;
display: block;
margin-bottom: 20px;
border-radius: 4px;
}
.iframe-container iframe {
width: 100%;
height: 100%;
border: none;
display: block;
}
</style>
</head>
<body>
<h1>Payment Information</h1>
<div id="customCard-container">
<label>
<span>Card Number:</span>
<span class="iframe-container" data-cse="encryptedCardNumber"></span>
</label>
<label>
<span>Expiry Date:</span>
<span class="iframe-container" data-cse="encryptedExpiryDate"></span>
</label>
<label>
<span>CVV/CVC:</span>
<span class="iframe-container" data-cse="encryptedSecurityCode"></span>
</label>
<button id="tokenize-button">Submit Payment</button>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Style configuration
const styleObject = {
base: {
height: '30px',
background: '#ffffff',
caretColor: '#007bff',
color: '#333333',
display: 'block',
fontSize: '16px',
fontSmoothing: 'antialiased',
webkitFontSmoothing: 'antialiased',
mozOsxFontSmoothing: 'grayscale',
fontWeight: '400',
letterSpacing: 'normal',
lineHeight: '1.5',
textAlign: 'left',
outline: 'none',
opacity: '1',
padding: '0 5px',
transition: 'all 0.2s ease-in-out',
},
error: {
color: '#d9534f',
caretColor: '#d9534f',
background: '#fff3f3',
outline: '1px solid #d9534f',
},
validated: {
color: '#28a745',
caretColor: '#28a745',
background: '#f3fff3',
outline: '1px solid #28a745',
}
};
// Success handler for card tokenization
function handleCardToken(statusCode, response) {
if (statusCode >= 400) {
console.error('Tokenization failed:', response.message);
alert('Payment failed: ' + response.message);
return;
}
console.log('Success! Token:', response.invoiced_token);
// Send token to your backend for payment processing
processPayment(response.invoiced_token);
}
// Success handler for ACH tokenization
function handleACHToken(statusCode, response) {
if (statusCode >= 400) {
console.error('ACH tokenization failed:', response.message);
return;
}
console.log('ACH Success! Token:', response.invoiced_token);
processPayment(response.invoiced_token);
}
// Initialize Invoiced v2
Invoiced.create(
{
publishableKey: "YOUR_PUBLISHABLE_KEY_FROM_FLYWIRE_INVOICED_TEAM",
clientKey: "YOUR_CLIENT_KEY_FROM_FLYWIRE_INVOICED_TEAM",
payFacEnv: "live",
invoicedUrl: "https://tknz.invoiced.com",
},
{
styles: styleObject,
},
handleCardToken,
handleACHToken
);
// Button click handler
document.getElementById('tokenize-button').addEventListener('click', function() {
const customerName = 'John Doe'; // Get from your form
const billingAddress = {
address1: '123 Main St',
address2: null,
city: 'New York',
state: 'NY',
postal_code: '10001',
country: 'US'
};
// Tokenize the card
Invoiced.card.tokenize(customerName, billingAddress);
});
// Payment processing function
function processPayment(token) {
// Send token to your backend API
fetch('/api/process-payment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
invoiced_token: token,
// ... other payment data
})
})
.then(response => response.json())
.then(data => {
console.log('Payment processed:', data);
// Handle success
})
.catch(error => {
console.error('Payment error:', error);
// Handle error
});
}
});
</script>
</body>
</html>Testing Limitations
⚠️ Important: Full end-to-end testing is not available for v2 tokenization.
What you CAN verify:
- ✅ Script Loading: Confirm the new script loads without console errors
- ✅ Form Rendering: Verify payment form fields display correctly
- ✅ Domain Setup: Check that you can click into payment fields (indicates proper CORS/allowlisting)
What you CANNOT do:
- ❌ Complete Tokenization: No sandbox environment supports full token generation
- ❌ Test Card Numbers: Standard test cards will not work with v2
- ❌ End-to-End Flow: Cannot complete full payment flow until production
Why this limitation exists: Invoiced v2 does not support Flywire Payments in sandbox environments, so testing must be done directly in production.
Recommendation:
- Carefully follow all setup steps in this guide
- Verify your domain allowlisting with the Flywire Invoiced team before going live
- Have your development team ready to troubleshoot quickly if issues arise in production
Troubleshooting
Common Issues
1. Form fields won't accept input
- Cause: CORS error - your domain isn't allowlisted
- Solution: Contact the Flywire Invoiced team to add your domain to the allowlist
2. 500 error when submitting
- Cause: Wrong credentials or domain not allowlisted
- Solution: Verify your publishable key and client key with the Flywire Invoiced team
3. Script won't load
- Cause: Missing crossorigin="anonymous" attribute
- Solution: Add the crossorigin attribute to your script tag
Getting Help
If you encounter issues:
- Check the browser console for error messages
- Verify your credentials are correct for your environment
- Confirm your domain is in the allowlist provided to the Flywire Invoiced team
- Contact the Flywire Invoiced team with specific error messages
Setup Checklist
Added script tag with v2 URL and crossorigin="anonymous" attribute
Provided complete domain list to the Flywire Invoiced team
Received publishable key from the Flywire Invoiced team
Received client key from the Flywire Invoiced team
Updated configuration with credentials
Verified domain allowlisting is complete
Tested form rendering and field interaction
Updated any documentation or deployment scripts
Support
For technical support:
- Contact the Flywire Invoiced Support Team
- Include browser console errors in any support requests
- Specify your current environment (first-time setup or migration)
Need help? This setup should be straightforward, but if you run into issues, don't hesitate to reach out to the Flywire Invoiced team for assistance.