Files
linumiq-invoice/app/routes/webhooks.orders.create.tsx
T
Gerhard Scheikl 0800d1160b feat(automations): auto-email invoice on wire-transfer placed and on fulfillment
- New ShopSettings fields: autoEmailOnWireTransferPlaced,
  autoEmailOnFulfilledNonWireTransfer, wireTransferGatewayNames.
- New Automations section in settings with two toggles + gateway list.
- orders/create webhook now fires automation 1 (wire-transfer placed).
- New orders/fulfilled webhook fires automation 2 (non-wire-transfer fulfilled).
- Shared helper services/invoice/automations.server.ts handles classification
  and idempotent generate+send (skips if already sent).
- Webhook subscription for orders/fulfilled added to all 3 app tomls.

This is the non-Plus fallback for Shopify Flow, whose custom-app actions
are gated to Plus stores only.
2026-05-09 20:21:41 +02:00

50 lines
1.7 KiB
TypeScript

import type { ActionFunctionArgs } from "react-router";
import { authenticate } from "../shopify.server";
import db from "../db.server";
import {
generateAndEmailInvoice,
isWireTransferOrder,
} from "../services/invoice/automations.server";
/**
* orders/create — Automation 1: when a wire-transfer order is placed,
* immediately generate and email the invoice (which includes the bank
* details + GiroCode) so the customer can pay. Other orders are ignored
* here; they're handled by orders/fulfilled (Automation 2).
*/
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) return new Response();
const settings = await db.shopSettings.findUnique({ where: { shopDomain: shop } });
if (!settings?.autoEmailOnWireTransferPlaced) return new Response();
const orderId = payload?.id;
if (orderId == null) return new Response();
const gateways: string[] = Array.isArray(payload?.payment_gateway_names)
? payload.payment_gateway_names
: [];
if (!isWireTransferOrder(gateways, settings.wireTransferGatewayNames)) {
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 (wire-transfer placed) failed for order ${orderId} on ${shop}: ${result.reason}`);
}
} catch (err) {
console.error(`auto-email (wire-transfer placed) crashed for order ${orderId} on ${shop}:`, err);
}
return new Response();
};