如何在Shopify应用安装时自动创建商品删除Webhook
解决方案
核心思路
在应用OAuth安装回调完成后,利用获取到的店铺Access Token调用Shopify Admin API自动注册products/delete类型的Webhook,同时配置Webhook接收端点处理商品删除事件,同步操作MySQL数据库。
步骤1:配置Shopify应用上下文与OAuth流程
确保你的应用已初始化Shopify API上下文,并包含必要权限(read_products是注册商品删除Webhook的必备权限):
const { Shopify } = require('@shopify/shopify-api'); Shopify.Context.initialize({ API_KEY: process.env.SHOPIFY_API_KEY, API_SECRET_KEY: process.env.SHOPIFY_API_SECRET, SCOPES: ['read_products'], // 必须包含该权限 HOST_NAME: process.env.SHOPIFY_APP_HOST.replace(/https?:\/\//, ''), API_VERSION: '2024-04', // 使用Shopify最新稳定API版本 IS_EMBEDDED_APP: true, });
步骤2:在OAuth回调中自动注册Webhook
应用安装完成后,OAuth回调会返回店铺会话信息,此时调用Webhook注册API:
async function handleOAuthCallback(req, res) { try { // 验证OAuth回调并获取会话 const session = await Shopify.Auth.validateAuthCallback( req, res, req.query ); // 注册商品删除Webhook const webhookRegistration = await Shopify.Webhooks.Registry.register({ shop: session.shop, accessToken: session.accessToken, topic: 'products/delete', // 指定Webhook类型 path: '/webhooks/products-delete', // 你的Webhook接收端点 // 直接定义Webhook处理逻辑 webhookHandler: async (topic, shop, rawBody) => { const productData = JSON.parse(rawBody); const shopifyProductId = productData.id; // 同步删除MySQL中的对应商品 const mysql = require('mysql2/promise'); const dbConnection = await mysql.createConnection({ host: process.env.MYSQL_HOST, user: process.env.MYSQL_USER, password: process.env.MYSQL_PASSWORD, database: process.env.MYSQL_DB_NAME, }); await dbConnection.execute( 'DELETE FROM products WHERE shopify_product_id = ?', [shopifyProductId] ); await dbConnection.end(); }, }); if (webhookRegistration.success) { console.log('商品删除Webhook注册成功'); } else { console.error('Webhook注册失败:', webhookRegistration.result); } // 跳转到应用后台页面 res.redirect('/dashboard'); } catch (error) { console.error('OAuth回调处理失败:', error); res.status(500).send('安装失败'); } } // 挂载OAuth回调路由(以Express为例) app.get('/auth/callback', handleOAuthCallback);
步骤3:配置Webhook接收端点与签名验证
Shopify发送Webhook时会附带签名,必须验证签名合法性,防止恶意请求:
app.post('/webhooks/products-delete', async (req, res) => { try { // 获取Shopify发送的签名与店铺信息 const hmacHeader = req.headers['x-shopify-hmac-sha256']; const shopDomain = req.headers['x-shopify-shop-domain']; // 读取原始请求体(必须未解析,否则签名验证会失败) const rawBody = await new Promise((resolve) => { let data = ''; req.on('data', chunk => data += chunk); req.on('end', () => resolve(data)); }); // 验证Webhook签名 const isValid = Shopify.Webhooks.validate({ rawBody, hmac: hmacHeader, shop: shopDomain, apiSecretKey: Shopify.Context.API_SECRET_KEY, }); if (!isValid) { return res.status(401).send('无效的Webhook签名'); } // 若已在注册时定义handler,这里可直接返回200;否则重复处理逻辑 res.status(200).send('OK'); } catch (error) { console.error('Webhook处理失败:', error); res.status(500).send('处理失败'); } });
关键注意事项
- HTTPS要求:Webhook接收端点必须是公开可访问的HTTPS地址,本地开发可使用ngrok等工具转发
- 快速响应:处理Webhook时需尽快返回200状态码,Shopify会在超时未响应时重试
- 权限检查:确保应用的OAuth scopes包含
read_products,否则无法注册商品相关Webhook - 错误重试:可在MySQL删除逻辑中添加错误日志,便于排查同步失败情况
内容的提问来源于stack exchange,提问作者Hans Handanov
相关产品推荐
相关产品推荐

