はじめに
- tomlファイルでのWebhook設定例、購読側コードの実装例
- Webhookトピックのリスト
- shopify.server.tsの設定
- tomlファイルの設定
要再検討
- 最終的に、app/routes/webhook.tsxでウェブフックを購読することに成功した
- そこに至るまでに、かなりのトライ&エラーがあり、どれが本当に必要なのか見極めてない
- ネットにも、ハマっている人が多数いる
- そもそも、tomlとshopify.server.tsは両方とも記述が必要なのか
開発
shopify.app.toml
- ShopifyアプリのストアURL/webhooksでウェブフックorders/createトピックを購読する場合の設定
- スコープは、このファイルの設定だけで、問題なかった
[access_scopes]
scopes = "read_customers,read_orders,read_products,write_orders,write_products"
[webhooks]
api_version = "2024-07"
[[webhooks.subscriptions]]
topics = [ "app/uninstalled", "orders/create" ]
uri = "/webhooks"
shopify.server.ts
- tomlと冗長のような気もするが、先にtomlだけやってだめだったので、こちらも追加して、成功した。tomlの方が不要の可能性が残っている
import "@shopify/shopify-app-remix/adapters/node";
import {
ApiVersion,
AppDistribution,
shopifyApp,
DeliveryMethod
} 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(","),
webhooks: {
APP_UNINSTALLED: {
deliveryMethod: DeliveryMethod.Http,
callbackUrl: '/webhooks',
},
ORDERS_CREATE: {
deliveryMethod: DeliveryMethod.Http,
callbackUrl: '/webhooks',
},
},
hooks: {
afterAuth: async ({session}) => {
console.log("hooks are being registered!!!")
await shopify.registerWebhooks({session});
},
},
appUrl: process.env.SHOPIFY_APP_URL || "",
authPathPrefix: "/auth",
sessionStorage: new PrismaSessionStorage(prisma),
distribution: AppDistribution.AppStore,
restResources,
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;
app/routes/webhooks.tsx
- ORDERS_CREATEトピックを受け取った場合の処理の追加箇所
- webhookはPOSTをつかうため、loaderは不要で、actionのみでいいという理解
import type { ActionFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import db from "../db.server";
export const action = async ({ request }: ActionFunctionArgs) => {
console.log("action !!!!!!!!!!");
const { topic, shop, session, admin, payload } = await authenticate.webhook(request);
if (!admin) {
// The admin context isn't returned if the webhook fired after a shop was uninstalled.
throw new Response();
}
// The topics handled here should be declared in the shopify.app.toml.
// More info: https://shopify.dev/docs/apps/build/cli-for-apps/app-configuration
switch (topic) {
case "ORDERS_CREATE":
console.log("orders/create: ", payload);
break;
case "APP_UNINSTALLED":
if (session) {
await db.session.deleteMany({ where: { shop } });
}
break;
case "CUSTOMERS_DATA_REQUEST":
case "CUSTOMERS_REDACT":
case "SHOP_REDACT":
default:
throw new Response("Unhandled webhook topic", { status: 404 });
}
throw new Response();
};
結果
- nvm run devを実行している、vscodeのコンソールのログの抜粋
19:03:17 │ remix │ Running pre-dev command: "npx prisma generate"
19:03:17 │ graphiql │ GraphiQL server started on port 3457
19:03:17 │ webhooks │ Sending APP_UNINSTALLED webhook to app server
19:03:18 │ remix │ Environment variables loaded from .env
19:03:18 │ remix │ Prisma schema loaded from prisma/schema.prisma
19:03:18 │ remix │ Datasource "db": SQLite database "dev.sqlite" at "file:dev.sqlite"
19:03:18 │ remix │
19:03:18 │ remix │ 1 migration found in prisma/migrations
19:03:18 │ remix │
19:03:18 │ remix │
19:03:18 │ remix │ No pending migrations to apply.
19:03:19 │ remix │ ➜ Local: http://localhost:54826/
19:03:19 │ remix │ ➜ Network: use --host to expose
19:03:21 │ remix │ [shopify-api/INFO] version 11.2.0, environment Remix
19:03:21 │ remix │ [shopify-app/INFO] Future flag wip_optionalScopesApi is disabled.
19:03:21 │ remix │
19:03:21 │ remix │ Enable this to use the optionalScopes API to request additional scopes and
manage them.
19:03:21 │ remix │
19:03:22 │ remix │ (node:8664) ExperimentalWarning: Importing JSON modules is an experimental
feature and might change at any time
19:03:22 │ remix │ (Use `node --trace-warnings ...` to show where the warning was created)
19:03:22 │ remix │ action !!!!!!!!!!
19:03:22 │ webhooks │ APP_UNINSTALLED webhook delivered
19:03:55 │ remix │ [shopify-app/INFO] Authenticating admin request
19:03:55 │ remix │ [shopify-app/INFO] Authenticating admin request
19:03:55 │ remix │ [shopify-app/INFO] No valid session found
19:03:55 │ remix │ [shopify-app/INFO] Requesting offline access token
19:03:55 │ remix │ [shopify-app/INFO] No valid session found
19:03:55 │ remix │ [shopify-app/INFO] Requesting offline access token
19:03:55 │ remix │ [shopify-api/INFO] Creating new session | {shop:
s3labteststore2.myshopify.com, isOnline: false}
19:03:55 │ remix │ [shopify-api/INFO] Creating new session | {shop:
s3labteststore2.myshopify.com, isOnline: false}
19:03:55 │ remix │ [shopify-app/INFO] Running afterAuth hook
19:03:55 │ remix │ hooks are being registered!!!
19:03:55 │ remix │ [shopify-api/INFO] Registering webhooks | {shop:
s3labteststore2.myshopify.com}
19:04:38 │ remix │ action !!!!!!!!!!
19:04:38 │ remix │ orders/create: {
19:04:38 │ remix │ id: xxxxx,
19:04:38 │ remix │ admin_graphql_api_id: 'gid://shopify/Order/xxxxx',
19:04:38 │ remix │ app_id: xxxxx,
19:04:38 │ remix │ browser_ip: 'xxxxx',
19:04:38 │ remix │ buyer_accepts_marketing: true,
19:04:38 │ remix │ cancel_reason: null,
19:04:38 │ remix │ cancelled_at: null,
19:04:38 │ remix │ cart_token: 'xxxxx',
19:04:38 │ remix │ checkout_id: xxxxx,
19:04:38 │ remix │ checkout_token: 'xxxxx',
・・・
19:04:38 │ remix │ }
19:04:38 │ remix │ ]
19:04:38 │ remix │ }
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
› Press d │ toggle development store preview: ✔ on
› Press g │ open GraphiQL (Admin API) in your browser
› Press p │ preview in your browser
› Press q │ quit