Shopify App 开发:订阅 Webhook 的三种实现方式

917 阅读6分钟

背景

在实际项目中,经常需要在 Shopify 商店发生特定事件时能够自动接收到通知。这些事件可以是新订单的创建、产品的添加、购物车的更新等等。好在 Shopify 本身向开发者提供了丰富的 Webhooks 列表,当你在 Shopify 应用中订阅了特定的事件后,一旦这些事件发生,Shopify 就会向你指定的 URL 发送包含事件详情的数据。这些数据通常以 JSON 格式发送,易于解析和处理。

本文主要总结了本人在实际项目中实践过的3种订阅 Shopify Webhook 的方式。

目录

  • 准备工作
  • 方案一:Shopify 商店后台配置
  • 方案二:shopify.app.toml 配置
  • 方案三:GraphQL Admin API 配置
  • 相关拓展

准备工作

Webhook 测试地址

因为国内的网络问题,我们在使用内网穿透工具启动本地服务时, Shopify 通过内网穿透的形式无法正常访问我们本地的服务,因此会导致 Webhook 订阅失败。

本文主要聚焦于 Shopify Webhook 本身的功能,而非网络服务问题的解决,因此这里推荐给大家一个免费的可以用于测试的 Webhook 站点工具,以此来确认是否成功的订阅了 Shopify 的 Webhook,站点工具地址是:webhook.site/

进入网站,获取到测试的 Webhook 地址,示例如下:

图1-1.png

Shopify 应用权限

为了保证我们能够正常的订阅到 Shopify 中的事件,我们需要确认是否拥有要订阅的事件对应的权限,相关权限可以参考下面两个文档进行确认:

图1-2.png

图1-3.png

实际业务中,在我们决定要订阅不同的 Webhook 事件之前,一定要先保证我们已经配置了相应事件的权限,否则会导致无法正常订阅。

配置权限,可以在 Shopify App 项目的 shopify.app.toml 文件中,配置示例如下:

[access_scopes]
# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes
scopes = "write_products"

需要注意的是,每当修改了 shopify.app.toml 文件中的任意内容时,必须将其发布到 Shopify 后台中,否则将没有任何效果。可以使用 shopify app deploy 推送新的配置。

tips: 由于本文重点在于如何配置 Webhook 订阅,因此针对如何设置权限,不过多讨论。

方案一:Shopify 商店后台配置

  • 登录你的商店后台,即:https://admin.shopify.com/store/xxx

  • 进入左侧“设置”->“通知”->“Webhook”。

    图2-1.png

  • 进入 webhook 测试站点,复制系统随机生成的 webhook 地址,并保持页面不要关闭,方便随时查看接口响应。

  • 在 Shopify 的 Webhook 页面中,新增一个订阅事件,并将刚才复制的 URL 填进去。

    图2-2.png

  • 添加完成后,可以点击“发送测试”按钮,查看是否订阅成功

    图2-3.png

  • 如果不出意外,我们将在 webhook.site 站点收到刚才的测试请求:

    图2-4.png

  • 为了验证我们的 Webhook 订阅成功,我们可以手动的创建一个产品,再次验证这个 Webhook

    图2-5.png

  • 创建一个用于测试的产品

    图2-6.png

  • 如果不出意外,我们已经收到了 Webhook 数据

    图2-7.png

  • 至此,我们实现了最简单的 Webhook 订阅配置,此方法的优缺点也很明显:

    • 优点:操作简单,不需要涉及代码开发
    • 缺点:仅支持一些基础的 Webhook 事件,不能覆盖 Shopify 全部的 Webhooks 事件列表

方案二: shopify.app.toml 配置

在测试之前,请确保你的 shopify.app.toml 配置中已设置好所需的 [access_scopes]

打开 Shopify App 项目的 shopify.app.toml 文件,以创建产品这个事件为例,订阅 Webhook 的配置如下:

[webhooks]
api_version = "2024-07"
[[webhooks.subscriptions]]
 topics = [ "products/create"]
 uri = "https://webhook.site/9523687d-5460-41b4-9093-d8a3d13e6dce"

配置说明:

  • webhooks 需要设置对应的 api_version 版本,建议使用最新的稳定版。
  • webhooks.subscriptions 设置需要订阅的 Shopify 事件
    • topics 为所有订阅事件的数组,全部事件可以参考文档 webhook topics list
    • uri 为订阅 webhook 的地址,此处依然采用测试地址,可以根据实际情况改成业务地址

配置完成后,通过 shopify app dev --reset 重置一遍配置文件,并选择项目为已存在项目,之后才会正常触发修改后的 webhook 订阅。

由于订阅效果跟方案一相同,因此就不重复贴示例图了。

方案三:GraphQL Admin API 配置

除了使用配置文件外,我们也可以通过代码的方式,在项目运行阶段,动态的设置 Webhook。

具体实现是在 shopify.server.ts 文件中,示例代码如下:

import "@shopify/shopify-app-remix/adapters/node";

import {
  ApiVersion,
  AppDistribution,
  DeliveryMethod,
  shopifyApp,
} from "@shopify/shopify-app-remix/server";

import { PrismaSessionStorage } from "@shopify/shopify-app-session-storage-prisma";
import { restResources } from "@shopify/shopify-api/rest/admin/2024-07";
import prisma from "./db.server";

const shopify = shopifyApp({
  apiKey: process.env.SHOPIFY_API_KEY,
  apiSecretKey: process.env.SHOPIFY_API_SECRET || "",
  apiVersion: ApiVersion.July24,
  scopes: process.env.SCOPES?.split(","),
  appUrl: process.env.SHOPIFY_APP_URL || "",
  authPathPrefix: "/auth",
  sessionStorage: new PrismaSessionStorage(prisma),
  distribution: AppDistribution.AppStore,
  restResources,
  // 重点是 webhooks 和 hooks 部分
  webhooks: {
    PRODUCTS_CREATE: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "https://webhook.site/9523687d-5460-41b4-9093-d8a3d13e6dce",
    },
  },
  hooks: {
    afterAuth: async ({ session }) => {
      shopify.registerWebhooks({ session });
    },
  },
  future: {
    unstable_newEmbeddedAuthStrategy: true,
  },
  ...(process.env.SHOP_CUSTOM_DOMAIN
    ? { customShopDomains: [process.env.SHOP_CUSTOM_DOMAIN] }
    : {}),
});
  
export default shopify;
export const apiVersion = ApiVersion.July24;
export const addDocumentResponseHeaders = shopify.addDocumentResponseHeaders;
export const authenticate = shopify.authenticate;
export const unauthenticated = shopify.unauthenticated;
export const login = shopify.login;
export const registerWebhooks = shopify.registerWebhooks;
export const sessionStorage = shopify.sessionStorage;
  • 代码说明:
    • 跟 webhook 相关的代码,主要是 webhookshooks 两部分
      • hooks 可以保持不变,除非你了解其中逻辑
      • webhooks 中则是具体的事件订阅设置
    • 其中 PRODUCTS_CREATEwebhook topics list中的 products/create 对应,但注意需要大写
    • deliveryMethod 固定设置 DeliveryMethod.Http 即可
    • callbackUrl 为订阅 webhook 的接口地址

配置完成后,通过 shopify app dev --reset 重置一遍配置文件,并选择项目为已存在项目,之后才会正常触发修改后的 webhook 订阅。

由于订阅效果跟方案一相同,因此就不重复贴示例图了。

相关拓展

  • 如果你仔细阅读过官方文档 shopify.dev/docs/apps/b… ,你会发现官方比较推荐使用 Google Cloud 的 Pub/Sub 或者是 Amazon EventBridge 来实现 Webhook 订阅。但个人认为除非有特殊要求,否则没有必要再引入一个技术链路,因为这意味着整个功能的链路额外增加了一层,当我们遇到问题时,将更难确定是哪一层出现了问题。

  • 我们在开发 Shopify App 时,本身就启动了一个基于 Remix.js 的 Node.js 服务,因此我们可以考虑用这个服务来接收 Shopify 的 Webhook 事件,而且实践起来也比较简单,感兴趣的同学可以尝试,这里将核心关键点列举出来:

    • 只支持方案二和方案三,将对于的 uri 改为相对路径如:/webhooks
    • 在 Shopify App 的 Remix.js 服务中,增加一个 /webhooks 路由
    • 在这个路由中即可获取到 Shopify 的 Webhook 订阅事件
    • 需要注意,国内的穿透服务可能无法支持这种用法,这个跟网络有关,可以用 shopify app dev 启动,而不要设置指定的隧道域名,或是尝试海外的 ngrok 穿透服务
  • 我们可以在 https://partners.shopify.com/ 后台,看到 Webhook 日志,这方便我们排查 Webhook 发送失败的原因。

    图3-1.png

    图3-2.png

  • 还有更多关于 Webhook 的内容,比如发送失败的重连次数,是否有重复订阅的 Webhook 的校验等,感兴趣的同学可以查阅官方文档

参考文档

浏览知识共享许可协议

知识共享许可协议
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。