Shopify实现订阅 LINE 到货通知
在Shopify中实现 “订阅 LINE 到货通知” 功能,让客户能够在商品售罄时订阅通知,当商品补货时自动接收LINE消息。本文将提供完整的技术实现方案。
✅ 目标功能
让客户在商品售罄时,输入 LINE ID 或授权 LINE 登录,当商品补货时,自动通过 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/proxy
2. 订阅 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_string
Shopify 主题设置
在 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. 性能优化
- 使用消息队列处理大量通知
- 实现通知发送的批量处理
- 添加缓存机制减少数据库查询
2. 用户体验优化
- 支持多语言通知消息
- 添加商品图片到通知中
- 实现智能推荐相关商品
3. 业务功能扩展
- 支持价格降低通知
- 添加 VIP 用户优先通知
- 实现限时抢购提醒
通过以上完整的实现方案,您可以为 Shopify 商店添加强大的 LINE 到货通知功能,提升客户购物体验和商店转化率。
最后更新时间: