Files
linumiq-invoice/app/routes/webhooks.orders.fulfilled.tsx
T
Gerhard Scheikl 93aec2f368 refactor(automations): detect manual payment via OrderTransaction.manualPaymentGateway
- Drop wireTransferGatewayNames from ShopSettings (new migration).
- Replace string-matching with a GraphQL query against
  Order.transactions[].manualPaymentGateway, the first-class flag
  Shopify exposes for any merchant-defined manual payment method.
- Both webhook handlers now fetch the order on the fly to classify it,
  removing the configurable gateway-names field from settings.
2026-05-09 20:31:31 +02:00

52 lines
1.8 KiB
TypeScript

import type { ActionFunctionArgs } from "react-router";
import { authenticate } from "../shopify.server";
import db from "../db.server";
import {
generateAndEmailInvoice,
isManualPaymentOrder,
} from "../services/invoice/automations.server";
/**
* orders/fulfilled — Automation 2: when an order is fulfilled and is NOT a
* wire-transfer (manual-payment-gateway) order, automatically email the
* invoice to the customer. Manual-gateway orders are intentionally skipped
* because Automation 1 already emailed them at order-create time.
*/
export const action = async ({ request }: ActionFunctionArgs) => {
const { shop, topic, payload, session, admin } = await authenticate.webhook(request);
console.log(`Received ${topic} webhook for ${shop}`);
if (!session || !admin) {
// App was uninstalled before the webhook drained — nothing to do.
return new Response();
}
const settings = await db.shopSettings.findUnique({ where: { shopDomain: shop } });
if (!settings?.autoEmailOnFulfilledNonWireTransfer) return new Response();
const orderId = payload?.id;
if (orderId == null) return new Response();
const orderGid = `gid://shopify/Order/${String(orderId).replace(/^.*\//, "")}`;
if (await isManualPaymentOrder(admin, orderGid)) {
// Manual / wire-transfer order — handled by Automation 1, skip here.
return new Response();
}
try {
const result = await generateAndEmailInvoice({
shopDomain: shop,
admin,
orderId,
customerLocale: typeof payload?.customer_locale === "string" ? payload.customer_locale : undefined,
});
if (!result.ok) {
console.warn(`auto-email (fulfilled) failed for order ${orderId} on ${shop}: ${result.reason}`);
}
} catch (err) {
console.error(`auto-email (fulfilled) crashed for order ${orderId} on ${shop}:`, err);
}
return new Response();
};