Skip to main content

Collecting Card Information

Use Omise.js to securely collect card details directly from your website while keeping sensitive data off your servers.

Overviewโ€‹

Omise.js is a client-side JavaScript library that enables secure card tokenization directly from your website. This approach ensures that sensitive payment data never touches your server, significantly reducing your PCI compliance burden.

How Tokenization Worksโ€‹

Key Benefits:

  • Card data goes directly to Omise's PCI-certified servers
  • Your server only receives a single-use token
  • Reduced PCI compliance requirements
  • Enhanced security for your customers

Implementation Stepsโ€‹

Step 1: Include Omise.js Libraryโ€‹

Add the Omise.js script before the closing </body> tag:

<script src="https://cdn.omise.co/omise.js"></script>
HTTPS Required

Omise.js requires HTTPS on your checkout page. Use TLS 1.2 or higher for optimal security.

Step 2: Create Payment Formโ€‹

Build your HTML form with card input fields:

<form id="payment-form">
<div class="form-group">
<label for="card-name">Cardholder Name</label>
<input type="text" id="card-name" placeholder="John Doe" required />
</div>

<div class="form-group">
<label for="card-number">Card Number</label>
<input type="text" id="card-number" placeholder="4242 4242 4242 4242" required />
</div>

<div class="form-row">
<div class="form-group">
<label for="expiry-month">Expiry Month</label>
<input type="text" id="expiry-month" placeholder="12" maxlength="2" required />
</div>

<div class="form-group">
<label for="expiry-year">Expiry Year</label>
<input type="text" id="expiry-year" placeholder="2027" maxlength="4" required />
</div>

<div class="form-group">
<label for="cvv">CVV</label>
<input type="text" id="cvv" placeholder="123" maxlength="4" required />
</div>
</div>

<!-- Optional billing address (improves authorization rates) -->
<div class="form-group">
<label for="postal-code">Postal Code</label>
<input type="text" id="postal-code" placeholder="10110" />
</div>

<button type="submit" id="pay-button">Pay Now</button>
<div id="error-message" style="color: red;"></div>
</form>

Step 3: Set Your Public Keyโ€‹

Initialize Omise.js with your public key:

Omise.setPublicKey("pkey_test_YOUR_PUBLIC_KEY");
Test vs Live Keys

Use test keys (pkey_test_...) during development and live keys (pkey_...) in production. Never use live keys in test environments!

Step 4: Handle Form Submissionโ€‹

Create token on form submission:

document.getElementById('payment-form').addEventListener('submit', function(e) {
e.preventDefault();

// Disable submit button to prevent double submission
const payButton = document.getElementById('pay-button');
payButton.disabled = true;
payButton.textContent = 'Processing...';

// Create card token
Omise.createToken("card", {
name: document.getElementById('card-name').value,
number: document.getElementById('card-number').value,
expiration_month: parseInt(document.getElementById('expiry-month').value),
expiration_year: parseInt(document.getElementById('expiry-year').value),
security_code: document.getElementById('cvv').value,
postal_code: document.getElementById('postal-code').value
}, function(statusCode, response) {
if (statusCode === 200) {
// Success! Token created
handleToken(response.id);
} else {
// Error occurred
handleError(response.message);
payButton.disabled = false;
payButton.textContent = 'Pay Now';
}
});
});

function handleToken(token) {
// Send token to your server
fetch('/checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
// Include CSRF token for security (framework-dependent)
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]')?.content
},
body: JSON.stringify({
token: token,
amount: 10025, // Amount in smallest unit (e.g., cents)
currency: 'thb'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Redirect to success page
window.location.href = '/payment-success';
} else {
handleError(data.error);
}
})
.catch(error => {
handleError('Network error occurred');
});
}

function handleError(message) {
document.getElementById('error-message').textContent = message;
const payButton = document.getElementById('pay-button');
payButton.disabled = false;
payButton.textContent = 'Pay Now';
}

Step 5: Process Payment on Serverโ€‹

Receive the token on your server and create a charge:

const express = require('express');
const omise = require('omise')({
secretKey: 'skey_test_YOUR_SECRET_KEY'
});

app.post('/checkout', async (req, res) => {
try {
const { token, amount, currency } = req.body;

const charge = await omise.charges.create({
amount: amount,
currency: currency,
card: token
});

if (charge.status === 'successful') {
res.json({ success: true, chargeId: charge.id });
} else {
res.json({ success: false, error: charge.failure_message });
}
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});

Security Best Practicesโ€‹

1. Never Store Card Data on Your Serverโ€‹

Critical Security Rule

NEVER log, store, or transmit raw card data through your server. Always use tokens.

// โŒ BAD: Sending card data to your server
fetch('/checkout', {
body: JSON.stringify({
cardNumber: '4242424242424242', // DON'T DO THIS!
cvv: '123' // DON'T DO THIS!
})
});

// โœ… GOOD: Sending only the token
fetch('/checkout', {
body: JSON.stringify({
token: 'tokn_test_...' // This is safe!
})
});

2. Use HTTPS Everywhereโ€‹

  • Minimum TLS 1.2 required
  • Apply to entire site, not just checkout
  • Use Let's Encrypt for free certificates

3. Implement CSP Headersโ€‹

<meta http-equiv="Content-Security-Policy"
content="script-src 'self' https://cdn.omise.co;">

4. Disable Analytics on Checkoutโ€‹

Prevent accidental card data capture:

// Disable Google Analytics on checkout page
window['ga-disable-UA-XXXXXX-Y'] = true;

5. Implement CSRF Protectionโ€‹

Always include CSRF tokens in payment requests to prevent cross-site request forgery attacks:

// Client-side: Include CSRF token in requests
fetch('/checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({ token: tokenId })
});
<!-- Add CSRF meta tag in your page head -->
<meta name="csrf-token" content="<?php echo $_SESSION['csrf_token']; ?>">

Server-side validation examples:

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

app.post('/checkout', csrfProtection, async (req, res) => {
// CSRF validation automatic with middleware
const { token, amount } = req.body;
// Process payment...
});

6. Clear Form After Submissionโ€‹

function clearForm() {
document.getElementById('payment-form').reset();
// Or remove values programmatically
document.getElementById('card-number').value = '';
document.getElementById('cvv').value = '';
}

Improving Authorization Ratesโ€‹

Collect Billing Addressโ€‹

Omise.createToken("card", {
// Required fields
name: "John Doe",
number: "4242424242424242",
expiration_month: 12,
expiration_year: 2027,
security_code: "123",

// Optional but recommended for better authorization
city: "Bangkok",
postal_code: "10110",
country: "TH",
street1: "1448 Praditmanutham Road",
phone_number: "+66876543210"
}, callback);

Benefits of Complete Billing Information:

  • Improves authorization rates by 5-15%
  • Enables Address Verification Service (AVS)
  • Reduces fraud risk
  • Required for some international cards

Common Issues & Troubleshootingโ€‹

Issue: "Public key is required"โ€‹

Cause: Forgot to set public key

Solution:

Omise.setPublicKey("pkey_test_YOUR_KEY");

Issue: "Card number is invalid"โ€‹

Causes:

  • Incorrect card number format
  • Test card used in live mode
  • Unsupported card brand

Solution:

  • Validate card format before submission
  • Use Luhn algorithm for client-side validation
  • Check supported card brands

Issue: Token creation returns 400 errorโ€‹

Common Causes:

  • Missing required fields (name, number, expiration, CVV)
  • Invalid expiration date
  • Wrong data types (string vs integer)

Solution:

// Ensure correct data types
Omise.createToken("card", {
name: cardName, // string
number: cardNumber.replace(/\s/g, ''), // string, remove spaces
expiration_month: parseInt(expiryMonth), // integer!
expiration_year: parseInt(expiryYear), // integer!
security_code: cvv // string
}, callback);

Issue: Mixed Content Errorโ€‹

Cause: Loading Omise.js over HTTP

Solution: Ensure entire page uses HTTPS

Advanced Featuresโ€‹

Client-Side Card Validationโ€‹

function validateCard() {
const cardNumber = document.getElementById('card-number').value;

// Basic Luhn algorithm check
function luhnCheck(num) {
let arr = (num + '')
.split('')
.reverse()
.map(x => parseInt(x));
let lastDigit = arr.splice(0, 1)[0];
let sum = arr.reduce(
(acc, val, i) => (i % 2 !== 0 ? acc + val : acc + ((val * 2) % 9) || 9),
0
);
sum += lastDigit;
return sum % 10 === 0;
}

if (!luhnCheck(cardNumber.replace(/\s/g, ''))) {
document.getElementById('error-message').textContent =
'Invalid card number';
return false;
}

return true;
}

Format Card Number Inputโ€‹

document.getElementById('card-number').addEventListener('input', function(e) {
let value = e.target.value.replace(/\s/g, '');
let formattedValue = value.match(/.{1,4}/g)?.join(' ') || value;
e.target.value = formattedValue;
});

Detect Card Brandโ€‹

function detectCardBrand(number) {
const patterns = {
visa: /^4/,
mastercard: /^5[1-5]/,
amex: /^3[47]/,
jcb: /^35/
};

for (let brand in patterns) {
if (patterns[brand].test(number)) {
return brand;
}
}
return 'unknown';
}

FAQโ€‹

Can I customize the appearance of the payment form?

Yes! You have complete control over HTML and CSS. Omise.js only handles tokenization - you design the form. Style it to match your brand perfectly.

Are tokens reusable?

No, tokens are single-use only. Each payment requires a new token. To save cards for future use, create a Customer and attach the card.

Learn more about saved cards โ†’

What happens if tokenization fails?

Omise.js returns a status code and error message. Common errors:

  • Invalid card number
  • Expired card
  • Incorrect CVV
  • Network connectivity issues

Always handle errors gracefully and show user-friendly messages.

Do I need PCI compliance?

Using Omise.js significantly reduces PCI requirements. Since card data never touches your server, you typically only need SAQ A compliance (simplest form). Consult with your security team for specifics.

Can I collect cards without a form?

Yes, Omise.js offers a pre-built payment form that handles UI and validation:

Omise.setPublicKey("pkey_test_...");
OmiseCard.open({
amount: 10025,
currency: "THB",
onCreateTokenSuccess: (token) => { /* handle token */ }
});

Learn more in Omise.js documentation โ†’

How do I test card collection?

Use Omise test cards:

  • Success: 4242 4242 4242 4242
  • Decline: 4000 0000 0000 0002

Any future expiration date and any CVV work in test mode.

View all test cards โ†’

Next Stepsโ€‹

  1. Set up your HTML form
  2. Implement JavaScript tokenization
  3. Create server-side charge endpoint
  4. Test with test cards
  5. Add 3D Secure for high-value transactions
  6. Go live