Shopify
Email checker for Shopify. Verify customer emails at checkout and in Shopify apps.
Bescherm uw Shopify-winkel tegen nepaccounts, verminder bounce van verlaten winkelwagen-e-mails en verbeter klantcommunicatie door e-mailadressen te verifiëren.
Waarom e-mails verifiëren in Shopify?
| Uitdaging | Impact | Oplossing |
|---|---|---|
| Nepaccounts | Promo-misbruik, fraude | Verifiëren bij registratie |
| Verlaten winkelwagen | Gebounced herstel-e-mails | Verifiëren voor verzending |
| Ordernotificaties | Mislukte bezorgupdates | Verifiëren bij checkout |
| Marketingcampagnes | Lage afleverbaarheid | Klantenlijst opschonen |
Integratiemethoden
| Methode | Beste voor | Complexiteit |
|---|---|---|
| Shopify Flow | Geautomatiseerde workflows | Laag |
| Shopify Functions | Checkout-validatie | Gemiddeld |
| App van derden | Volledige oplossing | Laag |
| Aangepaste app | Volledige controle | Hoog |
Methode 1: Shopify Flow (Aanbevolen)
Gebruik Shopify Flow om e-mails automatisch te verifiëren.
Nieuwe klant-e-mails verifiëren
Maak een workflow om e-mails te verifiëren wanneer klanten zich registreren:
Trigger: Customer created
Conditie: Customer email is not blank
Acties:
- HTTP-verzoek verzenden naar EmailVerify
- Klanttag toevoegen op basis van resultaat
Flow-configuratie
Workflow: Verify New Customer Email
Trigger:
Event: Customer created
Condition:
- Customer email is not blank
Action 1:
Type: Send HTTP request
URL: https://api.emailverify.ai/v1/verify
Method: POST
Headers:
- Authorization: Bearer YOUR_API_KEY
- Content-Type: application/json
Body: {"email": "{{customer.email}}"}
Wait:
Duration: 1 second
Action 2:
Type: Add customer tags
Tags:
- email_verified (if status = valid)
- email_invalid (if status = invalid)Verifiëren voor verlaten winkelwagen-e-mails
Workflow: Verify Before Abandonment Email
Trigger:
Event: Checkout abandoned
Delay: 1 hour
Condition:
- Customer email is not blank
- Customer does not have tag "email_invalid"
Action 1:
Type: Send HTTP request to EmailVerify
Body: {"email": "{{checkout.email}}"}
Action 2:
Type: Branch
If status = "valid":
- Continue to abandonment email sequence
If status = "invalid":
- Add tag "email_invalid"
- Do not send emailMethode 2: Checkout-validatie met Shopify Functions
Maak een Shopify Function om e-mails tijdens checkout te valideren.
Stap 1: Maak een Cart Transform Function
// extensions/email-validation/src/run.js
import { EmailVerify } from '@emailverify/node';
export function run(input) {
const { cart } = input;
const email = cart?.buyerIdentity?.email;
if (!email) {
return { operations: [] };
}
// Note: For real-time validation, use a pre-validated cache
// or implement async validation via metafield
return {
operations: [],
};
}Stap 2: Maak een Checkout UI Extension
// extensions/email-validation-ui/src/Checkout.jsx
import {
useExtensionApi,
render,
Banner,
BlockStack,
} from '@shopify/checkout-ui-extensions-react';
import { useState, useEffect } from 'react';
render('Checkout::Contact::RenderAfter', () => <EmailValidation />);
function EmailValidation() {
const { buyerIdentity } = useExtensionApi();
const [validationStatus, setValidationStatus] = useState(null);
useEffect(() => {
const email = buyerIdentity?.email?.current;
if (email) {
validateEmail(email).then(setValidationStatus);
}
}, [buyerIdentity?.email?.current]);
if (!validationStatus) return null;
if (validationStatus.status === 'invalid') {
return (
<Banner status="warning">
Please check your email address. It appears to be invalid.
</Banner>
);
}
if (validationStatus.result?.disposable) {
return (
<Banner status="info">
We recommend using a permanent email for order updates.
</Banner>
);
}
return null;
}
async function validateEmail(email) {
const response = await fetch('/apps/email-verify/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
});
return response.json();
}Methode 3: Aangepaste Shopify App
Bouw een complete e-mailverificatie-oplossing.
App Backend (Node.js)
// server/index.js
import '@shopify/shopify-app-remix/adapters/node';
import { shopifyApp } from '@shopify/shopify-app-remix/server';
import { EmailVerify } from '@emailverify/node';
const shopify = shopifyApp({
// ... Shopify config
});
const emailVerify = new EmailVerify({
apiKey: process.env.EMAILVERIFY_API_KEY,
});
// API route for email verification
export async function action({ request }) {
const { email, customerId } = await request.json();
try {
const result = await emailVerify.verify(email);
// Update customer metafield
if (customerId) {
await updateCustomerVerificationStatus(customerId, result);
}
return json(result);
} catch (error) {
return json({ error: error.message }, { status: 500 });
}
}
async function updateCustomerVerificationStatus(customerId, result) {
const { admin } = await shopify.authenticate.admin(request);
await admin.graphql(`
mutation updateCustomerMetafield($input: CustomerInput!) {
customerUpdate(input: $input) {
customer {
id
}
}
}
`, {
variables: {
input: {
id: `gid://shopify/Customer/${customerId}`,
metafields: [
{
namespace: "email_verification",
key: "status",
value: result.status,
type: "single_line_text_field"
},
{
namespace: "email_verification",
key: "score",
value: String(result.score),
type: "number_decimal"
},
{
namespace: "email_verification",
key: "verified_at",
value: new Date().toISOString(),
type: "date_time"
}
]
}
}
});
}Webhook Handler
Behandel klant-aanmaak webhooks:
// server/webhooks/customer-created.js
export async function handleCustomerCreated(topic, shop, body) {
const customer = JSON.parse(body);
const { id, email } = customer;
if (!email) return;
try {
// Verify email
const result = await emailVerify.verify(email);
// Update customer with tags
const tags = [];
if (result.status === 'valid') {
tags.push('email_verified');
} else if (result.status === 'invalid') {
tags.push('email_invalid');
}
if (result.result?.disposable) {
tags.push('disposable_email');
}
await updateCustomerTags(shop, id, tags);
// Store verification result
await updateCustomerVerificationStatus(shop, id, result);
console.log(`Verified ${email}: ${result.status}`);
} catch (error) {
console.error(`Failed to verify ${email}:`, error);
}
}Thema-integratie (Liquid)
Voeg verificatie toe aan registratieformulier:
{% comment %} snippets/email-verification.liquid {% endcomment %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const emailInput = document.querySelector('input[type="email"]');
const submitButton = document.querySelector('form[action="/account"] button[type="submit"]');
let verificationResult = null;
emailInput.addEventListener('blur', async function() {
const email = this.value;
if (!email) return;
// Show loading state
emailInput.classList.add('verifying');
try {
const response = await fetch('/apps/emailverify/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
verificationResult = await response.json();
// Update UI based on result
updateEmailFieldUI(verificationResult);
} catch (error) {
console.error('Verification failed:', error);
} finally {
emailInput.classList.remove('verifying');
}
});
function updateEmailFieldUI(result) {
// Remove existing messages
const existingMessage = document.querySelector('.email-verification-message');
if (existingMessage) existingMessage.remove();
// Create message element
const message = document.createElement('div');
message.className = 'email-verification-message';
if (result.status === 'invalid') {
message.classList.add('error');
message.textContent = 'Please enter a valid email address';
submitButton.disabled = true;
} else if (result.result?.disposable) {
message.classList.add('warning');
message.textContent = 'Please use a permanent email for account recovery';
} else if (result.status === 'valid') {
message.classList.add('success');
message.textContent = '✓ Email verified';
submitButton.disabled = false;
}
emailInput.parentNode.appendChild(message);
}
});
</script>
<style>
.email-verification-message {
font-size: 12px;
margin-top: 4px;
}
.email-verification-message.error { color: #c9302c; }
.email-verification-message.warning { color: #f0ad4e; }
.email-verification-message.success { color: #5cb85c; }
input[type="email"].verifying {
background-image: url('/path/to/spinner.gif');
background-position: right 10px center;
background-repeat: no-repeat;
}
</style>Gebruiksscenario's
1. Nepaccountregistratie voorkomen
Blokkeer wegwerp- en ongeldige e-mails tijdens aanmelding:
// Theme app extension
async function validateRegistration(email) {
const result = await verifyEmail(email);
if (result.status === 'invalid') {
return {
valid: false,
message: 'Please enter a valid email address',
};
}
if (result.result.disposable) {
return {
valid: false,
message: 'Temporary email addresses are not allowed',
};
}
return { valid: true };
}2. Verlaten winkelwagen-herstel
Verzend alleen herstel-e-mails naar geldige adressen:
Shopify Flow:
Trigger: Checkout abandoned (1 hour delay)
Condition: Check email verification status
If valid:
→ Send abandonment email
→ Add to remarketing audience
If invalid:
→ Skip email
→ Log for analytics3. Orderrisicobeoordeling
Factor e-mailkwaliteit in bij fraudedetectie:
function calculateOrderRiskScore(order, emailVerification) {
let riskScore = 0;
// Email verification factors
if (emailVerification.status === 'invalid') {
riskScore += 30;
}
if (emailVerification.result?.disposable) {
riskScore += 20;
}
if (emailVerification.result?.free && order.total > 500) {
riskScore += 10; // High value order with free email
}
// Other factors...
if (order.billing_address !== order.shipping_address) {
riskScore += 15;
}
return riskScore;
}4. Klantsegmentatie
Maak klantsegmenten op basis van e-mailkwaliteit:
| Segment | Criteria | Marketingstrategie |
|---|---|---|
| Hoge waarde | Geverifieerd, zakelijke e-mail | Premiumcampagnes |
| Standaard | Geverifieerd, gratis e-mail | Reguliere campagnes |
| Risico | Niet geverifieerd, oud account | Herverificatiecampagne |
| Uitgesloten | Ongeldig, wegwerp | Geen marketing |
Metafield-setup
Maak metafields om verificatiegegevens op te slaan:
Klant-metafields
mutation createMetafieldDefinitions {
metafieldDefinitionCreate(definition: {
namespace: "email_verification"
key: "status"
name: "Email Verification Status"
type: "single_line_text_field"
ownerType: CUSTOMER
}) {
createdDefinition { id }
}
}Aanbevolen metafields:
| Namespace | Key | Type | Beschrijving |
|---|---|---|---|
| email_verification | status | single_line_text_field | valid, invalid, unknown |
| email_verification | score | number_decimal | 0.0 - 1.0 |
| email_verification | verified_at | date_time | Laatste verificatiedatum |
| email_verification | disposable | boolean | Is wegwerp-e-mail |
| email_verification | role_based | boolean | Is rol-gebaseerde e-mail |
Metafields in Liquid benaderen
{% if customer.metafields.email_verification.status == 'valid' %}
<span class="verified-badge">✓ Verified</span>
{% endif %}Bulk klantverificatie
Schoon uw bestaande klantenlijst op:
Klanten exporteren
async function exportCustomersForVerification(admin) {
const query = `
query getCustomers($cursor: String) {
customers(first: 250, after: $cursor) {
edges {
node {
id
email
createdAt
metafield(namespace: "email_verification", key: "status") {
value
}
}
cursor
}
pageInfo {
hasNextPage
}
}
}
`;
let customers = [];
let cursor = null;
do {
const response = await admin.graphql(query, {
variables: { cursor },
});
const { edges, pageInfo } = response.data.customers;
// Filter unverified customers
const unverified = edges
.filter((e) => !e.node.metafield)
.map((e) => ({
id: e.node.id,
email: e.node.email,
}));
customers.push(...unverified);
cursor = edges[edges.length - 1]?.cursor;
} while (response.data.customers.pageInfo.hasNextPage);
return customers;
}Bulk verifiëren en bijwerken
async function bulkVerifyCustomers(customers) {
const emails = customers.map((c) => c.email);
// Submit bulk verification job
const job = await emailVerify.verifyBulk(emails);
// Wait for completion
const results = await waitForJobCompletion(job.job_id);
// Update customers with results
for (const result of results) {
const customer = customers.find((c) => c.email === result.email);
if (customer) {
await updateCustomerVerificationStatus(customer.id, result);
}
}
return results;
}Best practices
1. Verifieer op meerdere punten
- Registratie: Blokkeer nepaccounts
- Checkout: Zorg dat ordernotificaties klanten bereiken
- Verlaten winkelwagen: Verspil geen e-mails aan ongeldige adressen
2. Cache resultaten
Cache verificatieresultaten om redundante API-calls te vermijden:
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
async function verifyWithCache(email) {
const cacheKey = `email_verify:${email}`;
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
const result = await emailVerify.verify(email);
await redis.setex(cacheKey, CACHE_DURATION / 1000, JSON.stringify(result));
return result;
}3. Behandel randgevallen
function handleVerificationResult(result, context) {
switch (result.status) {
case 'valid':
// Normal flow
break;
case 'invalid':
if (context === 'checkout') {
// Don't block checkout, just log
logInvalidEmail(result.email, 'checkout');
} else if (context === 'registration') {
// Block registration
throw new Error('Invalid email');
}
break;
case 'unknown':
// Accept but flag for review
flagForReview(result.email);
break;
case 'accept_all':
// Valid but monitor for bounces
markAsCatchAll(result.email);
break;
}
}4. Monitor en optimaliseer
Volg deze metrics:
- Verificatie-succespercentage
- Bouncepercentage-reductie
- Nepaccount-preventiepercentage
- Verlaten winkelwagen-e-mail afleverbaarheid