Shopify LINE 到货通知实现指南
LINE 到货通知(Back in Stock via LINE)在台湾、日本、泰国市场是显著高于 Email 转化的渠道——LINE 消息打开率通常 70%+,Email 在同区段仅 20–30%。但 Shopify 官方没有原生 LINE 集成,需要自行接入 LINE Messaging API + 库存监听。
本文给出一个完整可落地的实现方案:前端订阅表单 → 数据存储 → 库存监听 → LINE 推送 → 防骚扰。代码示例可直接复制改造。
写在前面:如果你面向欧美市场,建议先评估 Email / SMS 渠道(Klaviyo、Postscript),LINE 在欧美几乎没有用户基数。LINE 路径只在亚太特定市场才值得投入开发。
渠道对比速查
| 渠道 | 适合市场 | 打开率 | 实现难度 | 单次成本 |
|---|---|---|---|---|
| LINE | 台湾、日本、泰国、印尼 | 70–85% | 中(需 Messaging API) | 按消息计费,月活 $0.X |
| 全球通用 | 20–35% | 低(Shopify 自带 + Klaviyo) | 接近 0 | |
| SMS | 北美、东南亚 | 90%+ | 中 | 较高($0.01–$0.05 / 条) |
| 拉美、中东、印度 | 80%+ | 中(需 Business API) | 按对话计费 | |
| Web Push | 全球 | 5–15% | 低(Shopify App) | 0 |
LINE 不是万能解——在面向多市场的店铺里,优先按访客地区分流:台日访客显示 LINE 订阅,其他地区显示 Email。
✅ 目标功能
让客户在商品售罄时,输入 LINE ID 或授权 LINE Login,当商品补货时,自动通过 LINE 消息通知客户。
方案实现流程
一、准备工具和条件
| 项目 | 工具或服务 |
|---|---|
| 客户订阅表单 | Shopify前端自定义代码 |
| 通知发送 | LINE 官方账号(Messaging API) |
| 数据存储 | Shopify metafield / Shopify app / 外部数据库 |
| 通知触发 | Shopify Flow / Shopify App / 自建Webhook逻辑 |
二、设置 LINE 官方账号 & Messaging API
1. 创建 LINE 官方账号
- 访问 LINE Business Manager
- 创建新的商业账号
- 完成账号验证
2. 设置 Messaging API
- 在 LINE Developers Console 创建新的 Provider
- 创建”Messaging API”频道
- 获取重要信息:
- Channel ID
- Channel Secret
- Access Token
3. 配置 Webhook
# Webhook URL 示例
https://your-app.herokuapp.com/webhook/line三、Shopify 前端订阅逻辑
1. 添加订阅表单到商品页面
在 sections/product-form.liquid 或相关模板中添加:
<!-- 缺货时显示的 LINE 订阅表单 -->
{% unless current_variant.available %}
<div id="line-restock-notification" class="restock-form">
<h3>📢 商品补货通知</h3>
<p>此商品暂时缺货,订阅 LINE 通知,补货时第一时间告知您!</p>
<div class="line-subscription-form">
<input
type="text"
id="line-id-input"
placeholder="请输入您的 LINE ID"
class="form-input"
/>
<button
onclick="subscribeLine()"
class="btn btn-primary"
id="subscribe-btn"
>
订阅到货通知
</button>
</div>
<div id="line-result-msg" class="notification-msg"></div>
<!-- 或者使用 LINE 登录按钮 -->
<div class="line-login-option">
<p>或者使用 LINE 账号快速订阅:</p>
<div
id="line-login-btn"
data-client-id="{{ settings.line_login_channel_id }}"
data-redirect-uri="{{ shop.secure_url }}/apps/line-auth"
>
<img src="https://d.line-scdn.net/r/devcenter/downloadables/login_button/login_btn_base.png" alt="LINE登录">
</div>
</div>
</div>
<style>
.restock-form {
border: 2px solid #00C300;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
background: #f8fff8;
}
.line-subscription-form {
display: flex;
gap: 10px;
margin: 15px 0;
}
.form-input {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
}
.notification-msg {
margin-top: 10px;
padding: 10px;
border-radius: 4px;
}
.notification-msg.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.notification-msg.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
</style>
{% endunless %}2. JavaScript 订阅逻辑
// assets/line-restock-notification.js
function subscribeLine() {
const lineId = document.getElementById("line-id-input").value.trim();
const subscribeBtn = document.getElementById("subscribe-btn");
const resultMsg = document.getElementById("line-result-msg");
// 验证输入
if (!lineId) {
showMessage("请输入您的 LINE ID", "error");
return;
}
// 获取当前商品变体信息
const variantId = getSelectedVariantId();
const productId = {{ product.id }};
// 禁用按钮,防止重复提交
subscribeBtn.disabled = true;
subscribeBtn.textContent = "订阅中...";
// 发送订阅请求
fetch("/apps/line-restock/subscribe", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Shopify-Web-Token": "{{ form.authenticity_token }}"
},
body: JSON.stringify({
lineId: lineId,
variantId: variantId,
productId: productId,
productTitle: "{{ product.title | escape }}",
variantTitle: getSelectedVariantTitle()
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
showMessage("订阅成功!商品补货时我们会立即通知您", "success");
document.getElementById("line-id-input").value = "";
} else {
showMessage("订阅失败:" + (data.message || "请稍后重试"), "error");
}
})
.catch(error => {
console.error("订阅错误:", error);
showMessage("网络错误,请稍后重试", "error");
})
.finally(() => {
subscribeBtn.disabled = false;
subscribeBtn.textContent = "订阅到货通知";
});
}
function getSelectedVariantId() {
// 获取当前选中的变体ID
const variantSelect = document.querySelector('[name="id"]');
return variantSelect ? variantSelect.value : {{ product.selected_or_first_available_variant.id }};
}
function getSelectedVariantTitle() {
const variantSelect = document.querySelector('[name="id"]');
if (variantSelect) {
const selectedOption = variantSelect.options[variantSelect.selectedIndex];
return selectedOption.textContent;
}
return "{{ product.selected_or_first_available_variant.title | escape }}";
}
function showMessage(message, type) {
const resultMsg = document.getElementById("line-result-msg");
resultMsg.textContent = message;
resultMsg.className = `notification-msg ${type}`;
// 3秒后自动清除消息
setTimeout(() => {
resultMsg.textContent = "";
resultMsg.className = "notification-msg";
}, 3000);
}
// LINE 登录功能
function initLineLogin() {
const lineLoginBtn = document.getElementById("line-login-btn");
if (lineLoginBtn) {
lineLoginBtn.addEventListener("click", function() {
const clientId = this.dataset.clientId;
const redirectUri = this.dataset.redirectUri;
const state = generateRandomString(32);
const loginUrl = `https://access.line.me/oauth2/v2.1/authorize?` +
`response_type=code&` +
`client_id=${clientId}&` +
`redirect_uri=${encodeURIComponent(redirectUri)}&` +
`state=${state}&` +
`scope=profile%20openid`;
window.location.href = loginUrl;
});
}
}
function generateRandomString(length) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', function() {
initLineLogin();
});四、后端 API 实现
1. Shopify App Proxy 设置
在 Shopify 合作伙伴后台设置 App Proxy:
Subpath prefix: apps
Subpath: line-restock
URL: https://your-server.com/shopify/proxy2. 订阅 API 端点
// server.js (Node.js + Express 示例)
const express = require('express');
const axios = require('axios');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// LINE API 配置
const LINE_CHANNEL_ACCESS_TOKEN = process.env.LINE_CHANNEL_ACCESS_TOKEN;
const LINE_CHANNEL_SECRET = process.env.LINE_CHANNEL_SECRET;
// 数据库配置 (示例使用 MongoDB)
const mongoose = require('mongoose');
// 订阅记录模型
const RestockSubscription = mongoose.model('RestockSubscription', {
shopDomain: String,
lineUserId: String,
variantId: String,
productId: String,
productTitle: String,
variantTitle: String,
isActive: { type: Boolean, default: true },
createdAt: { type: Date, default: Date.now }
});
// 处理订阅请求
app.post('/shopify/proxy/subscribe', async (req, res) => {
try {
const { lineId, variantId, productId, productTitle, variantTitle } = req.body;
const shopDomain = req.get('X-Shopify-Shop-Domain');
// 验证 LINE ID 格式
if (!lineId || lineId.length < 3) {
return res.json({ success: false, message: 'LINE ID 格式不正确' });
}
// 检查是否已经订阅
const existingSubscription = await RestockSubscription.findOne({
shopDomain,
lineUserId: lineId,
variantId,
isActive: true
});
if (existingSubscription) {
return res.json({ success: false, message: '您已经订阅了此商品的到货通知' });
}
// 创建新的订阅记录
const subscription = new RestockSubscription({
shopDomain,
lineUserId: lineId,
variantId,
productId,
productTitle,
variantTitle
});
await subscription.save();
// 发送确认消息到 LINE
await sendLineMessage(lineId, `订阅成功!\n\n商品:${productTitle}\n规格:${variantTitle}\n\n补货时我们会立即通知您!`);
res.json({ success: true, message: '订阅成功' });
} catch (error) {
console.error('订阅失败:', error);
res.json({ success: false, message: '系统错误,请稍后重试' });
}
});
// 发送 LINE 消息
async function sendLineMessage(userId, message) {
try {
await axios.post('https://api.line.me/v2/bot/message/push', {
to: userId,
messages: [{
type: 'text',
text: message
}]
}, {
headers: {
'Authorization': `Bearer ${LINE_CHANNEL_ACCESS_TOKEN}`,
'Content-Type': 'application/json'
}
});
} catch (error) {
console.error('LINE 消息发送失败:', error.response?.data || error.message);
}
}五、库存变更监听与通知发送
1. 设置 Shopify Webhook
在 Shopify 后台或通过 API 设置库存更新 Webhook:
// 注册 Webhook
app.post('/webhooks/inventory/update', async (req, res) => {
try {
// 验证 Webhook 签名
const hmac = req.get('X-Shopify-Hmac-Sha256');
const body = JSON.stringify(req.body);
const hash = crypto.createHmac('sha256', process.env.SHOPIFY_WEBHOOK_SECRET)
.update(body, 'utf8')
.digest('base64');
if (hash !== hmac) {
return res.status(401).send('Unauthorized');
}
const inventoryLevel = req.body;
const variantId = inventoryLevel.inventory_item_id;
// 检查库存是否从0变为有库存
if (inventoryLevel.available > 0) {
await processRestockNotification(variantId);
}
res.status(200).send('OK');
} catch (error) {
console.error('Webhook 处理错误:', error);
res.status(500).send('Internal Server Error');
}
});2. 补货通知处理
async function processRestockNotification(variantId) {
try {
// 查找所有订阅此变体的用户
const subscriptions = await RestockSubscription.find({
variantId: variantId,
isActive: true
});
if (subscriptions.length === 0) {
return;
}
// 获取商品信息
const variant = await getShopifyVariant(subscriptions[0].shopDomain, variantId);
if (!variant) {
console.error('无法获取变体信息:', variantId);
return;
}
// 构建通知消息
const message = `🎉 好消息!您订阅的商品已经补货了!\n\n` +
`商品:${subscriptions[0].productTitle}\n` +
`规格:${subscriptions[0].variantTitle}\n` +
`价格:${variant.price}\n\n` +
`立即购买:${getProductUrl(subscriptions[0].shopDomain, subscriptions[0].productId, variantId)}\n\n` +
`数量有限,先到先得!`;
// 发送通知给所有订阅用户
const notifications = subscriptions.map(subscription =>
sendRestockNotification(subscription, message)
);
await Promise.all(notifications);
// 将订阅标记为已通知
await RestockSubscription.updateMany(
{ variantId: variantId, isActive: true },
{ isActive: false, notifiedAt: new Date() }
);
console.log(`补货通知已发送给 ${subscriptions.length} 位用户`);
} catch (error) {
console.error('补货通知处理错误:', error);
}
}
async function sendRestockNotification(subscription, message) {
try {
await sendLineMessage(subscription.lineUserId, message);
// 记录通知发送日志
console.log(`通知已发送: ${subscription.lineUserId} - ${subscription.productTitle}`);
} catch (error) {
console.error(`通知发送失败: ${subscription.lineUserId}`, error);
}
}
function getProductUrl(shopDomain, productId, variantId) {
return `https://${shopDomain}/products/${productId}?variant=${variantId}`;
}六、高级功能增强
1. LINE Rich Menu 集成
// 创建 Rich Menu
async function createLineRichMenu() {
const richMenu = {
size: {
width: 2500,
height: 1686
},
selected: true,
name: "购物助手菜单",
chatBarText: "购物助手",
areas: [
{
bounds: {
x: 0,
y: 0,
width: 1250,
height: 843
},
action: {
type: "postback",
data: "action=view_subscriptions"
}
},
{
bounds: {
x: 1250,
y: 0,
width: 1250,
height: 843
},
action: {
type: "uri",
uri: "https://yourshop.com"
}
}
]
};
try {
const response = await axios.post('https://api.line.me/v2/bot/richmenu', richMenu, {
headers: {
'Authorization': `Bearer ${LINE_CHANNEL_ACCESS_TOKEN}`,
'Content-Type': 'application/json'
}
});
console.log('Rich Menu 创建成功:', response.data);
return response.data.richMenuId;
} catch (error) {
console.error('Rich Menu 创建失败:', error.response?.data);
}
}2. 取消订阅功能
// 处理 LINE Postback 事件
app.post('/webhook/line', (req, res) => {
const events = req.body.events;
events.forEach(async (event) => {
if (event.type === 'postback') {
const userId = event.source.userId;
const data = new URLSearchParams(event.postback.data);
const action = data.get('action');
switch (action) {
case 'view_subscriptions':
await handleViewSubscriptions(userId);
break;
case 'cancel_subscription':
const variantId = data.get('variantId');
await handleCancelSubscription(userId, variantId);
break;
}
}
});
res.status(200).send('OK');
});
async function handleViewSubscriptions(userId) {
try {
const subscriptions = await RestockSubscription.find({
lineUserId: userId,
isActive: true
});
if (subscriptions.length === 0) {
await sendLineMessage(userId, '您目前没有任何商品订阅');
return;
}
let message = '📋 您的订阅列表:\n\n';
subscriptions.forEach((sub, index) => {
message += `${index + 1}. ${sub.productTitle}\n`;
message += ` 规格:${sub.variantTitle}\n\n`;
});
// 创建取消订阅的快捷回复
const quickReply = {
items: subscriptions.map(sub => ({
type: 'action',
action: {
type: 'postback',
label: `取消 ${sub.productTitle}`,
data: `action=cancel_subscription&variantId=${sub.variantId}`
}
}))
};
await sendLineMessage(userId, message, quickReply);
} catch (error) {
console.error('查看订阅失败:', error);
}
}七、技术选项总结
| 目标 | 实现方式 | 推荐工具 |
|---|---|---|
| 客户授权/获取 Line ID | LINE 登录 / 表单输入 | LINE Login API |
| 储存订阅数据 | Metafields / 外部数据库 | MongoDB / PostgreSQL |
| 自动检测库存变化 | Shopify Webhook | Inventory Levels Update |
| 发送 LINE 消息 | LINE Messaging API | Node.js/Express API |
| 服务器部署 | 云服务 | Heroku / Vercel / AWS |
八、部署清单
环境变量配置
# .env
LINE_CHANNEL_ACCESS_TOKEN=your_line_channel_access_token
LINE_CHANNEL_SECRET=your_line_channel_secret
LINE_LOGIN_CHANNEL_ID=your_line_login_channel_id
SHOPIFY_API_KEY=your_shopify_api_key
SHOPIFY_API_SECRET=your_shopify_api_secret
SHOPIFY_WEBHOOK_SECRET=your_webhook_secret
MONGODB_URI=your_mongodb_connection_stringShopify 主题设置
在 config/settings_schema.json 中添加:
{
"name": "LINE 通知设置",
"settings": [
{
"type": "text",
"id": "line_login_channel_id",
"label": "LINE Login Channel ID"
},
{
"type": "checkbox",
"id": "enable_line_restock_notification",
"label": "启用 LINE 到货通知",
"default": true
}
]
}九、测试验证
1. 功能测试步骤
- 订阅测试:在缺货商品页面测试订阅功能
- 通知测试:手动触发库存更新,验证通知发送
- 取消订阅测试:通过 LINE 消息测试取消订阅
- 错误处理测试:测试各种异常情况的处理
2. 监控和日志
// 添加日志记录
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'line-notifications.log' })
]
});
// 在关键操作中添加日志
logger.info('订阅创建', {
userId: lineId,
variantId,
timestamp: new Date()
});十、优化建议
1. 性能优化
- 使用消息队列(如 BullMQ、SQS)处理大量通知,避免 Webhook 超时
- 实现通知发送的批量处理(LINE Messaging API 支持 multicast,最多 500 收件人 / 次)
- 添加 Redis 缓存减少数据库查询
2. 用户体验优化
- 支持多语言通知消息(按
customer.locale渲染) - 在通知里附商品图片(LINE Rich Message)
- 加 “立即购买”按钮(Quick Reply)直接跳回 Shopify 商品页
3. 业务功能扩展
- 价格下降通知(监听
products/updateWebhook 的variants.price字段) - VIP 用户优先通知(提前 N 分钟推送)
- 限时抢购提醒(与 Launchpad 联动)
上线前检查清单
- LINE Messaging API 配置完成,Channel Access Token 安全存储(不要硬编码到主题)
- Webhook URL 用 HTTPS,且校验 LINE Signature(防伪造请求)
- 数据库字段加索引:
product_id + variant_id + status(高并发查询用) - 库存 Webhook:在 Shopify Admin → Notifications → Webhooks 注册
inventory_levels/update - 重复通知防护:同一用户同一 SKU 30 天内最多 1 次通知
- 退订机制:消息底部带”停止接收”链接(合规必备)
- 消息频率限制:单店铺 < 1000 条 / 分钟,避免触发 LINE 风控
- 降级方案:LINE API 故障时回退到 Email
- 审计日志:保留每次通知的发送记录至少 90 天
常见问题(FAQ)
Q:用户必须有 LINE 账号才能订阅吗? A:是。但不一定要让用户先注册——可以让用户先用 Email 订阅,到货时同时尝试 LINE(如绑定过)与 Email。亚太市场建议默认 LINE Login,欧美用户走 Email。
Q:LINE Messaging API 收费吗? A:免费额度内(500 条 / 月 推送 + 无限接收),超出后按阶梯收费。中小店铺通常不超出。详见 LINE 官方价格页。
Q:能用 Shopify 自带的”Back in Stock”功能吗? A:Shopify 没有原生”到货通知”功能。需要装 App(如 Back in Stock by Swym、Restock Rocket)或自建。App 大多只支持 Email + SMS,不支持 LINE——这正是本文方案存在的理由。
Q:库存变更的判定逻辑是什么?
A:监听 inventory_levels/update Webhook。判断条件:(previous.available <= 0) && (current.available > 0)。注意:直接监听 products/update 不够准,因为价格 / 描述更新也会触发。
Q:怎么防止通知风暴(一个 SKU 一夜补货 100 个,触发 100 条推送)?
A:核心做法——(1)每个用户每个 SKU 只发一次;(2)发送后立刻把订阅状态改为 sent;(3)批量推送时用 multicast 而非循环 push。
Q:用户在主题编辑器中能看到这个 Section 吗? A:建议把订阅表单封装成 Section(参考 Shopify Section 开发指南),让运营能控制是否启用、文案是什么。比硬编码到 product-form.liquid 灵活。
Q:合规上要注意什么? A:(1)订阅前明确告知发送频率与停止方式;(2)日本市场需符合 特定電子メール法,台湾市场参考 個人資料保護法;(3)保留用户同意的时间戳;(4)退订请求 48 小时内生效。
Q:能用 AI 做更智能的通知吗? A:可以——基于用户浏览历史推送”你关注的 XX 类商品已上架”。但先把基础流跑稳再扩展 AI 个性化。AI 个性化收益通常在月推送 > 1 万条后才显著。
延伸阅读
- Shopify Webhooks 完整指南 — Webhook 重试、HMAC 校验、幂等性设计
- Shopify Section 开发指南 — 把订阅表单封装为可配置 Section
- Shopify App 开发 — 把本方案打包成可复用 App
- 营销自动化实践 — 与 Email / SMS / EDM 流程联动
- 跨境电商进阶 — 不同市场的渠道选择策略
- GDPR 合规实施指南 — 欧盟用户参考(虽然 LINE 主要在亚太)
小结:LINE 到货通知是亚太市场 ROI 极高的获客 / 转化挽留工具——但实现复杂度也实实在在。建议先 MVP:只做”Email 订阅 + LINE 二次绑定”,跑两个月看转化率与单条成本,再决定要不要扩到更多营销场景(价格降价提醒、限时活动等)。