Shopify
Email checker for Shopify. Verify customer emails at checkout and in Shopify apps.
保護您的 Shopify 商店免受虛假帳戶影響、減少購物車放棄電子郵件的退信並透過驗證電子郵件地址改善客戶溝通。
為什麼在 Shopify 中驗證電子郵件?
| 挑戰 | 影響 | 解決方案 |
|---|---|---|
| 虛假帳戶 | 促銷濫用、詐欺 | 在註冊時驗證 |
| 購物車放棄 | 恢復電子郵件退信 | 發送前驗證 |
| 訂單通知 | 更新送達失敗 | 結帳時驗證 |
| 行銷活動 | 低可送達性 | 清理客戶清單 |
整合方法
| 方法 | 最適合 | 複雜度 |
|---|---|---|
| Shopify Flow | 自動化工作流程 | 低 |
| Shopify Functions | 結帳驗證 | 中 |
| 第三方應用程式 | 完整解決方案 | 低 |
| 自訂應用程式 | 完全控制 | 高 |
方法 1: Shopify Flow (推薦)
使用 Shopify Flow 自動驗證電子郵件。
驗證新客戶電子郵件
建立一個工作流程在客戶註冊時驗證電子郵件:
觸發器: 客戶已建立
條件: 客戶電子郵件不為空白
動作:
- 發送 HTTP 請求到 EmailVerify
- 根據結果新增客戶標籤
Flow 配置
Workflow: 驗證新客戶電子郵件
Trigger:
Event: 客戶已建立
Condition:
- 客戶電子郵件不為空白
Action 1:
Type: 發送 HTTP 請求
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 秒
Action 2:
Type: 新增客戶標籤
Tags:
- email_verified (如果 status = valid)
- email_invalid (如果 status = invalid)在購物車放棄電子郵件之前驗證
Workflow: 在放棄電子郵件前驗證
Trigger:
Event: 結帳已放棄
Delay: 1 小時
Condition:
- 客戶電子郵件不為空白
- 客戶沒有標籤 "email_invalid"
Action 1:
Type: 發送 HTTP 請求到 EmailVerify
Body: {"email": "{{checkout.email}}"}
Action 2:
Type: 分支
If status = "valid":
- 繼續放棄電子郵件序列
If status = "invalid":
- 新增標籤 "email_invalid"
- 不發送電子郵件方法 2: 使用 Shopify Functions 進行結帳驗證
建立一個 Shopify Function 在結帳期間驗證電子郵件。
步驟 1: 建立 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: [] };
}
// 注意: 對於即時驗證,使用預先驗證的快取
// 或透過 metafield 實作非同步驗證
return {
operations: [],
};
}步驟 2: 建立 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">
請檢查您的電子郵件地址。它似乎無效。
</Banner>
);
}
if (validationStatus.result?.disposable) {
return (
<Banner status="info">
我們建議使用永久電子郵件以接收訂單更新。
</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();
}方法 3: 自訂 Shopify 應用程式
建立完整的電子郵件驗證解決方案。
應用程式後端 (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 配置
});
const emailVerify = new EmailVerify({
apiKey: process.env.EMAILVERIFY_API_KEY,
});
// 電子郵件驗證的 API 路由
export async function action({ request }) {
const { email, customerId } = await request.json();
try {
const result = await emailVerify.verify(email);
// 更新客戶 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
處理客戶建立 webhook:
// 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 {
// 驗證電子郵件
const result = await emailVerify.verify(email);
// 使用標籤更新客戶
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);
// 儲存驗證結果
await updateCustomerVerificationStatus(shop, id, result);
console.log(`已驗證 ${email}: ${result.status}`);
} catch (error) {
console.error(`驗證 ${email} 失敗:`, error);
}
}主題整合 (Liquid)
在註冊表單中新增驗證:
{% 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;
// 顯示載入狀態
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();
// 根據結果更新 UI
updateEmailFieldUI(verificationResult);
} catch (error) {
console.error('驗證失敗:', error);
} finally {
emailInput.classList.remove('verifying');
}
});
function updateEmailFieldUI(result) {
// 移除現有訊息
const existingMessage = document.querySelector('.email-verification-message');
if (existingMessage) existingMessage.remove();
// 建立訊息元素
const message = document.createElement('div');
message.className = 'email-verification-message';
if (result.status === 'invalid') {
message.classList.add('error');
message.textContent = '請輸入有效的電子郵件地址';
submitButton.disabled = true;
} else if (result.result?.disposable) {
message.classList.add('warning');
message.textContent = '請使用永久電子郵件以便帳戶恢復';
} else if (result.status === 'valid') {
message.classList.add('success');
message.textContent = '✓ 電子郵件已驗證';
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>使用案例
1. 防止虛假帳戶註冊
在註冊期間封鎖一次性和無效電子郵件:
// 主題應用程式擴充
async function validateRegistration(email) {
const result = await verifyEmail(email);
if (result.status === 'invalid') {
return {
valid: false,
message: '請輸入有效的電子郵件地址',
};
}
if (result.result.disposable) {
return {
valid: false,
message: '不允許使用臨時電子郵件地址',
};
}
return { valid: true };
}2. 購物車放棄恢復
僅向有效地址發送放棄電子郵件:
Shopify Flow:
Trigger: 結帳已放棄 (延遲 1 小時)
Condition: 檢查電子郵件驗證狀態
If valid:
→ 發送放棄電子郵件
→ 新增到再行銷受眾
If invalid:
→ 跳過電子郵件
→ 記錄以供分析3. 訂單風險評估
將電子郵件品質納入詐欺偵測:
function calculateOrderRiskScore(order, emailVerification) {
let riskScore = 0;
// 電子郵件驗證因素
if (emailVerification.status === 'invalid') {
riskScore += 30;
}
if (emailVerification.result?.disposable) {
riskScore += 20;
}
if (emailVerification.result?.free && order.total > 500) {
riskScore += 10; // 高價值訂單使用免費電子郵件
}
// 其他因素...
if (order.billing_address !== order.shipping_address) {
riskScore += 15;
}
return riskScore;
}4. 客戶分群
根據電子郵件品質建立客戶區隔:
| 區隔 | 條件 | 行銷策略 |
|---|---|---|
| 高價值 | 已驗證、商業電子郵件 | 優質活動 |
| 標準 | 已驗證、免費電子郵件 | 常規活動 |
| 風險 | 未驗證、舊帳戶 | 重新驗證活動 |
| 排除 | 無效、一次性 | 無行銷 |
Metafield 設定
建立 metafield 以儲存驗證資料:
客戶 Metafields
mutation createMetafieldDefinitions {
metafieldDefinitionCreate(definition: {
namespace: "email_verification"
key: "status"
name: "Email Verification Status"
type: "single_line_text_field"
ownerType: CUSTOMER
}) {
createdDefinition { id }
}
}推薦的 metafield:
| 命名空間 | 鍵 | 類型 | 說明 |
|---|---|---|---|
| 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 | 最後驗證日期 |
| email_verification | disposable | boolean | 是否為一次性電子郵件 |
| email_verification | role_based | boolean | 是否為角色型電子郵件 |
在 Liquid 中存取 Metafield
{% if customer.metafields.email_verification.status == 'valid' %}
<span class="verified-badge">✓ 已驗證</span>
{% endif %}批次客戶驗證
清理您現有的客戶清單:
匯出客戶
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;
// 篩選未驗證的客戶
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;
}批次驗證和更新
async function bulkVerifyCustomers(customers) {
const emails = customers.map((c) => c.email);
// 提交批次驗證作業
const job = await emailVerify.verifyBulk(emails);
// 等待完成
const results = await waitForJobCompletion(job.job_id);
// 使用結果更新客戶
for (const result of results) {
const customer = customers.find((c) => c.email === result.email);
if (customer) {
await updateCustomerVerificationStatus(customer.id, result);
}
}
return results;
}最佳實踐
1. 在多個點驗證
- 註冊: 封鎖虛假帳戶
- 結帳: 確保訂單通知到達客戶
- 購物車放棄: 不要在無效地址上浪費電子郵件
2. 快取結果
快取驗證結果以避免冗餘的 API 呼叫:
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 小時
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. 處理邊緣情況
function handleVerificationResult(result, context) {
switch (result.status) {
case 'valid':
// 正常流程
break;
case 'invalid':
if (context === 'checkout') {
// 不封鎖結帳,只記錄
logInvalidEmail(result.email, 'checkout');
} else if (context === 'registration') {
// 封鎖註冊
throw new Error('電子郵件無效');
}
break;
case 'unknown':
// 接受但標記以供審查
flagForReview(result.email);
break;
case 'accept_all':
// 有效但監控退信
markAsCatchAll(result.email);
break;
}
}4. 監控和優化
追蹤這些指標:
- 驗證成功率
- 退信率降低
- 虛假帳戶預防率
- 購物車放棄電子郵件可送達性