BigCommerce
Email checker for BigCommerce. Verify customer emails at checkout and registration.
Integrate EmailVerify with BigCommerce to verify customer emails at checkout, reduce fraud, and improve deliverability for order confirmations and marketing emails.
Integration Methods
| Method | Best For | Complexity |
|---|---|---|
| Script Manager | Quick setup | Low |
| Stencil Theme | Custom themes | Medium |
| Storefront API | Headless stores | High |
Method 1: Script Manager Integration
Add real-time email verification using BigCommerce Script Manager.
Create Verification Script
- Go to Storefront → Script Manager
- Click Create a Script
- Configure:
- Name: EmailVerify Email Verification
- Location: Footer
- Pages: Checkout
Script Code
<script>
(function() {
const EMAILVERIFY_API_KEY = 'YOUR_API_KEY';
// Wait for checkout to load
function waitForCheckout() {
const emailInput = document.querySelector('[data-test="customer-email"] input, #email');
if (emailInput) {
initVerification(emailInput);
} else {
setTimeout(waitForCheckout, 500);
}
}
function initVerification(emailInput) {
const statusEl = document.createElement('span');
statusEl.className = 'bv-status';
emailInput.parentNode.appendChild(statusEl);
let verifyTimeout;
let lastEmail = '';
emailInput.addEventListener('blur', function() {
const email = this.value;
if (!email || email === lastEmail || !isValidFormat(email)) {
return;
}
clearTimeout(verifyTimeout);
verifyTimeout = setTimeout(function() {
verifyEmail(email, statusEl);
lastEmail = email;
}, 500);
});
}
async function verifyEmail(email, statusEl) {
statusEl.textContent = 'Verifying...';
statusEl.className = 'bv-status verifying';
try {
const response = await fetch('https://api.emailverify.ai/v1/verify', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + EMAILVERIFY_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email: email })
});
const data = await response.json();
if (data.status === 'valid') {
statusEl.textContent = '✓ Email verified';
statusEl.className = 'bv-status valid';
} else if (data.status === 'invalid') {
statusEl.textContent = '✗ Please enter a valid email';
statusEl.className = 'bv-status invalid';
} else if (data.result && data.result.disposable) {
statusEl.textContent = '✗ Please use a permanent email';
statusEl.className = 'bv-status invalid';
} else {
statusEl.textContent = '';
statusEl.className = 'bv-status';
}
} catch (error) {
console.error('Verification error:', error);
statusEl.textContent = '';
statusEl.className = 'bv-status';
}
}
function isValidFormat(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// Add styles
const style = document.createElement('style');
style.textContent = `
.bv-status {
display: block;
margin-top: 4px;
font-size: 12px;
}
.bv-status.verifying { color: #666; }
.bv-status.valid { color: #28a745; }
.bv-status.invalid { color: #dc3545; }
`;
document.head.appendChild(style);
// Initialize
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', waitForCheckout);
} else {
waitForCheckout();
}
})();
</script>Method 2: Stencil Theme Integration
For custom Stencil themes, add verification directly to your theme files.
Create Theme Component
// assets/js/theme/global/email-verification.js
import utils from '@bigcommerce/stencil-utils';
export default class EmailVerification {
constructor() {
this.apiKey = window.emailverifyConfig?.apiKey;
this.emailInputs = document.querySelectorAll('input[type="email"]');
this.cache = new Map();
if (this.apiKey && this.emailInputs.length) {
this.init();
}
}
init() {
this.emailInputs.forEach(input => {
this.attachVerification(input);
});
}
attachVerification(input) {
const statusEl = document.createElement('span');
statusEl.className = 'form-field-verification';
input.parentNode.appendChild(statusEl);
let timeout;
input.addEventListener('blur', () => {
const email = input.value.trim();
if (!email || !this.isValidFormat(email)) {
this.clearStatus(statusEl);
return;
}
clearTimeout(timeout);
timeout = setTimeout(() => this.verify(email, statusEl, input), 500);
});
}
async verify(email, statusEl, input) {
// Check cache
if (this.cache.has(email)) {
this.displayResult(this.cache.get(email), statusEl, input);
return;
}
this.showLoading(statusEl);
try {
const response = await fetch('https://api.emailverify.ai/v1/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email })
});
const data = await response.json();
this.cache.set(email, data);
this.displayResult(data, statusEl, input);
} catch (error) {
console.error('Email verification error:', error);
this.clearStatus(statusEl);
}
}
displayResult(data, statusEl, input) {
input.classList.remove('is-valid', 'is-invalid');
if (data.status === 'valid' && !data.result?.disposable) {
statusEl.innerHTML = '<span class="icon icon--success"></span> Verified';
statusEl.className = 'form-field-verification is-valid';
input.classList.add('is-valid');
} else if (data.status === 'invalid') {
statusEl.innerHTML = '<span class="icon icon--error"></span> Invalid email';
statusEl.className = 'form-field-verification is-invalid';
input.classList.add('is-invalid');
} else if (data.result?.disposable) {
statusEl.innerHTML = '<span class="icon icon--warning"></span> Please use a permanent email';
statusEl.className = 'form-field-verification is-invalid';
input.classList.add('is-invalid');
} else {
this.clearStatus(statusEl);
}
}
showLoading(statusEl) {
statusEl.innerHTML = '<span class="spinner"></span> Verifying...';
statusEl.className = 'form-field-verification is-loading';
}
clearStatus(statusEl) {
statusEl.innerHTML = '';
statusEl.className = 'form-field-verification';
}
isValidFormat(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
}Initialize in Theme
// assets/js/theme/global.js
import EmailVerification from './global/email-verification';
export default class Global extends PageManager {
onReady() {
// ... other initializations
new EmailVerification();
}
}Theme Configuration
Add to config.json:
{
"settings": {
"emailverify_enabled": true,
"emailverify_api_key": ""
}
}Schema Settings
Add to schema.json for admin configuration:
[
{
"name": "EmailVerify",
"settings": [
{
"type": "checkbox",
"label": "Enable Email Verification",
"id": "emailverify_enabled"
},
{
"type": "text",
"label": "API Key",
"id": "emailverify_api_key"
}
]
}
]Inject Config to Template
{{!-- templates/layout/base.html --}}
<script>
window.emailverifyConfig = {
enabled: {{settings.emailverify_enabled}},
apiKey: '{{settings.emailverify_api_key}}'
};
</script>Method 3: Storefront API Integration
For headless BigCommerce stores using the Storefront API.
Verification Service
// services/email-verification.ts
interface VerificationResult {
email: string;
status: 'valid' | 'invalid' | 'unknown';
score?: number;
isDisposable: boolean;
isRole: boolean;
}
class EmailVerificationService {
private apiKey: string;
private cache: Map<string, VerificationResult> = new Map();
constructor(apiKey: string) {
this.apiKey = apiKey;
}
async verify(email: string): Promise<VerificationResult> {
// Check cache
const cached = this.cache.get(email);
if (cached) {
return cached;
}
try {
const response = await fetch('https://api.emailverify.ai/v1/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
const data = await response.json();
const result: VerificationResult = {
email,
status: data.status,
score: data.score,
isDisposable: data.result?.disposable ?? false,
isRole: data.result?.role ?? false,
};
this.cache.set(email, result);
return result;
} catch (error) {
console.error('Verification error:', error);
return {
email,
status: 'unknown',
isDisposable: false,
isRole: false,
};
}
}
isValid(result: VerificationResult): boolean {
return result.status === 'valid' && !result.isDisposable;
}
getErrorMessage(result: VerificationResult): string | null {
if (result.status === 'invalid') {
return 'Please enter a valid email address.';
}
if (result.isDisposable) {
return 'Temporary email addresses are not accepted.';
}
return null;
}
}
export const emailVerification = new EmailVerificationService(
process.env.EMAILVERIFY_API_KEY!
);React Checkout Component
// components/checkout/EmailInput.tsx
import { useState, useCallback } from 'react';
import { emailVerification } from '@/services/email-verification';
import { debounce } from 'lodash';
interface EmailInputProps {
value: string;
onChange: (value: string) => void;
onValidationChange: (isValid: boolean) => void;
}
export function EmailInput({ value, onChange, onValidationChange }: EmailInputProps) {
const [status, setStatus] = useState<'idle' | 'verifying' | 'valid' | 'invalid'>('idle');
const [message, setMessage] = useState('');
const verifyEmail = useCallback(
debounce(async (email: string) => {
if (!email || !email.includes('@')) {
setStatus('idle');
setMessage('');
return;
}
setStatus('verifying');
const result = await emailVerification.verify(email);
if (emailVerification.isValid(result)) {
setStatus('valid');
setMessage('Email verified');
onValidationChange(true);
} else {
setStatus('invalid');
setMessage(emailVerification.getErrorMessage(result) || 'Verification failed');
onValidationChange(false);
}
}, 500),
[onValidationChange]
);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
onChange(newValue);
verifyEmail(newValue);
};
return (
<div className="form-field">
<label htmlFor="email">Email Address</label>
<input
type="email"
id="email"
value={value}
onChange={handleChange}
className={`form-input ${status === 'valid' ? 'is-valid' : ''} ${status === 'invalid' ? 'is-invalid' : ''}`}
/>
{status !== 'idle' && (
<span className={`form-feedback ${status}`}>
{status === 'verifying' && <Spinner />}
{message}
</span>
)}
</div>
);
}Next.js API Route
For server-side verification to protect your API key:
// pages/api/verify-email.ts
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
const { email } = req.body;
if (!email) {
return res.status(400).json({ error: 'Email required' });
}
try {
const response = await fetch('https://api.emailverify.ai/v1/verify', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.EMAILVERIFY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ email }),
});
const data = await response.json();
return res.status(200).json({
valid: data.status === 'valid' && !data.result?.disposable,
status: data.status,
disposable: data.result?.disposable ?? false,
});
} catch (error) {
console.error('Verification error:', error);
return res.status(200).json({ valid: true }); // Allow on error
}
}Webhooks Integration
Process orders and verify emails asynchronously.
Webhook Handler
// api/webhooks/bigcommerce.js
import crypto from 'crypto';
import EmailVerify from '@emailverify/sdk';
const bv = new EmailVerify(process.env.EMAILVERIFY_API_KEY);
export default async function handler(req, res) {
// Verify webhook signature
const signature = req.headers['x-bc-webhook-signature'];
const expectedSignature = crypto
.createHmac('sha256', process.env.BC_WEBHOOK_SECRET)
.update(JSON.stringify(req.body))
.digest('base64');
if (signature !== expectedSignature) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { scope, data } = req.body;
if (scope === 'store/order/created') {
await handleOrderCreated(data);
}
res.status(200).json({ received: true });
}
async function handleOrderCreated(data) {
const orderId = data.id;
// Fetch order details from BigCommerce
const order = await fetchOrder(orderId);
if (!order.billing_address?.email) {
return;
}
// Verify email
const result = await bv.verify({ email: order.billing_address.email });
// Store verification result
await storeVerificationResult(orderId, result);
// Flag suspicious orders
if (result.status === 'invalid' || result.is_disposable) {
await flagOrder(orderId, {
reason: result.status === 'invalid'
? 'Invalid email address'
: 'Disposable email detected',
verification: result,
});
}
}
async function fetchOrder(orderId) {
const response = await fetch(
`https://api.bigcommerce.com/stores/${process.env.BC_STORE_HASH}/v2/orders/${orderId}`,
{
headers: {
'X-Auth-Token': process.env.BC_ACCESS_TOKEN,
'Accept': 'application/json',
},
}
);
return response.json();
}
async function flagOrder(orderId, data) {
// Add order note or update status
await fetch(
`https://api.bigcommerce.com/stores/${process.env.BC_STORE_HASH}/v2/orders/${orderId}`,
{
method: 'PUT',
headers: {
'X-Auth-Token': process.env.BC_ACCESS_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify({
staff_notes: `⚠️ Email Verification Warning: ${data.reason}`,
}),
}
);
}Register Webhook
async function registerWebhook() {
const response = await fetch(
`https://api.bigcommerce.com/stores/${process.env.BC_STORE_HASH}/v3/hooks`,
{
method: 'POST',
headers: {
'X-Auth-Token': process.env.BC_ACCESS_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify({
scope: 'store/order/created',
destination: 'https://yoursite.com/api/webhooks/bigcommerce',
is_active: true,
}),
}
);
return response.json();
}Bulk Customer Verification
Verify existing customers in your store.
import EmailVerify from '@emailverify/sdk';
const bv = new EmailVerify(process.env.EMAILVERIFY_API_KEY);
async function verifyAllCustomers() {
const customers = await fetchAllCustomers();
const emails = customers.map(c => c.email);
console.log(`Verifying ${emails.length} customer emails...`);
// Submit bulk job
const job = await bv.verifyBulk(emails);
console.log(`Job ID: ${job.job_id}`);
// Wait for completion
let status;
do {
await sleep(5000);
status = await bv.getBulkJobStatus(job.job_id);
console.log(`Progress: ${status.progress_percent}%`);
} while (status.status !== 'completed');
// Process results
const results = await bv.getBulkJobResults(job.job_id);
const stats = { valid: 0, invalid: 0, disposable: 0 };
for (const result of results.results) {
if (result.status === 'valid') stats.valid++;
else stats.invalid++;
if (result.is_disposable) stats.disposable++;
// Update customer record
const customer = customers.find(c => c.email === result.email);
if (customer) {
await updateCustomerMeta(customer.id, {
email_status: result.status,
email_score: result.score,
});
}
}
console.log('Verification complete:', stats);
return stats;
}
async function fetchAllCustomers() {
const customers = [];
let page = 1;
while (true) {
const response = await fetch(
`https://api.bigcommerce.com/stores/${process.env.BC_STORE_HASH}/v3/customers?page=${page}&limit=250`,
{
headers: {
'X-Auth-Token': process.env.BC_ACCESS_TOKEN,
},
}
);
const data = await response.json();
if (data.data.length === 0) break;
customers.push(...data.data);
page++;
}
return customers;
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}Best Practices
1. Protect Your API Key
Never expose your API key in client-side code. Use a proxy:
// Server-side proxy
app.post('/api/verify', async (req, res) => {
const { email } = req.body;
const result = await bv.verify({ email });
// Only return necessary data
res.json({
valid: result.status === 'valid',
disposable: result.is_disposable,
});
});2. Cache Results
const cache = new Map();
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
function getCachedResult(email) {
const cached = cache.get(email);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.result;
}
return null;
}
function setCachedResult(email, result) {
cache.set(email, { result, timestamp: Date.now() });
}3. Handle Errors Gracefully
try {
const result = await verifyEmail(email);
// ... handle result
} catch (error) {
console.error('Verification failed:', error);
// Allow checkout to proceed
return { valid: true };
}