Shopifyアプリ(Remix)の開発 – Webhookの利用、注文イベントへ連動した処理

Shopify

はじめに

要再検討

  • 最終的に、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

関連記事

カテゴリー

アーカイブ

Lang »