@develemit/commerce-server 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +2 -1
- package/dist/index.d.ts +58 -4
- package/dist/index.js +70 -20
- package/dist/index.js.map +1 -1
- package/docs/payments.md +37 -0
- package/migrations/0010_order_tax.sql +1 -0
- package/migrations/0011_product_tax_code.sql +1 -0
- package/migrations/meta/0010_snapshot.json +1042 -0
- package/migrations/meta/0011_snapshot.json +1048 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/admin/routes.ts","../src/catalog/types.ts","../src/shared/cursor.ts","../src/shipping/repo.ts","../src/db/schema.ts","../src/shipping/service.ts","../src/admin/repo.ts","../src/admin/service.ts","../src/admin/types.ts","../src/admin/publish.ts","../src/admin/slug.ts","../src/admin/delete.ts","../src/admin/images.ts","../src/admin/upload.ts","../src/catalog/routes.ts","../src/catalog/cache-hooks.ts","../src/inventory/repo.ts","../src/inventory/types.ts","../src/inventory/service.ts","../src/catalog/repo.ts","../src/catalog/service.ts","../src/orders/admin-routes.ts","../src/orders/admin-service.ts","../src/orders/admin-repo.ts","../src/orders/repo.ts","../src/orders/refund-repo.ts","../src/orders/order-detail.ts","../src/orders/refund-errors.ts","../src/discounts/evaluate.ts","../src/discounts/repo.ts","../src/discounts/redemptions.ts","../src/payment/gateway.ts","../src/orders/gateway-refunds.ts","../src/orders/apply-event.ts","../src/orders/types.ts","../src/orders/order-service.ts","../src/orders/ship-service.ts","../src/orders/refund-service.ts","../src/orders/resolve-service.ts","../src/discounts/admin-routes.ts","../src/discounts/admin-service.ts","../src/orders/dev-buyer.ts","../src/orders/dev-checkout.ts","../src/orders/routes.ts","../src/discounts/service.ts","../src/orders/checkout-service.ts","../src/orders/webhook-routes.ts","../src/payment/fake-gateway.ts","../src/plugin.ts","../src/payment/stripe-loader.ts","../src/payment/stripe-gateway.ts","../src/hold-sweeper.ts","../src/emails/order-lines.ts","../src/db/migrate.ts","../src/seed.ts","../src/shipping/seed.ts","../src/storage/memory.ts","../src/storage/disk.ts","../src/payment/resolve.ts"],"sourcesContent":["import {\n adminProductInputSchema,\n adminProductPatchSchema,\n adminProductPageSchema,\n imageOrderInputSchema,\n productImagePatchSchema,\n shippingRatesInputSchema,\n} from '@develemit/commerce-contracts';\nimport type { FastifyPluginAsync, FastifyRequest } from 'fastify';\nimport { ProductNotFoundError } from '../catalog/types';\nimport { InvalidCursorError } from '../shared/cursor';\nimport type { CommerceDb } from '../db/types';\nimport { listShippingRates, setShippingRates } from '../shipping/service';\nimport type { FileStorage } from '../storage/types';\nimport { deleteAdminProduct } from './delete';\nimport {\n addProductImage,\n removeProductImage,\n reorderProductImages,\n updateProductImageAlt,\n} from './images';\nimport {\n createAdminProduct,\n getAdminProduct,\n getAdminProductCounts,\n listAdminProducts,\n patchAdminProduct,\n} from './service';\nimport {\n InvalidImageOrderError,\n ProductImageNotFoundError,\n ProductIncompleteError,\n ProductNotDeletableError,\n} from './types';\nimport { InvalidImageError, MAX_IMAGE_BYTES } from './upload';\n\nexport interface AdminOptions {\n authorize(req: FastifyRequest): Promise<boolean>;\n storage: FileStorage;\n requirePhotoToPublish?: boolean;\n}\n\nexport interface AdminPluginOptions extends AdminOptions {\n db: CommerceDb;\n currency: string;\n}\n\nfunction isZodError(err: unknown): err is Error & { issues: unknown[] } {\n return err instanceof Error && err.name === 'ZodError';\n}\n\nexport const adminPlugin: FastifyPluginAsync<AdminPluginOptions> = async (\n app,\n { db, currency, authorize, storage, requirePhotoToPublish },\n) => {\n const { default: multipart } = await import('@fastify/multipart');\n await app.register(multipart, {\n limits: { fileSize: MAX_IMAGE_BYTES, files: 1 },\n });\n\n app.setErrorHandler((err, _req, reply) => {\n if (isZodError(err)) {\n return reply\n .code(400)\n .send({ error: 'INVALID_INPUT', issues: err.issues });\n }\n if (\n err instanceof ProductNotFoundError ||\n err instanceof ProductImageNotFoundError\n ) {\n return reply.code(404).send({ error: err.code, message: err.message });\n }\n if (err instanceof ProductIncompleteError) {\n return reply.code(422).send({\n error: err.code,\n message: err.message,\n missing: err.missing,\n });\n }\n if (err instanceof ProductNotDeletableError) {\n return reply.code(409).send({\n error: err.code,\n message: err.message,\n reason: err.reason,\n });\n }\n if (\n err instanceof InvalidCursorError ||\n err instanceof InvalidImageError ||\n err instanceof InvalidImageOrderError\n ) {\n return reply.code(400).send({ error: err.code, message: err.message });\n }\n if ((err as { code?: string }).code === 'FST_REQ_FILE_TOO_LARGE') {\n return reply.code(413).send({ error: 'IMAGE_TOO_LARGE' });\n }\n return reply.send(err);\n });\n\n app.addHook('preHandler', async (req, reply) => {\n if (!(await authorize(req))) {\n return reply.code(401).send({ error: 'UNAUTHORIZED' });\n }\n });\n\n app.get('/products', async (req) =>\n adminProductPageSchema.parse(\n await listAdminProducts(db, req.query as object),\n ),\n );\n\n app.get('/products/counts', async () => getAdminProductCounts(db));\n\n app.post('/products', async (req, reply) => {\n const input = adminProductInputSchema.parse(req.body);\n const created = await createAdminProduct(db, currency, input, {\n requirePhotoToPublish,\n });\n return reply.code(201).send(created);\n });\n\n app.get<{ Params: { id: string } }>('/products/:id', async (req) =>\n getAdminProduct(db, req.params.id),\n );\n\n app.patch<{ Params: { id: string } }>('/products/:id', async (req) =>\n patchAdminProduct(\n db,\n req.params.id,\n adminProductPatchSchema.parse(req.body),\n { requirePhotoToPublish },\n ),\n );\n\n app.delete<{ Params: { id: string } }>(\n '/products/:id',\n async (req, reply) => {\n await deleteAdminProduct(db, storage, req.params.id);\n return reply.code(204).send();\n },\n );\n\n app.post<{ Params: { id: string } }>(\n '/products/:id/images',\n async (req, reply) => {\n const part = await req.file();\n if (!part) throw new InvalidImageError('An image file is required');\n const bytes = await part.toBuffer();\n const fields = part.fields as Record<string, { value?: unknown }>;\n const alt =\n typeof fields.alt?.value === 'string'\n ? productImagePatchSchema.parse({ alt: fields.alt.value }).alt\n : undefined;\n const product = await addProductImage(db, storage, req.params.id, {\n bytes,\n alt,\n });\n return reply.code(201).send(product);\n },\n );\n\n app.patch<{ Params: { id: string } }>(\n '/products/:id/images/order',\n async (req) =>\n reorderProductImages(\n db,\n req.params.id,\n imageOrderInputSchema.parse(req.body).imageIds,\n ),\n );\n\n app.patch<{ Params: { id: string; imageId: string } }>(\n '/products/:id/images/:imageId',\n async (req) =>\n updateProductImageAlt(\n db,\n req.params.id,\n req.params.imageId,\n productImagePatchSchema.parse(req.body).alt,\n ),\n );\n\n app.delete<{ Params: { id: string; imageId: string } }>(\n '/products/:id/images/:imageId',\n async (req) =>\n removeProductImage(db, storage, req.params.id, req.params.imageId),\n );\n\n app.get('/shipping-rates', async () => listShippingRates(db));\n\n app.put('/shipping-rates', async (req) =>\n setShippingRates(db, shippingRatesInputSchema.parse(req.body)),\n );\n};\n","export class ProductNotFoundError extends Error {\n readonly code = 'PRODUCT_NOT_FOUND';\n constructor(public readonly slug: string) {\n super(`Product not found: ${slug}`);\n this.name = 'ProductNotFoundError';\n }\n}\n\nexport interface CatalogFilter {\n category?: string;\n featured?: boolean;\n limit: number;\n cursor?: string;\n}\n\nexport interface PurchasableProduct {\n id: string;\n name: string;\n priceCents: number;\n currency: string;\n}\n\nexport type CategoryLabels =\n Record<string, string> | ((slug: string) => string | undefined);\n","export class InvalidCursorError extends Error {\n readonly code = 'INVALID_CURSOR';\n constructor() {\n super('The cursor is malformed');\n this.name = 'InvalidCursorError';\n }\n}\n\nexport interface Cursor {\n at: string;\n key: string;\n}\n\nexport const PG_TIMESTAMP =\n /^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(\\.\\d+)?[+-]\\d{2}(:?\\d{2})?$/;\n\nexport function isCursorTimestamp(at: string): boolean {\n return PG_TIMESTAMP.test(at) || !Number.isNaN(Date.parse(at));\n}\n\nexport function encodeCursor({ at, key }: Cursor): string {\n return Buffer.from(`${at}|${key}`).toString('base64url');\n}\n\nexport function decodeCursor(\n raw: string,\n accepts: (cursor: Cursor) => boolean,\n): Cursor {\n const text = Buffer.from(raw, 'base64url').toString('utf8');\n const split = text.indexOf('|');\n const cursor = { at: text.slice(0, split), key: text.slice(split + 1) };\n if (split < 0 || !cursor.key || !accepts(cursor)) {\n throw new InvalidCursorError();\n }\n return cursor;\n}\n","import { and, asc, eq, notInArray } from 'drizzle-orm';\nimport type { CommerceExecutor } from '../db/types';\nimport { shippingRates } from '../db/schema';\n\nexport interface ShippingRateOption {\n label: string;\n amountCents: number;\n minDays?: number;\n maxDays?: number;\n}\n\nexport async function listActiveRates(\n db: CommerceExecutor,\n currency: string,\n): Promise<ShippingRateOption[]> {\n const rows = await db\n .select()\n .from(shippingRates)\n .where(\n and(eq(shippingRates.active, true), eq(shippingRates.currency, currency)),\n )\n .orderBy(asc(shippingRates.sort), asc(shippingRates.amountCents));\n return rows.map((r) => ({\n label: r.label,\n amountCents: r.amountCents,\n ...(r.minDays !== null && { minDays: r.minDays }),\n ...(r.maxDays !== null && { maxDays: r.maxDays }),\n }));\n}\n\nexport type ShippingRateRow = typeof shippingRates.$inferSelect;\n\nexport async function listAllRates(\n db: CommerceExecutor,\n): Promise<ShippingRateRow[]> {\n return db\n .select()\n .from(shippingRates)\n .orderBy(asc(shippingRates.sort), asc(shippingRates.label));\n}\n\nexport async function replaceRates(\n tx: CommerceExecutor,\n rates: (typeof shippingRates.$inferInsert)[],\n): Promise<void> {\n const labels = rates.map((r) => r.label);\n await tx\n .delete(shippingRates)\n .where(\n labels.length > 0 ? notInArray(shippingRates.label, labels) : undefined,\n );\n for (const rate of rates) {\n await tx\n .insert(shippingRates)\n .values(rate)\n .onConflictDoUpdate({\n target: shippingRates.label,\n set: {\n amountCents: rate.amountCents,\n currency: rate.currency,\n minDays: rate.minDays ?? null,\n maxDays: rate.maxDays ?? null,\n active: rate.active ?? true,\n sort: rate.sort ?? 0,\n },\n });\n }\n}\n","import { sql } from 'drizzle-orm';\nimport {\n boolean,\n check,\n index,\n integer,\n jsonb,\n pgEnum,\n pgTable,\n text,\n unique,\n timestamp,\n uuid,\n} from 'drizzle-orm/pg-core';\n\nexport const productStatus = pgEnum('commerce_product_status', [\n 'draft',\n 'active',\n 'archived',\n]);\n\nexport const products = pgTable(\n 'commerce_products',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n slug: text('slug').notNull().unique(),\n name: text('name').notNull().default(''),\n description: text('description').notNull().default(''),\n materials: text('materials'),\n dimensions: text('dimensions'),\n priceCents: integer('price_cents'),\n currency: text('currency').notNull(),\n category: text('category'),\n quantity: integer('quantity').notNull().default(1),\n status: productStatus('status').notNull().default('draft'),\n featured: boolean('featured').notNull().default(false),\n publishedAt: timestamp('published_at', { withTimezone: true }),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (t) => [check('commerce_products_quantity_check', sql`${t.quantity} >= 0`)],\n);\n\nexport const productImages = pgTable('commerce_product_images', {\n id: uuid('id').primaryKey().defaultRandom(),\n productId: uuid('product_id')\n .notNull()\n .references(() => products.id, { onDelete: 'cascade' }),\n url: text('url').notNull(),\n alt: text('alt').notNull().default(''),\n storageKey: text('storage_key'),\n position: integer('position').notNull().default(0),\n});\n\nexport const holdStatus = pgEnum('commerce_hold_status', [\n 'active',\n 'released',\n 'committed',\n]);\n\nexport const inventoryHolds = pgTable(\n 'commerce_inventory_holds',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n productId: uuid('product_id')\n .notNull()\n .references(() => products.id, { onDelete: 'cascade' }),\n quantity: integer('quantity').notNull(),\n reference: text('reference').notNull(),\n expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),\n status: holdStatus('status').notNull().default('active'),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (t) => [\n check('commerce_inventory_holds_quantity_check', sql`${t.quantity} > 0`),\n index('commerce_inventory_holds_reference_idx').on(t.reference),\n index('commerce_inventory_holds_product_status_idx').on(\n t.productId,\n t.status,\n ),\n ],\n);\n\nexport const orderStatus = pgEnum('commerce_order_status', [\n 'pending',\n 'paid',\n 'shipped',\n 'cancelled',\n 'expired',\n 'needs_attention',\n 'refunded',\n]);\n\nexport const orders = pgTable('commerce_orders', {\n id: uuid('id').primaryKey(),\n status: orderStatus('status').notNull().default('pending'),\n email: text('email'),\n subtotalCents: integer('subtotal_cents').notNull(),\n discountCents: integer('discount_cents').notNull().default(0),\n discountCode: text('discount_code'),\n shippingCents: integer('shipping_cents').notNull().default(0),\n shippingLabel: text('shipping_label'),\n currency: text('currency').notNull(),\n checkoutSessionId: text('checkout_session_id').unique(),\n shippingAddress: jsonb('shipping_address'),\n fulfillmentCarrier: text('fulfillment_carrier'),\n fulfillmentTrackingNumber: text('fulfillment_tracking_number'),\n fulfillmentTrackingUrl: text('fulfillment_tracking_url'),\n shippedAt: timestamp('shipped_at', { withTimezone: true }),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n});\n\nexport const orderItems = pgTable('commerce_order_items', {\n id: uuid('id').primaryKey().defaultRandom(),\n orderId: uuid('order_id')\n .notNull()\n .references(() => orders.id, { onDelete: 'cascade' }),\n productId: uuid('product_id').notNull(),\n name: text('name').notNull(),\n priceCents: integer('price_cents').notNull(),\n quantity: integer('quantity').notNull(),\n});\n\nexport const orderRefunds = pgTable(\n 'commerce_order_refunds',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n orderId: uuid('order_id')\n .notNull()\n .references(() => orders.id, { onDelete: 'cascade' }),\n amountCents: integer('amount_cents').notNull(),\n currency: text('currency').notNull(),\n reason: text('reason'),\n restocked: boolean('restocked').notNull().default(false),\n source: text('source', { enum: ['admin', 'gateway'] }).notNull(),\n gatewayRefundId: text('gateway_refund_id').unique(),\n idempotencyKey: text('idempotency_key'),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (t) => [\n check('commerce_order_refunds_amount_check', sql`${t.amountCents} > 0`),\n index('commerce_order_refunds_order_idx').on(t.orderId),\n unique('commerce_order_refunds_idempotency_unique').on(\n t.orderId,\n t.idempotencyKey,\n ),\n ],\n);\n\nexport const stripeEvents = pgTable('commerce_stripe_events', {\n id: text('id').primaryKey(),\n type: text('type').notNull(),\n processedAt: timestamp('processed_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n});\n\nexport const shippingRates = pgTable(\n 'commerce_shipping_rates',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n label: text('label').notNull().unique(),\n amountCents: integer('amount_cents').notNull(),\n currency: text('currency').notNull(),\n minDays: integer('min_days'),\n maxDays: integer('max_days'),\n active: boolean('active').notNull().default(true),\n sort: integer('sort').notNull().default(0),\n },\n (t) => [\n check('commerce_shipping_rates_amount_check', sql`${t.amountCents} >= 0`),\n ],\n);\n\nexport const discounts = pgTable(\n 'commerce_discounts',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n code: text('code').notNull().unique(),\n type: text('type', { enum: ['percent', 'fixed'] }).notNull(),\n percentOff: integer('percent_off'),\n amountOffCents: integer('amount_off_cents'),\n currency: text('currency'),\n minSubtotalCents: integer('min_subtotal_cents'),\n startsAt: timestamp('starts_at', { withTimezone: true }),\n endsAt: timestamp('ends_at', { withTimezone: true }),\n maxRedemptions: integer('max_redemptions'),\n active: boolean('active').notNull().default(true),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (t) => [\n check('commerce_discounts_code_check', sql`${t.code} = upper(${t.code})`),\n check(\n 'commerce_discounts_percent_check',\n sql`${t.percentOff} is null or (${t.percentOff} between 1 and 100)`,\n ),\n check(\n 'commerce_discounts_amount_check',\n sql`${t.amountOffCents} is null or ${t.amountOffCents} > 0`,\n ),\n check(\n 'commerce_discounts_max_redemptions_check',\n sql`${t.maxRedemptions} is null or ${t.maxRedemptions} > 0`,\n ),\n check(\n 'commerce_discounts_type_fields_check',\n sql`(${t.type} = 'percent' and ${t.percentOff} is not null and ${t.amountOffCents} is null and ${t.currency} is null) or (${t.type} = 'fixed' and ${t.percentOff} is null and ${t.amountOffCents} is not null and ${t.currency} is not null)`,\n ),\n ],\n);\n\nexport const discountRedemptions = pgTable(\n 'commerce_discount_redemptions',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n discountId: uuid('discount_id')\n .notNull()\n .references(() => discounts.id, { onDelete: 'cascade' }),\n orderId: uuid('order_id')\n .notNull()\n .unique()\n .references(() => orders.id, { onDelete: 'cascade' }),\n status: text('status', { enum: ['reserved', 'committed', 'released'] })\n .notNull()\n .default('reserved'),\n amountCents: integer('amount_cents').notNull(),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (t) => [\n check(\n 'commerce_discount_redemptions_amount_check',\n sql`${t.amountCents} > 0`,\n ),\n check(\n 'commerce_discount_redemptions_status_check',\n sql`${t.status} in ('reserved', 'committed', 'released')`,\n ),\n index('commerce_discount_redemptions_discount_status_idx').on(\n t.discountId,\n t.status,\n ),\n ],\n);\n\nexport const commerceSchema = {\n products,\n productImages,\n productStatus,\n inventoryHolds,\n holdStatus,\n orders,\n orderItems,\n orderStatus,\n orderRefunds,\n stripeEvents,\n shippingRates,\n discounts,\n discountRedemptions,\n};\nexport type CommerceSchema = typeof commerceSchema;\n","import type { ShippingRate } from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport { listAllRates, replaceRates } from './repo';\n\nexport async function listShippingRates(\n db: CommerceDb,\n): Promise<ShippingRate[]> {\n return (await listAllRates(db)).map((r) => ({\n label: r.label,\n amountCents: r.amountCents,\n currency: r.currency,\n minDays: r.minDays,\n maxDays: r.maxDays,\n active: r.active,\n sort: r.sort,\n }));\n}\n\nexport async function setShippingRates(\n db: CommerceDb,\n rates: ShippingRate[],\n): Promise<ShippingRate[]> {\n await db.transaction((tx) => replaceRates(tx, rates));\n return listShippingRates(db);\n}\n","import {\n and,\n asc,\n desc,\n eq,\n gt,\n ilike,\n inArray,\n lt,\n ne,\n or,\n sql,\n type SQL,\n} from 'drizzle-orm';\nimport type { AdminProductCounts } from '@develemit/commerce-contracts';\nimport {\n inventoryHolds,\n orderItems,\n productImages,\n products,\n} from '../db/schema';\nimport type { CommerceExecutor } from '../db/types';\n\nexport type ProductRow = typeof products.$inferSelect;\nexport type ImageRow = typeof productImages.$inferSelect;\nexport interface ProductRecord {\n row: ProductRow;\n images: ImageRow[];\n}\n\nasync function withImages(\n db: CommerceExecutor,\n rows: ProductRow[],\n): Promise<ProductRecord[]> {\n if (rows.length === 0) return [];\n const images = await db\n .select()\n .from(productImages)\n .where(\n inArray(\n productImages.productId,\n rows.map((r) => r.id),\n ),\n )\n .orderBy(asc(productImages.position), asc(productImages.id));\n return rows.map((row) => ({\n row,\n images: images.filter((i) => i.productId === row.id),\n }));\n}\n\nexport interface ProductPageFilter {\n status?: ProductRow['status'];\n excludeArchived?: boolean;\n incomplete?: boolean;\n stock?: 'in' | 'sold';\n q?: string;\n after?: { createdAt: string; slug: string };\n limit: number;\n}\n\nexport interface ProductPageRecord extends ProductRecord {\n createdAtKey: string;\n}\n\nfunction escapeLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (c) => `\\\\${c}`);\n}\n\nconst incompleteDraft = sql`(${products.status} = 'draft' and (${products.name} = '' or ${products.priceCents} is null or ${products.category} is null))`;\n\nfunction pageConditions(filter: ProductPageFilter): SQL | undefined {\n const parts: (SQL | undefined)[] = [];\n if (filter.status) parts.push(eq(products.status, filter.status));\n if (filter.excludeArchived) parts.push(ne(products.status, 'archived'));\n if (filter.incomplete) parts.push(incompleteDraft);\n if (filter.stock === 'in') parts.push(gt(products.quantity, 0));\n if (filter.stock === 'sold') parts.push(eq(products.quantity, 0));\n if (filter.q) parts.push(ilike(products.name, `%${escapeLike(filter.q)}%`));\n if (filter.after) {\n const createdAt = sql`${filter.after.createdAt}::timestamptz`;\n parts.push(\n or(\n lt(products.createdAt, createdAt),\n and(\n eq(products.createdAt, createdAt),\n lt(products.slug, filter.after.slug),\n ),\n ),\n );\n }\n return and(...parts);\n}\n\nexport async function selectProductPage(\n db: CommerceExecutor,\n filter: ProductPageFilter,\n): Promise<ProductPageRecord[]> {\n const selected = await db\n .select({\n product: products,\n createdAtKey: sql<string>`${products.createdAt}::text`,\n })\n .from(products)\n .where(pageConditions(filter))\n .orderBy(desc(products.createdAt), desc(products.slug))\n .limit(filter.limit);\n const records = await withImages(\n db,\n selected.map((s) => s.product),\n );\n return records.map((r, i) => ({\n ...r,\n createdAtKey: selected[i].createdAtKey,\n }));\n}\n\nexport async function findProductById(\n db: CommerceExecutor,\n id: string,\n): Promise<ProductRecord | undefined> {\n const rows = await db.select().from(products).where(eq(products.id, id));\n return (await withImages(db, rows))[0];\n}\n\nexport async function slugsStartingWith(\n db: CommerceExecutor,\n base: string,\n): Promise<Set<string>> {\n const rows = await db\n .select({ slug: products.slug })\n .from(products)\n .where(sql`${products.slug} like ${`${base}%`}`);\n return new Set(rows.map((r) => r.slug));\n}\n\nexport async function insertProduct(\n db: CommerceExecutor,\n values: typeof products.$inferInsert,\n): Promise<string> {\n const [row] = await db\n .insert(products)\n .values(values)\n .returning({ id: products.id });\n return row.id;\n}\n\nexport async function updateProduct(\n db: CommerceExecutor,\n id: string,\n set: Partial<typeof products.$inferInsert>,\n): Promise<void> {\n await db\n .update(products)\n .set({ ...set, updatedAt: sql`now()` })\n .where(eq(products.id, id));\n}\n\nexport async function nextImagePosition(\n db: CommerceExecutor,\n productId: string,\n): Promise<number> {\n const [row] = await db\n .select({\n next: sql<number>`coalesce(max(${productImages.position}) + 1, 0)::int`,\n })\n .from(productImages)\n .where(eq(productImages.productId, productId));\n return row.next;\n}\n\nexport async function insertImage(\n db: CommerceExecutor,\n values: typeof productImages.$inferInsert,\n): Promise<ImageRow> {\n const [row] = await db.insert(productImages).values(values).returning();\n return row;\n}\n\nexport async function deleteImageRow(\n db: CommerceExecutor,\n id: string,\n): Promise<void> {\n await db.delete(productImages).where(eq(productImages.id, id));\n}\n\nexport async function setImagePosition(\n db: CommerceExecutor,\n id: string,\n position: number,\n): Promise<void> {\n await db\n .update(productImages)\n .set({ position })\n .where(eq(productImages.id, id));\n}\n\nexport async function setImageAlt(\n db: CommerceExecutor,\n id: string,\n alt: string,\n): Promise<void> {\n await db.update(productImages).set({ alt }).where(eq(productImages.id, id));\n}\n\nexport async function selectProductCounts(\n db: CommerceExecutor,\n): Promise<AdminProductCounts> {\n const [row] = await db\n .select({\n all: sql<number>`count(*) filter (where ${products.status} <> 'archived')::int`,\n live: sql<number>`count(*) filter (where ${products.status} = 'active' and ${products.quantity} > 0)::int`,\n draft: sql<number>`count(*) filter (where ${products.status} = 'draft')::int`,\n sold: sql<number>`count(*) filter (where ${products.status} = 'active' and ${products.quantity} = 0)::int`,\n archived: sql<number>`count(*) filter (where ${products.status} = 'archived')::int`,\n incompleteDrafts: sql<number>`count(*) filter (where ${products.status} = 'draft' and (${products.name} = '' or ${products.priceCents} is null or ${products.category} is null))::int`,\n })\n .from(products);\n return row;\n}\n\nexport async function productHasOrderLines(\n db: CommerceExecutor,\n productId: string,\n): Promise<boolean> {\n const rows = await db\n .select({ id: orderItems.id })\n .from(orderItems)\n .where(eq(orderItems.productId, productId))\n .limit(1);\n return rows.length > 0;\n}\n\nexport async function deleteProductRows(\n db: CommerceExecutor,\n id: string,\n): Promise<void> {\n await db.delete(inventoryHolds).where(eq(inventoryHolds.productId, id));\n await db.delete(productImages).where(eq(productImages.productId, id));\n await db.delete(products).where(eq(products.id, id));\n}\n","import type {\n AdminProduct,\n AdminProductListQuery,\n AdminProductPage,\n AdminProductInput,\n AdminProductPatch,\n AdminProductCounts,\n} from '@develemit/commerce-contracts';\nimport {\n adminProductInputSchema,\n adminProductListQuerySchema,\n adminProductPatchSchema,\n} from '@develemit/commerce-contracts';\nimport { randomUUID } from 'node:crypto';\nimport { ProductNotFoundError } from '../catalog/types';\nimport { decodeCursor, encodeCursor, PG_TIMESTAMP } from '../shared/cursor';\nimport type { products } from '../db/schema';\nimport type { CommerceDb } from '../db/types';\nimport {\n findProductById,\n insertProduct,\n selectProductCounts,\n selectProductPage,\n slugsStartingWith,\n updateProduct,\n type ProductRecord,\n} from './repo';\nimport { assertPublishable, temporarySlug } from './publish';\nimport { slugify, uniqueSlug } from './slug';\nimport { ProductIncompleteError } from './types';\n\nexport function toAdminProduct({ row, images }: ProductRecord): AdminProduct {\n return {\n id: row.id,\n slug: row.slug,\n name: row.name,\n description: row.description,\n materials: row.materials,\n dimensions: row.dimensions,\n priceCents: row.priceCents,\n currency: row.currency,\n category: row.category,\n quantity: row.quantity,\n status: row.status,\n featured: row.featured,\n everPublished: row.publishedAt !== null,\n images: images.map((i) => ({\n id: i.id,\n url: i.url,\n alt: i.alt,\n position: i.position,\n })),\n };\n}\n\nexport async function requireProduct(\n db: CommerceDb,\n id: string,\n): Promise<ProductRecord> {\n const found = await findProductById(db, id);\n if (!found) throw new ProductNotFoundError(id);\n return found;\n}\n\nfunction decodeProductCursor(raw: string): { createdAt: string; slug: string } {\n const { at, key } = decodeCursor(raw, ({ at }) => PG_TIMESTAMP.test(at));\n return { createdAt: at, slug: key };\n}\n\nexport async function listAdminProducts(\n db: CommerceDb,\n rawQuery: AdminProductListQuery = {},\n): Promise<AdminProductPage> {\n const query = adminProductListQuerySchema.parse(rawQuery);\n const found = await selectProductPage(db, {\n status: query.status,\n excludeArchived: query.excludeArchived === 'true',\n incomplete: query.incomplete === 'true',\n stock: query.stock,\n q: query.q,\n after: query.cursor ? decodeProductCursor(query.cursor) : undefined,\n limit: query.limit + 1,\n });\n const page = found.slice(0, query.limit);\n const last = page[page.length - 1];\n return {\n items: page.map(toAdminProduct),\n nextCursor:\n found.length > query.limit && last\n ? encodeCursor({ at: last.createdAtKey, key: last.row.slug })\n : null,\n };\n}\n\nexport async function getAdminProduct(\n db: CommerceDb,\n id: string,\n): Promise<AdminProduct> {\n return toAdminProduct(await requireProduct(db, id));\n}\n\nexport async function createAdminProduct(\n db: CommerceDb,\n currency: string,\n input: AdminProductInput,\n { requirePhotoToPublish = true }: { requirePhotoToPublish?: boolean } = {},\n): Promise<AdminProduct> {\n const data = adminProductInputSchema.parse(input);\n if (data.status === 'active' && requirePhotoToPublish) {\n throw new ProductIncompleteError(['images']);\n }\n const newId = randomUUID();\n const base = data.name ? slugify(data.name) : temporarySlug(newId);\n const id = await db.transaction(async (tx) => {\n const slug = uniqueSlug(base, await slugsStartingWith(tx, base));\n return insertProduct(tx, {\n ...data,\n publishedAt: data.status === 'active' ? new Date() : undefined,\n id: newId,\n slug,\n currency: data.currency ?? currency,\n });\n });\n return getAdminProduct(db, id);\n}\n\nexport async function patchAdminProduct(\n db: CommerceDb,\n id: string,\n input: AdminProductPatch,\n { requirePhotoToPublish = true }: { requirePhotoToPublish?: boolean } = {},\n): Promise<AdminProduct> {\n const { sold, ...fields } = adminProductPatchSchema.parse(input);\n const record = await requireProduct(db, id);\n if (fields.status === 'active') {\n const photoNeeded = requirePhotoToPublish && record.row.status !== 'active';\n assertPublishable(record, fields, photoNeeded);\n }\n const set: Partial<typeof products.$inferInsert> = sold\n ? { ...fields, quantity: 0 }\n : { ...fields };\n if (fields.status === 'active' && !record.row.publishedAt) {\n set.publishedAt = new Date();\n }\n const renames = fields.name && record.row.slug === temporarySlug(id);\n await db.transaction(async (tx) => {\n if (renames) {\n const base = slugify(fields.name!);\n set.slug = uniqueSlug(base, await slugsStartingWith(tx, base));\n }\n if (Object.keys(set).length > 0) await updateProduct(tx, id, set);\n });\n return getAdminProduct(db, id);\n}\n\nexport async function getAdminProductCounts(\n db: CommerceDb,\n): Promise<AdminProductCounts> {\n return selectProductCounts(db);\n}\n","import type { ProductIncompleteField } from '@develemit/commerce-contracts';\n\nexport class ProductImageNotFoundError extends Error {\n readonly code = 'IMAGE_NOT_FOUND';\n constructor(public readonly imageId: string) {\n super(`Image not found: ${imageId}`);\n this.name = 'ProductImageNotFoundError';\n }\n}\n\nexport class InvalidImageOrderError extends Error {\n readonly code = 'INVALID_IMAGE_ORDER';\n constructor() {\n super('imageIds must list every image of the product exactly once');\n this.name = 'InvalidImageOrderError';\n }\n}\n\nexport class ProductIncompleteError extends Error {\n readonly code = 'PRODUCT_INCOMPLETE';\n constructor(public readonly missing: ProductIncompleteField[]) {\n super(`Product is missing: ${missing.join(', ')}`);\n this.name = 'ProductIncompleteError';\n }\n}\n\nexport class ProductNotDeletableError extends Error {\n readonly code = 'PRODUCT_NOT_DELETABLE';\n constructor(public readonly reason: 'published' | 'has_orders') {\n super(\n reason === 'published'\n ? 'This product has been live, so it can only be archived'\n : 'This product is on an order, so it can only be archived',\n );\n this.name = 'ProductNotDeletableError';\n }\n}\n","import type { ProductIncompleteField } from '@develemit/commerce-contracts';\nimport type { ProductRecord } from './repo';\nimport { ProductIncompleteError } from './types';\n\ninterface PublishFields {\n name?: string;\n priceCents?: number | null;\n category?: string | null;\n}\n\nexport function temporarySlug(id: string): string {\n return `draft-${id.slice(0, 8)}`;\n}\n\nexport function missingForPublish(\n { row, images }: ProductRecord,\n patch: PublishFields,\n requirePhoto: boolean,\n): ProductIncompleteField[] {\n const name = patch.name ?? row.name;\n const priceCents = patch.priceCents ?? row.priceCents;\n const category = patch.category ?? row.category;\n const missing: ProductIncompleteField[] = [];\n if (!name) missing.push('name');\n if (priceCents === null) missing.push('priceCents');\n if (!category) missing.push('category');\n if (requirePhoto && images.length === 0) missing.push('images');\n return missing;\n}\n\nexport function assertPublishable(\n record: ProductRecord,\n patch: PublishFields,\n requirePhoto: boolean,\n): void {\n const missing = missingForPublish(record, patch, requirePhoto);\n if (missing.length > 0) throw new ProductIncompleteError(missing);\n}\n","export function slugify(name: string): string {\n const slug = name\n .normalize('NFKD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n return slug || 'product';\n}\n\nexport function uniqueSlug(base: string, taken: Set<string>): string {\n if (!taken.has(base)) return base;\n let n = 2;\n while (taken.has(`${base}-${n}`)) n += 1;\n return `${base}-${n}`;\n}\n","import type { CommerceDb } from '../db/types';\nimport type { FileStorage } from '../storage/types';\nimport { deleteProductRows, productHasOrderLines } from './repo';\nimport { requireProduct } from './service';\nimport { ProductNotDeletableError } from './types';\n\nexport async function deleteAdminProduct(\n db: CommerceDb,\n storage: FileStorage,\n id: string,\n): Promise<void> {\n const { row, images } = await requireProduct(db, id);\n if (row.status !== 'draft' || row.publishedAt) {\n throw new ProductNotDeletableError('published');\n }\n if (await productHasOrderLines(db, id)) {\n throw new ProductNotDeletableError('has_orders');\n }\n await db.transaction((tx) => deleteProductRows(tx, id));\n for (const image of images) {\n if (image.storageKey) await storage.delete(image.storageKey);\n }\n}\n","import { Readable } from 'node:stream';\nimport type { AdminProduct } from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport type { FileStorage } from '../storage/types';\nimport {\n deleteImageRow,\n insertImage,\n nextImagePosition,\n setImageAlt,\n setImagePosition,\n} from './repo';\nimport { getAdminProduct, requireProduct } from './service';\nimport { InvalidImageOrderError, ProductImageNotFoundError } from './types';\nimport { detectImage, imageKey } from './upload';\n\nexport async function addProductImage(\n db: CommerceDb,\n storage: FileStorage,\n productId: string,\n file: { bytes: Buffer; alt?: string },\n): Promise<AdminProduct> {\n await requireProduct(db, productId);\n const { mime, ext } = await detectImage(file.bytes);\n const stored = await storage.put(\n imageKey(productId, ext),\n Readable.from(file.bytes),\n mime,\n );\n try {\n await insertImage(db, {\n productId,\n url: stored.url,\n alt: file.alt ?? '',\n storageKey: stored.key,\n position: await nextImagePosition(db, productId),\n });\n } catch (err) {\n await storage.delete(stored.key);\n throw err;\n }\n return getAdminProduct(db, productId);\n}\n\nexport async function reorderProductImages(\n db: CommerceDb,\n productId: string,\n imageIds: string[],\n): Promise<AdminProduct> {\n const { images } = await requireProduct(db, productId);\n const existing = new Set(images.map((i) => i.id));\n if (\n imageIds.length !== existing.size ||\n new Set(imageIds).size !== imageIds.length ||\n !imageIds.every((id) => existing.has(id))\n ) {\n throw new InvalidImageOrderError();\n }\n await db.transaction(async (tx) => {\n for (const [position, id] of imageIds.entries()) {\n await setImagePosition(tx, id, position);\n }\n });\n return getAdminProduct(db, productId);\n}\n\nexport async function removeProductImage(\n db: CommerceDb,\n storage: FileStorage,\n productId: string,\n imageId: string,\n): Promise<AdminProduct> {\n const { images } = await requireProduct(db, productId);\n const image = images.find((i) => i.id === imageId);\n if (!image) throw new ProductImageNotFoundError(imageId);\n await deleteImageRow(db, imageId);\n if (image.storageKey) await storage.delete(image.storageKey);\n return getAdminProduct(db, productId);\n}\n\nexport async function updateProductImageAlt(\n db: CommerceDb,\n productId: string,\n imageId: string,\n alt: string,\n): Promise<AdminProduct> {\n const { images } = await requireProduct(db, productId);\n if (!images.some((i) => i.id === imageId)) {\n throw new ProductImageNotFoundError(imageId);\n }\n await setImageAlt(db, imageId, alt);\n return getAdminProduct(db, productId);\n}\n","import { randomUUID } from 'node:crypto';\nimport { fileTypeFromBuffer } from 'file-type';\n\nexport const MAX_IMAGE_BYTES = 8 * 1024 * 1024;\n\nconst EXTENSIONS: Record<string, string> = {\n 'image/jpeg': 'jpg',\n 'image/png': 'png',\n 'image/webp': 'webp',\n 'image/heic': 'heic',\n};\n\nexport class InvalidImageError extends Error {\n readonly code = 'INVALID_IMAGE';\n constructor(message: string) {\n super(message);\n this.name = 'InvalidImageError';\n }\n}\n\nexport async function detectImage(\n bytes: Buffer,\n): Promise<{ mime: string; ext: string }> {\n const type = await fileTypeFromBuffer(bytes);\n const ext = type && EXTENSIONS[type.mime];\n if (!type || !ext) {\n throw new InvalidImageError('File must be a jpeg, png, webp or heic image');\n }\n return { mime: type.mime, ext };\n}\n\nexport function imageKey(productId: string, ext: string): string {\n return `products/${productId}/${randomUUID()}.${ext}`;\n}\n","import {\n categorySchema,\n productCardPageSchema,\n productListQuerySchema,\n productSchema,\n} from '@develemit/commerce-contracts';\nimport type { FastifyPluginAsync } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport { InvalidCursorError } from '../shared/cursor';\nimport { addCatalogCaching, type CatalogCacheOptions } from './cache-hooks';\nimport { getProduct, listCategories, listProducts } from './service';\nimport { ProductNotFoundError, type CategoryLabels } from './types';\n\nexport interface CatalogPluginOptions {\n db: CommerceDb;\n categoryLabels?: CategoryLabels;\n catalogCache?: CatalogCacheOptions;\n}\n\nexport const catalogPlugin: FastifyPluginAsync<CatalogPluginOptions> = async (\n app,\n { db, categoryLabels, catalogCache },\n) => {\n addCatalogCaching(app, catalogCache);\n app.setErrorHandler((err, _req, reply) => {\n if (err instanceof ProductNotFoundError) {\n return reply.code(404).send({ error: err.code, message: err.message });\n }\n if (err instanceof InvalidCursorError) {\n return reply.code(400).send({ error: err.code, message: err.message });\n }\n return reply.send(err);\n });\n\n app.get('/products', async (req, reply) => {\n const raw = req.query as Record<string, string | undefined>;\n const parsed = productListQuerySchema.safeParse({\n category: raw.category || undefined,\n featured:\n raw.featured === undefined ? undefined : raw.featured === 'true',\n limit: raw.limit,\n cursor: raw.cursor || undefined,\n });\n if (!parsed.success) {\n return reply.code(400).send({ error: 'INVALID_QUERY' });\n }\n return productCardPageSchema.parse(await listProducts(db, parsed.data));\n });\n\n app.get<{ Params: { slug: string } }>('/products/:slug', async (req) => {\n return productSchema.parse(await getProduct(db, req.params.slug));\n });\n\n app.get('/categories', async () => {\n const categories = await listCategories(db, categoryLabels);\n return categories.map((c) => categorySchema.parse(c));\n });\n};\n","import { createHash } from 'node:crypto';\nimport type { FastifyInstance } from 'fastify';\n\nexport interface CatalogCacheOptions {\n cacheControl?: string | false;\n}\n\nexport const DEFAULT_CATALOG_CACHE_CONTROL =\n 'public, max-age=30, stale-while-revalidate=300';\n\nfunction etagOf(body: string): string {\n return `\"${createHash('sha1').update(body).digest('base64url')}\"`;\n}\n\nexport function matchesIfNoneMatch(\n header: string | undefined,\n etag: string,\n): boolean {\n if (!header) return false;\n if (header.trim() === '*') return true;\n return header\n .split(',')\n .map((t) => t.trim().replace(/^W\\//, ''))\n .includes(etag);\n}\n\nexport function addCatalogCaching(\n app: FastifyInstance,\n { cacheControl = DEFAULT_CATALOG_CACHE_CONTROL }: CatalogCacheOptions = {},\n): void {\n app.addHook('onSend', async (req, reply, payload) => {\n if (reply.statusCode >= 400) {\n reply.header('cache-control', 'no-store');\n return payload;\n }\n if (reply.statusCode !== 200 || typeof payload !== 'string') {\n return payload;\n }\n const etag = etagOf(payload);\n reply.header('etag', etag);\n if (cacheControl !== false) reply.header('cache-control', cacheControl);\n if (matchesIfNoneMatch(req.headers['if-none-match'], etag)) {\n reply.code(304);\n reply.removeHeader('content-length');\n reply.removeHeader('content-type');\n return '';\n }\n return payload;\n });\n}\n","import { and, asc, eq, gt, inArray, lt, sql } from 'drizzle-orm';\nimport type { CommerceExecutor } from '../db/types';\nimport { inventoryHolds, products } from '../db/schema';\nimport type { StockRequest } from './types';\n\nexport async function lockProductQuantities(\n tx: CommerceExecutor,\n productIds: string[],\n): Promise<Map<string, number>> {\n const rows = await tx\n .select({ id: products.id, quantity: products.quantity })\n .from(products)\n .where(inArray(products.id, productIds))\n .orderBy(asc(products.id))\n .for('update');\n return new Map(rows.map((r) => [r.id, r.quantity]));\n}\n\nexport async function activeHeldQuantities(\n db: CommerceExecutor,\n productIds: string[],\n): Promise<Map<string, number>> {\n if (productIds.length === 0) return new Map();\n const rows = await db\n .select({\n productId: inventoryHolds.productId,\n held: sql<number>`sum(${inventoryHolds.quantity})::int`,\n })\n .from(inventoryHolds)\n .where(\n and(\n inArray(inventoryHolds.productId, productIds),\n eq(inventoryHolds.status, 'active'),\n gt(inventoryHolds.expiresAt, sql`now()`),\n ),\n )\n .groupBy(inventoryHolds.productId);\n return new Map(rows.map((r) => [r.productId, r.held]));\n}\n\nexport async function productQuantities(\n db: CommerceExecutor,\n productIds: string[],\n): Promise<Map<string, number>> {\n if (productIds.length === 0) return new Map();\n const rows = await db\n .select({ id: products.id, quantity: products.quantity })\n .from(products)\n .where(inArray(products.id, productIds));\n return new Map(rows.map((r) => [r.id, r.quantity]));\n}\n\nexport async function insertHolds(\n tx: CommerceExecutor,\n reference: string,\n items: StockRequest[],\n expiresAt: Date,\n): Promise<void> {\n await tx.insert(inventoryHolds).values(\n items.map((item) => ({\n productId: item.productId,\n quantity: item.quantity,\n reference,\n expiresAt,\n })),\n );\n}\n\nexport async function releaseHolds(\n db: CommerceExecutor,\n reference: string,\n): Promise<void> {\n await db\n .update(inventoryHolds)\n .set({ status: 'released' })\n .where(\n and(\n eq(inventoryHolds.reference, reference),\n eq(inventoryHolds.status, 'active'),\n ),\n );\n}\n\nexport interface UncommittedHold extends StockRequest {\n lapsed: boolean;\n}\n\nexport async function uncommittedHolds(\n tx: CommerceExecutor,\n reference: string,\n): Promise<UncommittedHold[]> {\n return tx\n .select({\n productId: inventoryHolds.productId,\n quantity: inventoryHolds.quantity,\n lapsed: sql<boolean>`(${inventoryHolds.status} = 'released' or ${inventoryHolds.expiresAt} <= now())`,\n })\n .from(inventoryHolds)\n .where(\n and(\n eq(inventoryHolds.reference, reference),\n inArray(inventoryHolds.status, ['active', 'released']),\n ),\n );\n}\n\nexport async function markHoldsCommitted(\n tx: CommerceExecutor,\n reference: string,\n): Promise<void> {\n await tx\n .update(inventoryHolds)\n .set({ status: 'committed' })\n .where(\n and(\n eq(inventoryHolds.reference, reference),\n inArray(inventoryHolds.status, ['active', 'released']),\n ),\n );\n}\n\nexport async function extendActiveHolds(\n db: CommerceExecutor,\n reference: string,\n expiresAt: Date,\n): Promise<void> {\n await db\n .update(inventoryHolds)\n .set({ expiresAt })\n .where(\n and(\n eq(inventoryHolds.reference, reference),\n eq(inventoryHolds.status, 'active'),\n gt(inventoryHolds.expiresAt, sql`now()`),\n ),\n );\n}\n\nexport async function decrementQuantity(\n tx: CommerceExecutor,\n productId: string,\n quantity: number,\n): Promise<void> {\n await tx\n .update(products)\n .set({\n quantity: sql`greatest(${products.quantity} - ${quantity}, 0)`,\n updatedAt: sql`now()`,\n })\n .where(eq(products.id, productId));\n}\n\nexport async function incrementQuantity(\n tx: CommerceExecutor,\n productId: string,\n quantity: number,\n): Promise<void> {\n await tx\n .update(products)\n .set({\n quantity: sql`${products.quantity} + ${quantity}`,\n updatedAt: sql`now()`,\n })\n .where(eq(products.id, productId));\n}\n\nexport async function releaseExpiredHolds(\n db: CommerceExecutor,\n): Promise<string[]> {\n const released = await db\n .update(inventoryHolds)\n .set({ status: 'released' })\n .where(\n and(\n eq(inventoryHolds.status, 'active'),\n lt(inventoryHolds.expiresAt, sql`now()`),\n ),\n )\n .returning({ reference: inventoryHolds.reference });\n return released.map((r) => r.reference);\n}\n","export interface StockRequest {\n productId: string;\n quantity: number;\n}\n\nexport interface ReserveInput {\n reference: string;\n items: StockRequest[];\n expiresAt: Date;\n}\n\nexport class InsufficientStockError extends Error {\n readonly code = 'INSUFFICIENT_STOCK';\n constructor(public readonly productIds: string[]) {\n super(`Not enough stock for: ${productIds.join(', ')}`);\n this.name = 'InsufficientStockError';\n }\n}\n","import type { CommerceExecutor } from '../db/types';\nimport {\n activeHeldQuantities,\n decrementQuantity,\n extendActiveHolds,\n incrementQuantity,\n insertHolds,\n lockProductQuantities,\n productQuantities,\n releaseExpiredHolds,\n releaseHolds,\n markHoldsCommitted,\n uncommittedHolds,\n} from './repo';\nimport {\n InsufficientStockError,\n type ReserveInput,\n type StockRequest,\n} from './types';\n\nfunction mergeRequests(items: StockRequest[]): StockRequest[] {\n const merged = new Map<string, number>();\n for (const item of items) {\n merged.set(\n item.productId,\n (merged.get(item.productId) ?? 0) + item.quantity,\n );\n }\n return [...merged].map(([productId, quantity]) => ({ productId, quantity }));\n}\n\nfunction freeOf(quantity: number, held: number): number {\n return Math.max(quantity - held, 0);\n}\n\nexport async function freeQuantity(\n db: CommerceExecutor,\n productIds: string[],\n): Promise<Map<string, number>> {\n const [quantities, held] = await Promise.all([\n productQuantities(db, productIds),\n activeHeldQuantities(db, productIds),\n ]);\n return new Map(\n [...quantities].map(([id, qty]) => [id, freeOf(qty, held.get(id) ?? 0)]),\n );\n}\n\n// Locking the product rows serializes concurrent reservations of the same\n// item, so the held total read afterwards includes every committed rival hold.\nexport async function reserve(\n db: CommerceExecutor,\n { reference, items, expiresAt }: ReserveInput,\n): Promise<void> {\n const requests = mergeRequests(items);\n const ids = requests.map((r) => r.productId);\n await db.transaction(async (tx) => {\n const quantities = await lockProductQuantities(tx, ids);\n const held = await activeHeldQuantities(tx, ids);\n const short = requests\n .filter((r) => {\n const qty = quantities.get(r.productId);\n if (qty === undefined) return true;\n return freeOf(qty, held.get(r.productId) ?? 0) < r.quantity;\n })\n .map((r) => r.productId);\n if (short.length > 0) throw new InsufficientStockError(short);\n await insertHolds(tx, reference, requests, expiresAt);\n });\n}\n\nexport async function release(\n db: CommerceExecutor,\n reference: string,\n): Promise<void> {\n await releaseHolds(db, reference);\n}\n\nexport async function releaseExpired(db: CommerceExecutor): Promise<number> {\n return (await releaseExpiredHolds(db)).length;\n}\n\nexport async function releaseExpiredReferences(\n db: CommerceExecutor,\n): Promise<string[]> {\n return [...new Set(await releaseExpiredHolds(db))];\n}\n\nexport type CommitResult = 'committed' | 'unavailable';\n\nexport type CommitOutcome =\n | { result: 'committed' }\n | { result: 'unavailable'; shortProductIds: string[] };\n\n// A payment can complete after its hold lapsed, by which time another buyer\n// may have taken the stock. Lapsed holds are re-checked against what is free\n// now; if any is short nothing is decremented and the caller must escalate.\nexport async function commitWithin(\n tx: CommerceExecutor,\n reference: string,\n): Promise<CommitOutcome> {\n const holds = await uncommittedHolds(tx, reference);\n if (holds.length === 0) return { result: 'committed' };\n const quantities = await lockProductQuantities(\n tx,\n holds.map((h) => h.productId),\n );\n const lapsed = holds.filter((h) => h.lapsed);\n const held = await activeHeldQuantities(\n tx,\n lapsed.map((h) => h.productId),\n );\n const shortProductIds = lapsed\n .filter(\n (h) =>\n freeOf(quantities.get(h.productId) ?? 0, held.get(h.productId) ?? 0) <\n h.quantity,\n )\n .map((h) => h.productId);\n if (shortProductIds.length > 0) {\n await releaseHolds(tx, reference);\n return { result: 'unavailable', shortProductIds };\n }\n for (const hold of holds) {\n await decrementQuantity(tx, hold.productId, hold.quantity);\n }\n await markHoldsCommitted(tx, reference);\n return { result: 'committed' };\n}\n\nexport async function commit(\n db: CommerceExecutor,\n reference: string,\n): Promise<CommitResult> {\n return (await db.transaction((tx) => commitWithin(tx, reference))).result;\n}\n\nexport async function extendHolds(\n db: CommerceExecutor,\n reference: string,\n expiresAt: Date,\n): Promise<void> {\n await extendActiveHolds(db, reference, expiresAt);\n}\n\nexport async function restock(\n tx: CommerceExecutor,\n items: StockRequest[],\n): Promise<void> {\n const requests = mergeRequests(items);\n if (requests.length === 0) return;\n await lockProductQuantities(\n tx,\n requests.map((r) => r.productId),\n );\n for (const r of requests) {\n await incrementQuantity(tx, r.productId, r.quantity);\n }\n}\n\nexport { InsufficientStockError } from './types';\nexport type { ReserveInput, StockRequest } from './types';\n","import { and, asc, eq, gt, inArray, isNotNull, or, sql } from 'drizzle-orm';\nimport type { CommerceDb } from '../db/types';\nimport { productImages, products } from '../db/schema';\nimport type { CatalogFilter } from './types';\n\nconst isListed = and(\n eq(products.status, 'active'),\n isNotNull(products.priceCents),\n isNotNull(products.category),\n)!;\n\nexport type ProductRow = typeof products.$inferSelect;\nexport type ProductImageRow = typeof productImages.$inferSelect;\nexport interface ProductWithImages {\n row: ProductRow;\n images: ProductImageRow[];\n createdAtKey?: string;\n}\n\nasync function attachImages(\n db: CommerceDb,\n rows: ProductRow[],\n firstOnly = false,\n): Promise<ProductWithImages[]> {\n if (rows.length === 0) return [];\n const where = inArray(\n productImages.productId,\n rows.map((r) => r.id),\n );\n const images = firstOnly\n ? await db\n .selectDistinctOn([productImages.productId])\n .from(productImages)\n .where(where)\n .orderBy(productImages.productId, asc(productImages.position))\n : await db\n .select()\n .from(productImages)\n .where(where)\n .orderBy(asc(productImages.position));\n return rows.map((row) => ({\n row,\n images: images.filter((i) => i.productId === row.id),\n }));\n}\n\nexport interface ProductPageFilter extends CatalogFilter {\n after?: { createdAt: string; slug: string };\n}\n\nexport async function listActiveProducts(\n db: CommerceDb,\n filter: ProductPageFilter,\n): Promise<ProductWithImages[]> {\n const conditions = [isListed];\n if (filter.category) conditions.push(eq(products.category, filter.category));\n if (filter.featured !== undefined)\n conditions.push(eq(products.featured, filter.featured));\n if (filter.after) {\n const createdAt = sql`${filter.after.createdAt}::timestamptz`;\n conditions.push(\n or(\n gt(products.createdAt, createdAt),\n and(\n eq(products.createdAt, createdAt),\n gt(products.slug, filter.after.slug),\n ),\n )!,\n );\n }\n const selected = await db\n .select({\n product: products,\n createdAtKey: sql<string>`${products.createdAt}::text`,\n })\n .from(products)\n .where(and(...conditions))\n .orderBy(asc(products.createdAt), asc(products.slug))\n .limit(filter.limit + 1);\n const withImages = await attachImages(\n db,\n selected.map((s) => s.product),\n true,\n );\n return withImages.map((p, i) => ({\n ...p,\n createdAtKey: selected[i].createdAtKey,\n }));\n}\n\nexport async function findActiveProductBySlug(\n db: CommerceDb,\n slug: string,\n): Promise<ProductWithImages | undefined> {\n const rows = await db\n .select()\n .from(products)\n .where(and(eq(products.slug, slug), isListed))\n .limit(1);\n return (await attachImages(db, rows))[0];\n}\n\nexport async function countActiveByCategory(\n db: CommerceDb,\n): Promise<{ category: string; count: number }[]> {\n const rows = await db\n .select({\n category: products.category,\n count: sql<number>`count(*)::int`,\n })\n .from(products)\n .where(isListed)\n .groupBy(products.category)\n .orderBy(asc(products.category));\n return rows.flatMap((r) =>\n r.category === null ? [] : [{ category: r.category, count: r.count }],\n );\n}\n\nexport async function findActiveProductsByIds(\n db: CommerceDb,\n ids: string[],\n): Promise<ProductRow[]> {\n if (ids.length === 0) return [];\n return db\n .select()\n .from(products)\n .where(and(inArray(products.id, ids), isListed));\n}\n","import type {\n Category,\n Product,\n ProductCard,\n ProductCardPage,\n} from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport { decodeCursor, encodeCursor, PG_TIMESTAMP } from '../shared/cursor';\nimport { freeQuantity } from '../inventory/service';\nimport {\n countActiveByCategory,\n findActiveProductBySlug,\n findActiveProductsByIds,\n listActiveProducts,\n type ProductWithImages,\n} from './repo';\nimport {\n ProductNotFoundError,\n type CatalogFilter,\n type CategoryLabels,\n type PurchasableProduct,\n} from './types';\n\nfunction completeFields(row: {\n id: string;\n priceCents: number | null;\n category: string | null;\n}) {\n if (row.priceCents === null || row.category === null) {\n throw new Error(`Active product ${row.id} is missing price or category`);\n }\n return { priceCents: row.priceCents, category: row.category };\n}\n\nfunction availabilityOf(quantity: number, free: number) {\n if (free > 0) return 'available' as const;\n return quantity > 0 ? ('reserved' as const) : ('sold' as const);\n}\n\nfunction toProduct(\n { row, images }: ProductWithImages,\n free: Map<string, number>,\n): Product {\n const freeQty = free.get(row.id) ?? row.quantity;\n return {\n id: row.id,\n slug: row.slug,\n name: row.name,\n description: row.description,\n ...(row.materials ? { materials: row.materials } : {}),\n ...(row.dimensions ? { dimensions: row.dimensions } : {}),\n ...completeFields(row),\n currency: row.currency,\n images: images.map((i) => ({ url: i.url, alt: i.alt })),\n quantity: freeQty,\n availability: availabilityOf(row.quantity, freeQty),\n featured: row.featured,\n };\n}\n\nfunction toCard(product: Product): ProductCard {\n const { description, materials, dimensions, ...card } = product;\n void [description, materials, dimensions];\n return card;\n}\n\nfunction labelFor(slug: string, labels?: CategoryLabels): string {\n const label = typeof labels === 'function' ? labels(slug) : labels?.[slug];\n return label ?? slug;\n}\n\nfunction decodeProductCursor(raw: string): { createdAt: string; slug: string } {\n const { at, key } = decodeCursor(raw, ({ at }) => PG_TIMESTAMP.test(at));\n return { createdAt: at, slug: key };\n}\n\nexport async function listProducts(\n db: CommerceDb,\n filter: CatalogFilter,\n): Promise<ProductCardPage> {\n const found = await listActiveProducts(db, {\n ...filter,\n after: filter.cursor ? decodeProductCursor(filter.cursor) : undefined,\n });\n const page = found.slice(0, filter.limit);\n const last = page[page.length - 1];\n const free = await freeQuantity(\n db,\n page.map((p) => p.row.id),\n );\n return {\n items: page.map((p) => toCard(toProduct(p, free))),\n nextCursor:\n found.length > filter.limit && last\n ? encodeCursor({ at: last.createdAtKey ?? '', key: last.row.slug })\n : null,\n };\n}\n\nexport async function getProduct(\n db: CommerceDb,\n slug: string,\n): Promise<Product> {\n const found = await findActiveProductBySlug(db, slug);\n if (!found) throw new ProductNotFoundError(slug);\n return toProduct(found, await freeQuantity(db, [found.row.id]));\n}\n\nexport async function listCategories(\n db: CommerceDb,\n labels?: CategoryLabels,\n): Promise<Category[]> {\n return (await countActiveByCategory(db)).map((c) => ({\n slug: c.category,\n label: labelFor(c.category, labels),\n count: c.count,\n }));\n}\n\nexport async function findPurchasable(\n db: CommerceDb,\n ids: string[],\n): Promise<PurchasableProduct[]> {\n const rows = await findActiveProductsByIds(db, ids);\n return rows.map((row) => ({\n id: row.id,\n name: row.name,\n priceCents: completeFields(row).priceCents,\n currency: row.currency,\n }));\n}\n","import {\n adminOrderListQuerySchema,\n adminOrderPageSchema,\n refundOrderInputSchema,\n shipOrderInputSchema,\n} from '@develemit/commerce-contracts';\nimport type { FastifyPluginAsync, FastifyRequest } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport { InsufficientStockError } from '../inventory/types';\nimport type { PaymentGateway } from '../payment/gateway';\nimport { InvalidCursorError } from '../shared/cursor';\nimport {\n AdminOrderNotFoundError,\n getAdminOrder,\n InvalidOrderIdError,\n isOrderId,\n listAdminOrders,\n} from './admin-service';\nimport {\n IdempotencyKeyRequiredError,\n RefundExceedsBalanceError,\n RefundGatewayError,\n RefundOutcomeUnknownError,\n RestockNotApplicableError,\n RestockRequiresFullRefundError,\n} from './refund-errors';\nimport { refundOrder } from './refund-service';\nimport { resolveOrder } from './resolve-service';\nimport { InvalidOrderStateError, shipOrder } from './ship-service';\nimport type { HookLogger, OrderHooks } from './types';\n\nexport interface AdminOrdersPluginOptions {\n db: CommerceDb;\n authorize(req: FastifyRequest): Promise<boolean>;\n payment: PaymentGateway;\n hooks?: OrderHooks;\n}\n\nfunction idempotencyKey(req: FastifyRequest): string {\n const key = req.headers['idempotency-key'];\n if (typeof key !== 'string' || key.length === 0 || key.length > 255) {\n throw new IdempotencyKeyRequiredError();\n }\n return key;\n}\n\nconst BAD_REQUEST = [\n InvalidCursorError,\n InvalidOrderIdError,\n IdempotencyKeyRequiredError,\n RestockRequiresFullRefundError,\n RestockNotApplicableError,\n];\n\nfunction isZodError(err: unknown): err is Error & { issues: unknown[] } {\n return err instanceof Error && err.name === 'ZodError';\n}\n\nexport const adminOrdersPlugin: FastifyPluginAsync<\n AdminOrdersPluginOptions\n> = async (app, { db, authorize, payment, hooks }) => {\n app.setErrorHandler((err, _req, reply) => {\n if (isZodError(err)) {\n return reply\n .code(400)\n .send({ error: 'INVALID_INPUT', issues: err.issues });\n }\n if (BAD_REQUEST.some((type) => err instanceof type)) {\n const { code, message } = err as Error & { code: string };\n return reply.code(400).send({ error: code, message });\n }\n if (err instanceof RefundExceedsBalanceError) {\n return reply.code(400).send({\n error: err.code,\n message: err.message,\n balanceCents: err.balanceCents,\n });\n }\n if (\n err instanceof RefundGatewayError ||\n err instanceof RefundOutcomeUnknownError\n ) {\n return reply.code(502).send({ error: err.code, message: err.message });\n }\n if (err instanceof InvalidOrderStateError) {\n return reply\n .code(409)\n .send({ error: err.code, message: err.message, status: err.status });\n }\n if (err instanceof InsufficientStockError) {\n return reply.code(409).send({\n error: 'INSUFFICIENT_STOCK',\n message: err.message,\n productIds: err.productIds,\n });\n }\n if (err instanceof AdminOrderNotFoundError) {\n return reply.code(404).send({ error: err.code, message: err.message });\n }\n return reply.send(err);\n });\n\n app.addHook('preHandler', async (req, reply) => {\n if (!(await authorize(req))) {\n return reply.code(401).send({ error: 'UNAUTHORIZED' });\n }\n });\n\n app.get('/', async (req) =>\n adminOrderPageSchema.parse(\n await listAdminOrders(db, adminOrderListQuerySchema.parse(req.query)),\n ),\n );\n\n app.get<{ Params: { id: string } }>('/:id', async (req) => {\n if (!isOrderId(req.params.id)) throw new InvalidOrderIdError();\n return getAdminOrder(db, req.params.id);\n });\n\n app.post<{ Params: { id: string } }>('/:id/ship', async (req) => {\n if (!isOrderId(req.params.id)) throw new InvalidOrderIdError();\n const log: HookLogger = req.log;\n return shipOrder(\n db,\n req.params.id,\n shipOrderInputSchema.parse(req.body ?? {}),\n hooks,\n log,\n );\n });\n\n app.post<{ Params: { id: string } }>('/:id/resolve', async (req) => {\n if (!isOrderId(req.params.id)) throw new InvalidOrderIdError();\n const log: HookLogger = req.log;\n return resolveOrder(db, req.params.id, hooks, log);\n });\n\n app.post<{ Params: { id: string } }>('/:id/refunds', async (req) => {\n if (!isOrderId(req.params.id)) throw new InvalidOrderIdError();\n const key = idempotencyKey(req);\n const log: HookLogger = req.log;\n return refundOrder(\n db,\n payment,\n req.params.id,\n refundOrderInputSchema.parse(req.body ?? {}),\n key,\n hooks,\n log,\n );\n });\n};\n","import { orderRefSearchTerm } from '@develemit/commerce-contracts';\nimport type {\n AdminOrderDetail,\n AdminOrderListQuery,\n AdminOrderPage,\n OrderRefund,\n} from '@develemit/commerce-contracts';\nimport type { CommerceExecutor } from '../db/types';\nimport {\n decodeCursor,\n encodeCursor,\n isCursorTimestamp,\n} from '../shared/cursor';\nimport { findOrderRefunds, selectOrderPage } from './admin-repo';\nimport { detailById } from './order-detail';\nimport type { RefundRow } from './refund-repo';\nimport { findOrderById, sumRefundedCents } from './repo';\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport class InvalidOrderIdError extends Error {\n readonly code = 'INVALID_ORDER_ID';\n constructor() {\n super('The order id is malformed');\n this.name = 'InvalidOrderIdError';\n }\n}\n\nexport class AdminOrderNotFoundError extends Error {\n readonly code = 'ORDER_NOT_FOUND';\n constructor(public readonly orderId: string) {\n super(`No order: ${orderId}`);\n this.name = 'AdminOrderNotFoundError';\n }\n}\n\nexport function toOrderRefund(row: RefundRow): OrderRefund {\n return {\n id: row.id,\n amountCents: row.amountCents,\n reason: row.reason,\n restocked: row.restocked,\n source: row.source,\n status:\n row.idempotencyKey && !row.gatewayRefundId ? 'pending' : 'succeeded',\n createdAt: row.createdAt.toISOString(),\n };\n}\n\nexport function isOrderId(value: string): boolean {\n return UUID.test(value);\n}\n\nfunction decodeOrderCursor(raw: string): { createdAt: string; id: string } {\n const { at, key } = decodeCursor(\n raw,\n (c) => isOrderId(c.key) && isCursorTimestamp(c.at),\n );\n return { createdAt: at, id: key };\n}\n\nexport async function listAdminOrders(\n db: CommerceExecutor,\n query: AdminOrderListQuery,\n): Promise<AdminOrderPage> {\n const rows = await selectOrderPage(db, {\n status: query.status,\n q: query.q ? orderRefSearchTerm(query.q) || undefined : undefined,\n hasPendingRefund: query.hasPendingRefund,\n after: query.cursor ? decodeOrderCursor(query.cursor) : undefined,\n limit: query.limit + 1,\n });\n const page = rows.slice(0, query.limit);\n const last = page[page.length - 1];\n return {\n items: page.map((r) => ({\n id: r.id,\n status: r.status,\n email: r.email,\n totalCents: r.totalCents,\n currency: r.currency,\n itemCount: r.itemCount,\n createdAt: r.createdAt.toISOString(),\n })),\n nextCursor:\n rows.length > query.limit && last\n ? encodeCursor({ at: last.createdAtKey, key: last.id })\n : null,\n };\n}\n\nexport async function getAdminOrder(\n db: CommerceExecutor,\n id: string,\n): Promise<AdminOrderDetail> {\n const detail = await detailById(db, id);\n const row = await findOrderById(db, id);\n if (!detail || !row) throw new AdminOrderNotFoundError(id);\n const [refunds, refundedCents] = await Promise.all([\n findOrderRefunds(db, id),\n sumRefundedCents(db, id),\n ]);\n return {\n ...detail,\n refunds: refunds.map(toOrderRefund),\n refundedCents,\n checkoutSessionId: row.checkoutSessionId,\n updatedAt: row.updatedAt.toISOString(),\n };\n}\n","import { and, desc, eq, ilike, lt, or, sql, type SQL } from 'drizzle-orm';\nimport type { CommerceExecutor } from '../db/types';\nimport { orderRefunds, orders } from '../db/schema';\nimport type { OrderRow } from './repo';\n\nexport type OrderStatus = OrderRow['status'];\n\nexport interface OrderListFilter {\n status?: OrderStatus;\n q?: string;\n hasPendingRefund?: boolean;\n after?: { createdAt: string; id: string };\n limit: number;\n}\n\nexport interface OrderListRow {\n id: string;\n status: OrderStatus;\n email: string | null;\n totalCents: number;\n currency: string;\n itemCount: number;\n createdAt: Date;\n createdAtKey: string;\n}\n\nfunction escapeLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (c) => `\\\\${c}`);\n}\n\nfunction conditions(filter: OrderListFilter): SQL | undefined {\n const parts: (SQL | undefined)[] = [];\n if (filter.status) parts.push(eq(orders.status, filter.status));\n if (filter.hasPendingRefund !== undefined) {\n const pending = sql`exists (\n select 1 from commerce_order_refunds r\n where r.order_id = commerce_orders.id and r.idempotency_key is not null\n and r.gateway_refund_id is null\n )`;\n parts.push(filter.hasPendingRefund ? pending : sql`not ${pending}`);\n }\n if (filter.q) {\n const prefix = `${escapeLike(filter.q)}%`;\n parts.push(\n or(ilike(orders.email, prefix), sql`${orders.id}::text ilike ${prefix}`),\n );\n }\n if (filter.after) {\n const { id } = filter.after;\n const createdAt = sql`${filter.after.createdAt}::timestamptz`;\n parts.push(\n or(\n lt(orders.createdAt, createdAt),\n and(eq(orders.createdAt, createdAt), lt(orders.id, id)),\n ),\n );\n }\n return and(...parts);\n}\n\nexport async function selectOrderPage(\n db: CommerceExecutor,\n filter: OrderListFilter,\n): Promise<OrderListRow[]> {\n const itemCount = sql<number>`coalesce((\n select sum(i.quantity) from commerce_order_items i\n where i.order_id = commerce_orders.id\n ), 0)::int`;\n return db\n .select({\n id: orders.id,\n status: orders.status,\n email: orders.email,\n totalCents: sql<number>`${orders.subtotalCents} - ${orders.discountCents} + ${orders.shippingCents}`,\n currency: orders.currency,\n itemCount,\n createdAt: orders.createdAt,\n createdAtKey: sql<string>`${orders.createdAt}::text`,\n })\n .from(orders)\n .where(conditions(filter))\n .orderBy(desc(orders.createdAt), desc(orders.id))\n .limit(filter.limit);\n}\n\nexport async function findOrderRefunds(db: CommerceExecutor, orderId: string) {\n return db\n .select()\n .from(orderRefunds)\n .where(eq(orderRefunds.orderId, orderId))\n .orderBy(orderRefunds.createdAt, orderRefunds.id);\n}\n","import { and, asc, eq, sql } from 'drizzle-orm';\nimport type { CommerceExecutor } from '../db/types';\nimport { orderItems, orderRefunds, orders, stripeEvents } from '../db/schema';\nimport type { ChosenShipping, ShippingAddress } from '../payment/gateway';\nimport { isRecordedRefund } from './refund-repo';\n\nexport type OrderRow = typeof orders.$inferSelect;\nexport type OrderItemRow = typeof orderItems.$inferSelect;\ntype NewOrder = typeof orders.$inferInsert;\ntype NewOrderItem = Omit<typeof orderItems.$inferInsert, 'orderId'>;\ntype OrderStatus = OrderRow['status'];\n\nexport async function insertOrder(\n db: CommerceExecutor,\n order: NewOrder & { id: string },\n items: NewOrderItem[],\n): Promise<void> {\n await db.insert(orders).values(order);\n await db\n .insert(orderItems)\n .values(items.map((item) => ({ ...item, orderId: order.id })));\n}\n\nexport async function setCheckoutSession(\n db: CommerceExecutor,\n orderId: string,\n sessionId: string,\n): Promise<void> {\n await db\n .update(orders)\n .set({ checkoutSessionId: sessionId, updatedAt: sql`now()` })\n .where(eq(orders.id, orderId));\n}\n\nexport async function setOrderStatus(\n db: CommerceExecutor,\n orderId: string,\n status: OrderStatus,\n): Promise<void> {\n await db\n .update(orders)\n .set({ status, updatedAt: sql`now()` })\n .where(eq(orders.id, orderId));\n}\n\ninterface OrderDetails {\n email?: string;\n shippingAddress?: ShippingAddress;\n shipping?: ChosenShipping;\n}\n\nexport async function saveOrderDetails(\n db: CommerceExecutor,\n orderId: string,\n details: OrderDetails,\n status?: OrderStatus,\n): Promise<void> {\n await db\n .update(orders)\n .set({\n ...(status && { status }),\n email: details.email ?? null,\n shippingAddress: details.shippingAddress ?? null,\n shippingCents: details.shipping?.amountCents ?? 0,\n shippingLabel: details.shipping?.label ?? null,\n updatedAt: sql`now()`,\n })\n .where(eq(orders.id, orderId));\n}\n\nexport async function findOrderBySession(\n db: CommerceExecutor,\n sessionId: string,\n): Promise<OrderRow | undefined> {\n const [row] = await db\n .select()\n .from(orders)\n .where(eq(orders.checkoutSessionId, sessionId))\n .limit(1);\n return row;\n}\n\nexport async function findOrderById(\n db: CommerceExecutor,\n orderId: string,\n): Promise<OrderRow | undefined> {\n const [row] = await db\n .select()\n .from(orders)\n .where(eq(orders.id, orderId))\n .limit(1);\n return row;\n}\n\nexport async function findOrderItems(\n db: CommerceExecutor,\n orderId: string,\n): Promise<OrderItemRow[]> {\n return db\n .select()\n .from(orderItems)\n .where(eq(orderItems.orderId, orderId))\n .orderBy(asc(orderItems.name));\n}\n\nexport async function recordEvent(\n db: CommerceExecutor,\n id: string,\n type: string,\n): Promise<boolean> {\n const inserted = await db\n .insert(stripeEvents)\n .values({ id, type })\n .onConflictDoNothing()\n .returning({ id: stripeEvents.id });\n return inserted.length > 0;\n}\n\nexport async function sumRefundedCents(\n db: CommerceExecutor,\n orderId: string,\n): Promise<number> {\n const [row] = await db\n .select({\n total: sql<number>`coalesce(sum(${orderRefunds.amountCents}), 0)::int`,\n })\n .from(orderRefunds)\n .where(and(eq(orderRefunds.orderId, orderId), isRecordedRefund));\n return row.total;\n}\n","import { and, eq, isNotNull, isNull, or, sql } from 'drizzle-orm';\nimport { orderRefunds, orders } from '../db/schema';\nimport type { CommerceExecutor } from '../db/types';\n\nexport type RefundRow = typeof orderRefunds.$inferSelect;\ntype NewRefund = typeof orderRefunds.$inferInsert;\n\n// An admin refund is written as a pending row (key set, no gateway id) before\n// the gateway call so its amount counts against the balance while in flight.\nexport const isRecordedRefund = or(\n isNull(orderRefunds.idempotencyKey),\n isNotNull(orderRefunds.gatewayRefundId),\n);\n\nexport async function lockOrderForRefund(tx: CommerceExecutor, id: string) {\n const [row] = await tx\n .select({\n status: orders.status,\n totalCents: sql<number>`${orders.subtotalCents} - ${orders.discountCents} + ${orders.shippingCents}`,\n currency: orders.currency,\n checkoutSessionId: orders.checkoutSessionId,\n })\n .from(orders)\n .where(eq(orders.id, id))\n .for('update');\n return row;\n}\n\nexport async function sumReservedCents(\n tx: CommerceExecutor,\n orderId: string,\n): Promise<number> {\n const [row] = await tx\n .select({\n total: sql<number>`coalesce(sum(${orderRefunds.amountCents}), 0)::int`,\n })\n .from(orderRefunds)\n .where(eq(orderRefunds.orderId, orderId));\n return row.total;\n}\n\nexport async function findRefundByKey(\n tx: CommerceExecutor,\n orderId: string,\n idempotencyKey: string,\n): Promise<RefundRow | undefined> {\n const [row] = await tx\n .select()\n .from(orderRefunds)\n .where(\n and(\n eq(orderRefunds.orderId, orderId),\n eq(orderRefunds.idempotencyKey, idempotencyKey),\n ),\n );\n return row;\n}\n\nexport async function insertPendingRefund(\n tx: CommerceExecutor,\n refund: NewRefund & { idempotencyKey: string },\n): Promise<RefundRow> {\n const [row] = await tx.insert(orderRefunds).values(refund).returning();\n return row;\n}\n\nexport async function deletePendingRefund(\n db: CommerceExecutor,\n id: string,\n): Promise<void> {\n await db\n .delete(orderRefunds)\n .where(and(eq(orderRefunds.id, id), isNull(orderRefunds.gatewayRefundId)));\n}\n\n// A concurrent retry that failed may have deleted the pending row, so fall\n// back to inserting; the unique gateway id makes a second record a no-op.\nexport async function confirmRefund(\n tx: CommerceExecutor,\n pending: RefundRow,\n gatewayRefundId: string,\n): Promise<RefundRow | undefined> {\n const [existing] = await tx\n .select({ id: orderRefunds.id })\n .from(orderRefunds)\n .where(eq(orderRefunds.gatewayRefundId, gatewayRefundId));\n if (existing) {\n await deletePendingRefund(tx, pending.id);\n return undefined;\n }\n const [updated] = await tx\n .update(orderRefunds)\n .set({ gatewayRefundId })\n .where(\n and(\n eq(orderRefunds.id, pending.id),\n isNull(orderRefunds.gatewayRefundId),\n ),\n )\n .returning();\n if (updated) return updated;\n const [inserted] = await tx\n .insert(orderRefunds)\n .values({\n orderId: pending.orderId,\n amountCents: pending.amountCents,\n currency: pending.currency,\n reason: pending.reason,\n restocked: pending.restocked,\n source: pending.source,\n idempotencyKey: pending.idempotencyKey,\n gatewayRefundId,\n })\n .onConflictDoNothing()\n .returning();\n return inserted;\n}\n\nexport async function findRefundByGatewayId(\n tx: CommerceExecutor,\n gatewayRefundId: string,\n): Promise<RefundRow | undefined> {\n const [row] = await tx\n .select()\n .from(orderRefunds)\n .where(eq(orderRefunds.gatewayRefundId, gatewayRefundId));\n return row;\n}\n\n// Matches an in-flight admin refund by the scoped key it sent the gateway\n// (`orderId:idempotencyKey`) and fills in its gateway id.\nexport async function confirmPendingByKey(\n tx: CommerceExecutor,\n orderId: string,\n scopedKey: string,\n gatewayRefundId: string,\n): Promise<RefundRow | undefined> {\n const prefix = `${orderId}:`;\n if (!scopedKey.startsWith(prefix)) return undefined;\n const [updated] = await tx\n .update(orderRefunds)\n .set({ gatewayRefundId })\n .where(\n and(\n eq(orderRefunds.orderId, orderId),\n eq(orderRefunds.idempotencyKey, scopedKey.slice(prefix.length)),\n isNull(orderRefunds.gatewayRefundId),\n ),\n )\n .returning();\n return updated;\n}\n\nexport async function insertGatewayRefund(\n tx: CommerceExecutor,\n refund: Pick<RefundRow, 'orderId' | 'amountCents' | 'currency'> & {\n gatewayRefundId: string;\n },\n): Promise<RefundRow | undefined> {\n const [row] = await tx\n .insert(orderRefunds)\n .values({ ...refund, source: 'gateway', restocked: false })\n .onConflictDoNothing()\n .returning();\n return row;\n}\n","import type { OrderDetail, OrderSummary } from '@develemit/commerce-contracts';\nimport type { CommerceExecutor } from '../db/types';\nimport type { ShippingAddress } from '../payment/gateway';\nimport {\n findOrderById,\n findOrderBySession,\n findOrderItems,\n type OrderRow,\n} from './repo';\n\nfunction toFulfillment(order: OrderRow): OrderSummary['fulfillment'] {\n if (!order.shippedAt) return null;\n return {\n carrier: order.fulfillmentCarrier,\n trackingNumber: order.fulfillmentTrackingNumber,\n trackingUrl: order.fulfillmentTrackingUrl,\n shippedAt: order.shippedAt.toISOString(),\n };\n}\n\nasync function toSummary(\n db: CommerceExecutor,\n order: OrderRow,\n): Promise<OrderSummary> {\n const items = await findOrderItems(db, order.id);\n return {\n id: order.id,\n status: order.status,\n subtotalCents: order.subtotalCents,\n discountCents: order.discountCents,\n discountCode: order.discountCode,\n shippingCents: order.shippingCents,\n shippingLabel: order.shippingLabel,\n totalCents: order.subtotalCents - order.discountCents + order.shippingCents,\n currency: order.currency,\n items: items.map((i) => ({\n productId: i.productId,\n name: i.name,\n priceCents: i.priceCents,\n quantity: i.quantity,\n })),\n fulfillment: toFulfillment(order),\n createdAt: order.createdAt.toISOString(),\n };\n}\n\nexport async function summaryBySession(\n db: CommerceExecutor,\n sessionId: string,\n): Promise<OrderSummary | undefined> {\n const order = await findOrderBySession(db, sessionId);\n return order && toSummary(db, order);\n}\n\nexport async function detailById(\n db: CommerceExecutor,\n orderId: string,\n): Promise<OrderDetail | undefined> {\n const order = await findOrderById(db, orderId);\n if (!order) return undefined;\n return {\n ...(await toSummary(db, order)),\n email: order.email,\n shippingAddress: (order.shippingAddress as ShippingAddress | null) ?? null,\n };\n}\n","export class IdempotencyKeyRequiredError extends Error {\n readonly code = 'IDEMPOTENCY_KEY_REQUIRED';\n constructor() {\n super('An Idempotency-Key header of 1-255 characters is required');\n this.name = 'IdempotencyKeyRequiredError';\n }\n}\n\nexport class RefundExceedsBalanceError extends Error {\n readonly code = 'REFUND_EXCEEDS_BALANCE';\n constructor(public readonly balanceCents: number) {\n super(`The refundable balance is ${balanceCents} cents`);\n this.name = 'RefundExceedsBalanceError';\n }\n}\n\nexport class RestockRequiresFullRefundError extends Error {\n readonly code = 'RESTOCK_REQUIRES_FULL_REFUND';\n constructor() {\n super('Restock is only allowed when refunding the remaining balance');\n this.name = 'RestockRequiresFullRefundError';\n }\n}\n\nexport class RestockNotApplicableError extends Error {\n readonly code = 'RESTOCK_NOT_APPLICABLE';\n constructor() {\n super('This order never took stock, so there is nothing to restock');\n this.name = 'RestockNotApplicableError';\n }\n}\n\nexport class RefundGatewayError extends Error {\n readonly code = 'REFUND_GATEWAY_ERROR';\n constructor(cause: unknown) {\n super('The payment gateway could not create the refund', { cause });\n this.name = 'RefundGatewayError';\n }\n}\n\nexport class RefundOutcomeUnknownError extends Error {\n readonly code = 'REFUND_OUTCOME_UNKNOWN';\n constructor(cause: unknown) {\n super(\n 'The payment gateway did not confirm the refund and it may still have happened. Retry with the same Idempotency-Key or wait for the charge.refunded webhook',\n { cause },\n );\n this.name = 'RefundOutcomeUnknownError';\n }\n}\n","import type { DiscountRejectionReason } from '@develemit/commerce-contracts';\n\nexport interface EvaluableDiscount {\n type: 'percent' | 'fixed';\n percentOff: number | null;\n amountOffCents: number | null;\n currency: string | null;\n minSubtotalCents: number | null;\n startsAt: Date | null;\n endsAt: Date | null;\n maxRedemptions: number | null;\n active: boolean;\n}\n\nexport interface EvaluationContext {\n subtotalCents: number;\n currency: string;\n now: Date;\n activeRedemptions: number;\n}\n\nexport type Evaluation =\n | { ok: true; discountCents: number }\n | { ok: false; reason: DiscountRejectionReason };\n\nfunction rejection(reason: DiscountRejectionReason): Evaluation {\n return { ok: false, reason };\n}\n\nexport function discountAmount(\n discount: EvaluableDiscount,\n subtotalCents: number,\n): number {\n if (discount.type === 'percent') {\n return Math.floor((subtotalCents * (discount.percentOff ?? 0)) / 100);\n }\n return Math.min(discount.amountOffCents ?? 0, subtotalCents);\n}\n\nexport function evaluateDiscount(\n discount: EvaluableDiscount | undefined,\n ctx: EvaluationContext,\n): Evaluation {\n if (!discount) return rejection('not_found');\n if (!discount.active) return rejection('inactive');\n if (discount.startsAt && ctx.now < discount.startsAt) {\n return rejection('not_started');\n }\n if (discount.endsAt && ctx.now >= discount.endsAt) {\n return rejection('expired');\n }\n if (discount.type === 'fixed' && discount.currency !== ctx.currency) {\n return rejection('currency');\n }\n if (\n discount.minSubtotalCents !== null &&\n ctx.subtotalCents < discount.minSubtotalCents\n ) {\n return rejection('min_subtotal');\n }\n if (\n discount.maxRedemptions !== null &&\n ctx.activeRedemptions >= discount.maxRedemptions\n ) {\n return rejection('exhausted');\n }\n return {\n ok: true,\n discountCents: discountAmount(discount, ctx.subtotalCents),\n };\n}\n","import {\n and,\n desc,\n eq,\n getTableColumns,\n gt,\n ilike,\n inArray,\n isNull,\n lt,\n lte,\n or,\n sql,\n type SQL,\n} from 'drizzle-orm';\nimport { discountRedemptions, discounts } from '../db/schema';\nimport type { CommerceExecutor } from '../db/types';\n\nexport type DiscountRow = typeof discounts.$inferSelect;\nexport type RedemptionRow = typeof discountRedemptions.$inferSelect;\n\nexport async function findDiscountByCode(\n db: CommerceExecutor,\n code: string,\n options: { forUpdate?: boolean } = {},\n): Promise<DiscountRow | undefined> {\n const query = db.select().from(discounts).where(eq(discounts.code, code));\n const [row] = await (options.forUpdate ? query.for('update') : query);\n return row;\n}\n\nexport async function countActiveRedemptions(\n db: CommerceExecutor,\n discountId: string,\n): Promise<number> {\n const [row] = await db\n .select({ n: sql<number>`count(*)::int` })\n .from(discountRedemptions)\n .where(\n and(\n eq(discountRedemptions.discountId, discountId),\n inArray(discountRedemptions.status, ['reserved', 'committed']),\n ),\n );\n return row.n;\n}\n\nexport async function insertRedemption(\n db: CommerceExecutor,\n redemption: { discountId: string; orderId: string; amountCents: number },\n): Promise<void> {\n await db.insert(discountRedemptions).values(redemption);\n}\n\nexport async function transitionRedemptions(\n db: CommerceExecutor,\n orderIds: string[],\n from: RedemptionRow['status'][],\n to: RedemptionRow['status'],\n): Promise<void> {\n if (orderIds.length === 0) return;\n await db\n .update(discountRedemptions)\n .set({ status: to, updatedAt: sql`now()` })\n .where(\n and(\n inArray(discountRedemptions.orderId, orderIds),\n inArray(discountRedemptions.status, from),\n ),\n );\n}\n\nexport type DiscountStatus = 'active' | 'inactive' | 'scheduled' | 'expired';\n\nexport interface DiscountListFilter {\n status?: DiscountStatus;\n q?: string;\n now: Date;\n after?: { createdAt: string; id: string };\n limit: number;\n}\n\nexport type DiscountWithCounts = DiscountRow & {\n redeemedCount: number;\n reservedCount: number;\n totalRedemptions: number;\n};\n\nfunction escapeLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (c) => `\\\\${c}`);\n}\n\nfunction statusCondition(status: DiscountStatus, now: Date): SQL | undefined {\n const started = or(isNull(discounts.startsAt), lte(discounts.startsAt, now));\n const open = or(isNull(discounts.endsAt), gt(discounts.endsAt, now));\n if (status === 'inactive') return eq(discounts.active, false);\n if (status === 'scheduled') {\n return and(eq(discounts.active, true), gt(discounts.startsAt, now));\n }\n if (status === 'expired') {\n return and(eq(discounts.active, true), lte(discounts.endsAt, now));\n }\n return and(eq(discounts.active, true), started, open);\n}\n\nfunction listConditions(filter: DiscountListFilter): SQL | undefined {\n const parts: (SQL | undefined)[] = [];\n if (filter.status) parts.push(statusCondition(filter.status, filter.now));\n if (filter.q) parts.push(ilike(discounts.code, `${escapeLike(filter.q)}%`));\n if (filter.after) {\n const { id } = filter.after;\n const createdAt = sql`${filter.after.createdAt}::timestamptz`;\n parts.push(\n or(\n lt(discounts.createdAt, createdAt),\n and(eq(discounts.createdAt, createdAt), lt(discounts.id, id)),\n ),\n );\n }\n return and(...parts);\n}\n\nfunction redemptionCount(status?: 'reserved' | 'committed') {\n const filter = status ? sql`and r.status = ${status}` : sql``;\n return sql<number>`(\n select count(*) from commerce_discount_redemptions r\n where r.discount_id = commerce_discounts.id ${filter}\n )::int`;\n}\n\nconst withCounts = {\n ...getTableColumns(discounts),\n redeemedCount: redemptionCount('committed'),\n reservedCount: redemptionCount('reserved'),\n totalRedemptions: redemptionCount(),\n};\n\nexport async function selectDiscountPage(\n db: CommerceExecutor,\n filter: DiscountListFilter,\n): Promise<(DiscountWithCounts & { createdAtKey: string })[]> {\n return db\n .select({\n ...withCounts,\n createdAtKey: sql<string>`${discounts.createdAt}::text`,\n })\n .from(discounts)\n .where(listConditions(filter))\n .orderBy(desc(discounts.createdAt), desc(discounts.id))\n .limit(filter.limit);\n}\n\nexport async function findDiscountById(\n db: CommerceExecutor,\n id: string,\n options: { forUpdate?: boolean } = {},\n): Promise<DiscountWithCounts | undefined> {\n const query = db\n .select(withCounts)\n .from(discounts)\n .where(eq(discounts.id, id));\n const [row] = await (options.forUpdate ? query.for('update') : query);\n return row;\n}\n\nexport async function insertDiscount(\n db: CommerceExecutor,\n values: typeof discounts.$inferInsert,\n): Promise<string | undefined> {\n const [row] = await db\n .insert(discounts)\n .values(values)\n .onConflictDoNothing({ target: discounts.code })\n .returning({ id: discounts.id });\n return row?.id;\n}\n\nexport async function updateDiscount(\n db: CommerceExecutor,\n id: string,\n values: Partial<typeof discounts.$inferInsert>,\n): Promise<void> {\n await db\n .update(discounts)\n .set({ ...values, updatedAt: sql`now()` })\n .where(eq(discounts.id, id));\n}\n","import type { DiscountRejectionReason } from '@develemit/commerce-contracts';\nimport type { CommerceExecutor } from '../db/types';\nimport { evaluateDiscount } from './evaluate';\nimport {\n countActiveRedemptions,\n findDiscountByCode,\n insertRedemption,\n transitionRedemptions,\n} from './repo';\n\nexport class DiscountRejectedError extends Error {\n readonly code = 'DISCOUNT_INVALID';\n constructor(public readonly reason: DiscountRejectionReason) {\n super(`Discount rejected: ${reason}`);\n this.name = 'DiscountRejectedError';\n }\n}\n\nexport interface AppliedDiscount {\n code: string;\n discountCents: number;\n}\n\ninterface CheckoutDiscountInput {\n code: string;\n subtotalCents: number;\n currency: string;\n now?: Date;\n}\n\n// The discount row lock serializes rival checkouts for the same code, so the\n// count read after it includes every redemption a committed rival reserved.\n// A code worth nothing on this cart is still recorded on the order, but takes\n// no redemption slot.\nexport async function evaluateForCheckout(\n tx: CommerceExecutor,\n { code, subtotalCents, currency, now = new Date() }: CheckoutDiscountInput,\n): Promise<AppliedDiscount & { discountId: string }> {\n const discount = await findDiscountByCode(tx, code, { forUpdate: true });\n const result = evaluateDiscount(discount, {\n subtotalCents,\n currency,\n now,\n activeRedemptions: discount\n ? await countActiveRedemptions(tx, discount.id)\n : 0,\n });\n if (!result.ok) {\n throw new DiscountRejectedError(\n result.reason === 'inactive' ? 'not_found' : result.reason,\n );\n }\n return {\n code,\n discountCents: result.discountCents,\n discountId: discount!.id,\n };\n}\n\nexport async function reserveRedemption(\n tx: CommerceExecutor,\n orderId: string,\n applied: AppliedDiscount & { discountId: string },\n): Promise<void> {\n if (applied.discountCents === 0) return;\n await insertRedemption(tx, {\n discountId: applied.discountId,\n orderId,\n amountCents: applied.discountCents,\n });\n}\n\n// A payment can land after the sweeper released the redemption; the buyer was\n// charged the discounted price, so it is committed regardless.\nexport async function commitRedemption(\n db: CommerceExecutor,\n orderId: string,\n): Promise<void> {\n await transitionRedemptions(\n db,\n [orderId],\n ['reserved', 'released'],\n 'committed',\n );\n}\n\nexport async function releaseRedemptions(\n db: CommerceExecutor,\n orderIds: string[],\n): Promise<void> {\n await transitionRedemptions(db, orderIds, ['reserved'], 'released');\n}\n","import type { ShippingRateOption } from '../shipping/repo';\n\nexport interface CheckoutLine {\n productId: string;\n name: string;\n unitAmountCents: number;\n quantity: number;\n}\n\nexport interface CreateCheckoutSessionInput {\n reference: string;\n currency: string;\n lines: CheckoutLine[];\n successUrl: string;\n cancelUrl: string;\n shippingCountries: string[];\n shippingRates: ShippingRateOption[];\n expiresAt: Date;\n // Off the merchandise subtotal, never shipping. The kit has already\n // evaluated it; gateways apply this exact amount.\n discount?: CheckoutDiscount;\n}\n\nexport interface CheckoutDiscount {\n code: string;\n amountCents: number;\n}\n\nexport interface CheckoutSession {\n id: string;\n url: string;\n}\n\nexport interface ShippingAddress {\n name?: string;\n line1?: string;\n line2?: string;\n city?: string;\n state?: string;\n postalCode?: string;\n country?: string;\n}\n\nexport interface ChosenShipping {\n label: string;\n amountCents: number;\n}\n\nexport interface GatewayRefundEvent {\n gatewayRefundId: string;\n amountCents: number;\n // The idempotencyKey the kit passed to refund(), when the refund came from it.\n idempotencyKey?: string;\n}\n\nexport type PaymentEvent =\n | {\n id: string;\n type: 'checkout.completed';\n sessionId: string;\n email?: string;\n shippingAddress?: ShippingAddress;\n shipping?: ChosenShipping;\n paymentPending?: boolean;\n }\n | { id: string; type: 'checkout.async_paid'; sessionId: string }\n | { id: string; type: 'checkout.async_failed'; sessionId: string }\n | { id: string; type: 'checkout.expired'; sessionId: string }\n | {\n id: string;\n type: 'payment.refunded';\n sessionId: string;\n refunds: GatewayRefundEvent[];\n }\n | { id: string; type: 'ignored'; sourceType: string };\n\nexport interface RefundInput {\n sessionId: string;\n amountCents: number;\n idempotencyKey: string;\n}\n\nexport interface GatewayRefund {\n gatewayRefundId: string;\n}\n\nexport interface PaymentGateway {\n // Awaited while the plugin registers, so a gateway that can't work (a\n // missing optional peer) fails startup instead of the first checkout.\n preflight?(): Promise<void>;\n createCheckoutSession(\n input: CreateCheckoutSessionInput,\n ): Promise<CheckoutSession>;\n verifyWebhook(\n rawBody: Buffer,\n signature: string | undefined,\n ): Promise<PaymentEvent>;\n refund(input: RefundInput): Promise<GatewayRefund>;\n}\n\n// Thrown by a gateway when it is certain no refund was created. Any other\n// error from refund() is treated as an unknown outcome.\nexport class RefundRejectedError extends Error {\n readonly definitelyFailed = true;\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'RefundRejectedError';\n }\n}\n\nexport function isDefiniteRefundFailure(err: unknown): boolean {\n return err instanceof RefundRejectedError;\n}\n\nexport const SESSION_ID_PLACEHOLDER = '{CHECKOUT_SESSION_ID}';\n\nexport class WebhookSignatureError extends Error {\n readonly code = 'INVALID_WEBHOOK_SIGNATURE';\n constructor(message = 'Webhook signature verification failed') {\n super(message);\n this.name = 'WebhookSignatureError';\n }\n}\n","import type { GatewayRefundEvent } from '../payment/gateway';\nimport type { CommerceExecutor } from '../db/types';\nimport { releaseRedemptions } from '../discounts/redemptions';\nimport { restock } from '../inventory/service';\nimport { toOrderRefund } from './admin-service';\nimport type { Applied } from './apply-event';\nimport {\n confirmPendingByKey,\n findRefundByGatewayId,\n insertGatewayRefund,\n lockOrderForRefund,\n type RefundRow,\n} from './refund-repo';\nimport {\n findOrderItems,\n setOrderStatus,\n sumRefundedCents,\n type OrderRow,\n} from './repo';\n\nconst ACCEPTS_REFUNDS = new Set([\n 'paid',\n 'shipped',\n 'needs_attention',\n 'refunded',\n]);\nconst SETTLES_AS_REFUNDED = new Set(['paid', 'shipped', 'needs_attention']);\n\n// A refund the admin API started may still be pending when its echo arrives;\n// the echo then completes that row, including its restock, so the admin\n// path's own confirm becomes a no-op and the hook fires exactly once.\nasync function recordRefund(\n tx: CommerceExecutor,\n order: OrderRow,\n refund: GatewayRefundEvent,\n): Promise<RefundRow | undefined> {\n if (await findRefundByGatewayId(tx, refund.gatewayRefundId)) return undefined;\n const confirmed =\n refund.idempotencyKey &&\n (await confirmPendingByKey(\n tx,\n order.id,\n refund.idempotencyKey,\n refund.gatewayRefundId,\n ));\n if (confirmed) {\n if (confirmed.restocked) {\n await restock(tx, await findOrderItems(tx, order.id));\n }\n return confirmed;\n }\n return insertGatewayRefund(tx, {\n orderId: order.id,\n amountCents: refund.amountCents,\n currency: order.currency,\n gatewayRefundId: refund.gatewayRefundId,\n });\n}\n\nexport async function applyGatewayRefunds(\n tx: CommerceExecutor,\n order: OrderRow,\n refunds: GatewayRefundEvent[],\n): Promise<Applied> {\n const locked = await lockOrderForRefund(tx, order.id);\n if (!locked || !ACCEPTS_REFUNDS.has(locked.status)) {\n return { outcome: 'ignored' };\n }\n const hooks: NonNullable<Applied['hooks']> = [];\n for (const refund of refunds) {\n const recorded = await recordRefund(tx, order, refund);\n if (recorded) {\n hooks.push(['onOrderRefunded', order.id, toOrderRefund(recorded)]);\n }\n }\n if (\n SETTLES_AS_REFUNDED.has(locked.status) &&\n (await sumRefundedCents(tx, order.id)) >= locked.totalCents\n ) {\n await setOrderStatus(tx, order.id, 'refunded');\n await releaseRedemptions(tx, [order.id]);\n }\n return { outcome: 'processed', hooks };\n}\n","import type { CommerceExecutor } from '../db/types';\nimport { commitRedemption, releaseRedemptions } from '../discounts/redemptions';\nimport { commit, extendHolds, release } from '../inventory/service';\nimport type { PaymentEvent } from '../payment/gateway';\nimport { saveOrderDetails, setOrderStatus, type OrderRow } from './repo';\nimport { applyGatewayRefunds } from './gateway-refunds';\nimport type { OrderHooks } from './types';\n\nexport type HookName = keyof OrderHooks;\nexport type HookCall = [HookName, string, ...unknown[]];\nexport type Applied = {\n outcome: 'processed' | 'ignored';\n hooks?: HookCall[];\n};\n\nconst ignored: Applied = { outcome: 'ignored' };\n\n// Bank debits can take days to settle; the hold has to outlive them.\nexport const ASYNC_PAYMENT_HOLD_MS = 14 * 24 * 60 * 60 * 1000;\n\ntype Completed = Extract<PaymentEvent, { type: 'checkout.completed' }>;\n\nasync function settle(tx: CommerceExecutor, order: OrderRow): Promise<Applied> {\n if ((await commit(tx, order.id)) === 'unavailable') {\n await setOrderStatus(tx, order.id, 'needs_attention');\n return {\n outcome: 'processed',\n hooks: [['onOrderNeedsAttention', order.id]],\n };\n }\n await setOrderStatus(tx, order.id, 'paid');\n await commitRedemption(tx, order.id);\n return { outcome: 'processed', hooks: [['onOrderPaid', order.id]] };\n}\n\nasync function completed(\n tx: CommerceExecutor,\n order: OrderRow,\n event: Completed,\n): Promise<Applied> {\n if (['paid', 'shipped', 'needs_attention'].includes(order.status)) {\n return ignored;\n }\n await saveOrderDetails(tx, order.id, event);\n if (event.paymentPending) {\n await extendHolds(\n tx,\n order.id,\n new Date(Date.now() + ASYNC_PAYMENT_HOLD_MS),\n );\n return { outcome: 'processed' };\n }\n return settle(tx, order);\n}\n\nexport async function applyToOrder(\n tx: CommerceExecutor,\n order: OrderRow,\n event: Exclude<PaymentEvent, { type: 'ignored' }>,\n): Promise<Applied> {\n switch (event.type) {\n case 'checkout.completed':\n return completed(tx, order, event);\n case 'checkout.async_paid':\n return order.status === 'pending' ? settle(tx, order) : ignored;\n case 'checkout.async_failed':\n if (order.status !== 'pending') return ignored;\n await setOrderStatus(tx, order.id, 'cancelled');\n await release(tx, order.id);\n await releaseRedemptions(tx, [order.id]);\n return {\n outcome: 'processed',\n hooks: [['onOrderPaymentFailed', order.id]],\n };\n case 'checkout.expired':\n if (order.status !== 'pending') return ignored;\n await setOrderStatus(tx, order.id, 'expired');\n await release(tx, order.id);\n await releaseRedemptions(tx, [order.id]);\n return { outcome: 'processed', hooks: [['onOrderExpired', order.id]] };\n case 'payment.refunded':\n return applyGatewayRefunds(tx, order, event.refunds);\n }\n}\n","import type { OrderDetail, OrderRefund } from '@develemit/commerce-contracts';\nimport type { PaymentGateway } from '../payment/gateway';\n\nexport interface CheckoutUrls {\n success(sessionIdPlaceholder: string): string;\n cancel: string;\n}\n\nexport interface CheckoutSettings {\n currency: string;\n payment: PaymentGateway;\n urls: CheckoutUrls;\n shippingCountries: string[];\n hooks?: OrderHooks;\n}\n\nexport interface OrderHooks {\n onOrderPaid?(order: OrderDetail): Promise<void>;\n onOrderExpired?(order: OrderDetail): Promise<void>;\n onOrderNeedsAttention?(order: OrderDetail): Promise<void>;\n onOrderPaymentFailed?(order: OrderDetail): Promise<void>;\n onOrderShipped?(order: OrderDetail): Promise<void>;\n onOrderRefunded?(order: OrderDetail, refund: OrderRefund): Promise<void>;\n}\n\nexport interface HookLogger {\n error(obj: { err: unknown }, msg: string): void;\n warn?(obj: object, msg: string): void;\n}\n\nexport class CheckoutConflictError extends Error {\n readonly code = 'CHECKOUT_CONFLICT';\n constructor(public readonly productIds: string[]) {\n super(`Unavailable products: ${productIds.join(', ')}`);\n this.name = 'CheckoutConflictError';\n }\n}\n\nexport class PaymentGatewayError extends Error {\n readonly code = 'PAYMENT_GATEWAY_ERROR';\n constructor(cause: unknown) {\n super('The payment gateway could not create a checkout session', {\n cause,\n });\n this.name = 'PaymentGatewayError';\n }\n}\n\nexport class OrderNotFoundError extends Error {\n readonly code = 'ORDER_NOT_FOUND';\n constructor(public readonly sessionId: string) {\n super(`No order for checkout session: ${sessionId}`);\n this.name = 'OrderNotFoundError';\n }\n}\n","import type { OrderDetail, OrderSummary } from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport type { PaymentEvent } from '../payment/gateway';\nimport { applyToOrder, type HookCall, type HookName } from './apply-event';\nimport { detailById, summaryBySession } from './order-detail';\nimport { findOrderBySession, recordEvent } from './repo';\nimport { OrderNotFoundError, type HookLogger, type OrderHooks } from './types';\n\nexport type PaymentEventOutcome = 'processed' | 'duplicate' | 'ignored';\n\nexport async function getOrderSummary(\n db: CommerceDb,\n sessionId: string,\n): Promise<OrderSummary> {\n const summary = await summaryBySession(db, sessionId);\n if (!summary) throw new OrderNotFoundError(sessionId);\n return summary;\n}\n\n// The event row is written in the same transaction as its effects, so a\n// failure rolls both back and the gateway's retry processes it again.\nasync function applyEvent(\n db: CommerceDb,\n event: PaymentEvent,\n): Promise<{ outcome: PaymentEventOutcome; hooks?: HookCall[] }> {\n return db.transaction(async (tx) => {\n const type = event.type === 'ignored' ? event.sourceType : event.type;\n if (!(await recordEvent(tx, event.id, type))) {\n return { outcome: 'duplicate' };\n }\n if (event.type === 'ignored') return { outcome: 'ignored' };\n const order = await findOrderBySession(tx, event.sessionId);\n if (!order) return { outcome: 'ignored' };\n return applyToOrder(tx, order, event);\n });\n}\n\n// Hooks run after commit and only for the delivery that processed the event,\n// so a replay never re-fires them. A throwing hook is logged, not retried:\n// failing the webhook would make the gateway redeliver an already-applied event.\nexport async function runHook(\n db: CommerceDb,\n hooks: OrderHooks,\n [name, orderId]: [HookName, string],\n log?: HookLogger,\n ...args: unknown[]\n): Promise<void> {\n const hook = hooks[name] as\n ((order: OrderDetail, ...rest: unknown[]) => Promise<void>) | undefined;\n if (!hook) return;\n try {\n const order = await detailById(db, orderId);\n if (order) await hook.call(hooks, order, ...args);\n } catch (err) {\n log?.error({ err }, `${name} hook failed for order ${orderId}`);\n }\n}\n\nexport async function handlePaymentEvent(\n db: CommerceDb,\n event: PaymentEvent,\n hooks: OrderHooks = {},\n log?: HookLogger,\n): Promise<PaymentEventOutcome> {\n const { outcome, hooks: calls = [] } = await applyEvent(db, event);\n for (const [name, orderId, ...args] of calls) {\n await runHook(db, hooks, [name, orderId], log, ...args);\n }\n if (outcome === 'ignored' && event.type === 'payment.refunded') {\n log?.warn?.(\n { sessionId: event.sessionId },\n 'refund event for an unknown or unrefundable order',\n );\n }\n return outcome;\n}\n","import type {\n AdminOrderDetail,\n ShipOrderInput,\n} from '@develemit/commerce-contracts';\nimport { eq, sql } from 'drizzle-orm';\nimport { orders } from '../db/schema';\nimport type { CommerceDb } from '../db/types';\nimport { AdminOrderNotFoundError, getAdminOrder } from './admin-service';\nimport { runHook } from './order-service';\nimport type { HookLogger, OrderHooks } from './types';\n\nexport class InvalidOrderStateError extends Error {\n readonly code = 'INVALID_ORDER_STATE';\n constructor(public readonly status: string) {\n super(`The order is ${status}`);\n this.name = 'InvalidOrderStateError';\n }\n}\n\nasync function applyShipment(\n db: CommerceDb,\n id: string,\n input: ShipOrderInput,\n): Promise<boolean> {\n return db.transaction(async (tx) => {\n const [row] = await tx\n .select({ status: orders.status })\n .from(orders)\n .where(eq(orders.id, id))\n .for('update');\n if (!row) throw new AdminOrderNotFoundError(id);\n if (row.status !== 'paid' && row.status !== 'shipped') {\n throw new InvalidOrderStateError(row.status);\n }\n const first = row.status === 'paid';\n await tx\n .update(orders)\n .set({\n fulfillmentCarrier: input.carrier ?? null,\n fulfillmentTrackingNumber: input.trackingNumber ?? null,\n fulfillmentTrackingUrl: input.trackingUrl ?? null,\n ...(first && { status: 'shipped' as const, shippedAt: sql`now()` }),\n updatedAt: sql`now()`,\n })\n .where(eq(orders.id, id));\n return first;\n });\n}\n\nexport async function shipOrder(\n db: CommerceDb,\n id: string,\n input: ShipOrderInput,\n hooks: OrderHooks = {},\n log?: HookLogger,\n): Promise<AdminOrderDetail> {\n const first = await applyShipment(db, id, input);\n if (first) await runHook(db, hooks, ['onOrderShipped', id], log);\n return getAdminOrder(db, id);\n}\n","import type {\n AdminOrderDetail,\n RefundOrderInput,\n} from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport { releaseRedemptions } from '../discounts/redemptions';\nimport { restock } from '../inventory/service';\nimport {\n isDefiniteRefundFailure,\n type PaymentGateway,\n} from '../payment/gateway';\nimport {\n AdminOrderNotFoundError,\n getAdminOrder,\n toOrderRefund,\n} from './admin-service';\nimport { runHook } from './order-service';\nimport {\n confirmRefund,\n deletePendingRefund,\n findRefundByKey,\n insertPendingRefund,\n lockOrderForRefund,\n sumReservedCents,\n type RefundRow,\n} from './refund-repo';\nimport { findOrderItems, setOrderStatus, sumRefundedCents } from './repo';\nimport {\n RefundExceedsBalanceError,\n RefundGatewayError,\n RefundOutcomeUnknownError,\n RestockNotApplicableError,\n RestockRequiresFullRefundError,\n} from './refund-errors';\nimport { InvalidOrderStateError } from './ship-service';\nimport type { HookLogger, OrderHooks } from './types';\n\nconst REFUNDABLE = new Set(['paid', 'shipped', 'needs_attention']);\n\ntype Reservation =\n { kind: 'done' } | { kind: 'pending'; refund: RefundRow; sessionId: string };\n\nasync function reserveRefund(\n db: CommerceDb,\n orderId: string,\n input: RefundOrderInput,\n idempotencyKey: string,\n): Promise<Reservation> {\n return db.transaction(async (tx) => {\n const order = await lockOrderForRefund(tx, orderId);\n if (!order) throw new AdminOrderNotFoundError(orderId);\n const existing = await findRefundByKey(tx, orderId, idempotencyKey);\n if (existing?.gatewayRefundId) return { kind: 'done' };\n if (!REFUNDABLE.has(order.status) || !order.checkoutSessionId) {\n throw new InvalidOrderStateError(order.status);\n }\n const sessionId = order.checkoutSessionId;\n if (existing) return { kind: 'pending', refund: existing, sessionId };\n const balance = order.totalCents - (await sumReservedCents(tx, orderId));\n const amountCents = input.amountCents ?? balance;\n if (amountCents <= 0 || amountCents > balance) {\n throw new RefundExceedsBalanceError(balance);\n }\n if (input.restock && amountCents !== balance) {\n throw new RestockRequiresFullRefundError();\n }\n // needs_attention orders never committed their stock, so there is\n // nothing to give back.\n if (input.restock && order.status === 'needs_attention') {\n throw new RestockNotApplicableError();\n }\n const refund = await insertPendingRefund(tx, {\n orderId,\n amountCents,\n currency: order.currency,\n reason: input.reason ?? null,\n restocked: input.restock,\n source: 'admin',\n idempotencyKey,\n });\n return { kind: 'pending', refund, sessionId };\n });\n}\n\nasync function recordRefund(\n db: CommerceDb,\n pending: RefundRow,\n gatewayRefundId: string,\n): Promise<RefundRow | undefined> {\n return db.transaction(async (tx) => {\n const order = await lockOrderForRefund(tx, pending.orderId);\n const refund = await confirmRefund(tx, pending, gatewayRefundId);\n if (!order || !refund) return undefined;\n if (refund.restocked) {\n const items = await findOrderItems(tx, refund.orderId);\n await restock(tx, items);\n }\n if ((await sumRefundedCents(tx, refund.orderId)) >= order.totalCents) {\n await setOrderStatus(tx, refund.orderId, 'refunded');\n await releaseRedemptions(tx, [refund.orderId]);\n }\n return refund;\n });\n}\n\n// The gateway is called between two transactions so no row lock is held\n// across the network. The pending row keeps parallel refunds within the\n// balance, and the scoped idempotency key makes a retry return the same\n// gateway refund, which the unique gateway id then records only once. When\n// the gateway may have refunded anyway the pending row stays, holding its\n// amount until a same-key retry or the charge.refunded webhook confirms it.\nexport async function refundOrder(\n db: CommerceDb,\n gateway: PaymentGateway,\n orderId: string,\n input: RefundOrderInput,\n idempotencyKey: string,\n hooks: OrderHooks = {},\n log?: HookLogger,\n): Promise<AdminOrderDetail> {\n const reservation = await reserveRefund(db, orderId, input, idempotencyKey);\n if (reservation.kind === 'done') return getAdminOrder(db, orderId);\n const { refund: pending, sessionId } = reservation;\n let gatewayRefundId: string;\n try {\n ({ gatewayRefundId } = await gateway.refund({\n sessionId,\n amountCents: pending.amountCents,\n idempotencyKey: `${orderId}:${idempotencyKey}`,\n }));\n } catch (err) {\n if (!isDefiniteRefundFailure(err)) throw new RefundOutcomeUnknownError(err);\n await deletePendingRefund(db, pending.id);\n throw new RefundGatewayError(err);\n }\n const recorded = await recordRefund(db, pending, gatewayRefundId);\n if (recorded) {\n await runHook(\n db,\n hooks,\n ['onOrderRefunded', orderId],\n log,\n toOrderRefund(recorded),\n );\n }\n return getAdminOrder(db, orderId);\n}\n","import type { AdminOrderDetail } from '@develemit/commerce-contracts';\nimport { eq, sql } from 'drizzle-orm';\nimport { orders } from '../db/schema';\nimport type { CommerceDb } from '../db/types';\nimport { commitRedemption } from '../discounts/redemptions';\nimport { commitWithin } from '../inventory/service';\nimport { InsufficientStockError } from '../inventory/types';\nimport { AdminOrderNotFoundError, getAdminOrder } from './admin-service';\nimport { runHook } from './order-service';\nimport { InvalidOrderStateError } from './ship-service';\nimport type { HookLogger, OrderHooks } from './types';\n\nasync function applyResolution(db: CommerceDb, id: string): Promise<void> {\n await db.transaction(async (tx) => {\n const [row] = await tx\n .select({ status: orders.status })\n .from(orders)\n .where(eq(orders.id, id))\n .for('update');\n if (!row) throw new AdminOrderNotFoundError(id);\n if (row.status !== 'needs_attention') {\n throw new InvalidOrderStateError(row.status);\n }\n const outcome = await commitWithin(tx, id);\n if (outcome.result === 'unavailable') {\n throw new InsufficientStockError(outcome.shortProductIds);\n }\n await tx\n .update(orders)\n .set({ status: 'paid', updatedAt: sql`now()` })\n .where(eq(orders.id, id));\n await commitRedemption(tx, id);\n });\n}\n\nexport async function resolveOrder(\n db: CommerceDb,\n id: string,\n hooks: OrderHooks = {},\n log?: HookLogger,\n): Promise<AdminOrderDetail> {\n await applyResolution(db, id);\n await runHook(db, hooks, ['onOrderPaid', id], log);\n return getAdminOrder(db, id);\n}\n","import { adminDiscountListQuerySchema } from '@develemit/commerce-contracts';\nimport type { FastifyPluginAsync, FastifyRequest } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport { InvalidCursorError } from '../shared/cursor';\nimport {\n createDiscount,\n DiscountCodeTakenError,\n DiscountInUseError,\n DiscountNotFoundError,\n getAdminDiscount,\n InvalidDiscountError,\n InvalidDiscountIdError,\n isDiscountId,\n listAdminDiscounts,\n MaxRedemptionsBelowUsageError,\n patchDiscount,\n} from './admin-service';\n\nexport interface AdminDiscountsPluginOptions {\n db: CommerceDb;\n authorize(req: FastifyRequest): Promise<boolean>;\n}\n\nconst BAD_REQUEST = [\n InvalidCursorError,\n InvalidDiscountIdError,\n InvalidDiscountError,\n MaxRedemptionsBelowUsageError,\n];\n\nconst CONFLICT = [DiscountCodeTakenError, DiscountInUseError];\n\nfunction isZodError(err: unknown): err is Error & { issues: unknown[] } {\n return err instanceof Error && err.name === 'ZodError';\n}\n\nfunction discountId(req: FastifyRequest<{ Params: { id: string } }>): string {\n if (!isDiscountId(req.params.id)) throw new InvalidDiscountIdError();\n return req.params.id;\n}\n\nexport const adminDiscountsPlugin: FastifyPluginAsync<\n AdminDiscountsPluginOptions\n> = async (app, { db, authorize }) => {\n app.setErrorHandler((err, _req, reply) => {\n if (isZodError(err)) {\n return reply\n .code(400)\n .send({ error: 'INVALID_INPUT', issues: err.issues });\n }\n if (BAD_REQUEST.some((type) => err instanceof type)) {\n const { code, message } = err as Error & { code: string };\n return reply.code(400).send({ error: code, message });\n }\n if (CONFLICT.some((type) => err instanceof type)) {\n const { code, message } = err as Error & { code: string };\n return reply.code(409).send({ error: code, message });\n }\n if (err instanceof DiscountNotFoundError) {\n return reply.code(404).send({ error: err.code, message: err.message });\n }\n return reply.send(err);\n });\n\n app.addHook('preHandler', async (req, reply) => {\n if (!(await authorize(req))) {\n return reply.code(401).send({ error: 'UNAUTHORIZED' });\n }\n });\n\n app.get('/', async (req) =>\n listAdminDiscounts(db, adminDiscountListQuerySchema.parse(req.query)),\n );\n\n app.post('/', async (req, reply) => {\n const created = await createDiscount(db, (req.body ?? {}) as object);\n return reply.code(201).send(created);\n });\n\n app.get<{ Params: { id: string } }>('/:id', async (req) =>\n getAdminDiscount(db, discountId(req)),\n );\n\n app.patch<{ Params: { id: string } }>('/:id', async (req) =>\n patchDiscount(db, discountId(req), (req.body ?? {}) as object),\n );\n};\n","import {\n discountInputSchema,\n discountPatchSchema,\n type AdminDiscountListQuery,\n type AdminDiscountPage,\n type Discount,\n} from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport {\n decodeCursor,\n encodeCursor,\n isCursorTimestamp,\n} from '../shared/cursor';\nimport {\n findDiscountById,\n insertDiscount,\n selectDiscountPage,\n updateDiscount,\n type DiscountWithCounts,\n} from './repo';\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\ntype ParsedPatch = ReturnType<typeof discountPatchSchema.parse>;\n\nconst LOCKED_AFTER_USE = [\n 'code',\n 'type',\n 'percentOff',\n 'amountOffCents',\n 'currency',\n] as const;\n\nexport class DiscountNotFoundError extends Error {\n readonly code = 'DISCOUNT_NOT_FOUND';\n constructor(id: string) {\n super(`No discount: ${id}`);\n this.name = 'DiscountNotFoundError';\n }\n}\n\nexport class InvalidDiscountIdError extends Error {\n readonly code = 'INVALID_DISCOUNT_ID';\n constructor() {\n super('The discount id is malformed');\n this.name = 'InvalidDiscountIdError';\n }\n}\n\nexport class DiscountCodeTakenError extends Error {\n readonly code = 'DISCOUNT_CODE_TAKEN';\n constructor(code: string) {\n super(`The code ${code} already exists`);\n this.name = 'DiscountCodeTakenError';\n }\n}\n\nexport class DiscountInUseError extends Error {\n readonly code = 'DISCOUNT_IN_USE';\n constructor(public readonly fields: string[]) {\n super(`A redeemed discount cannot change ${fields.join(', ')}`);\n this.name = 'DiscountInUseError';\n }\n}\n\nexport class MaxRedemptionsBelowUsageError extends Error {\n readonly code = 'MAX_REDEMPTIONS_BELOW_USAGE';\n constructor(public readonly usage: number) {\n super(`maxRedemptions cannot be below the ${usage} already in use`);\n this.name = 'MaxRedemptionsBelowUsageError';\n }\n}\n\nexport class InvalidDiscountError extends Error {\n readonly code = 'INVALID_INPUT';\n constructor(message: string) {\n super(message);\n this.name = 'InvalidDiscountError';\n }\n}\n\nexport function isDiscountId(value: string): boolean {\n return UUID.test(value);\n}\n\nfunction parseDate(value: string | null | undefined): Date | null | undefined {\n if (value === undefined || value === null) return value;\n const date = new Date(value);\n if (Number.isNaN(+date)) throw new InvalidDiscountError('Invalid date');\n return date;\n}\n\nfunction toDiscount(row: DiscountWithCounts): Discount {\n return {\n id: row.id,\n code: row.code,\n type: row.type,\n percentOff: row.percentOff,\n amountOffCents: row.amountOffCents,\n currency: row.currency,\n minSubtotalCents: row.minSubtotalCents,\n startsAt: row.startsAt?.toISOString() ?? null,\n endsAt: row.endsAt?.toISOString() ?? null,\n maxRedemptions: row.maxRedemptions,\n active: row.active,\n redeemedCount: row.redeemedCount,\n reservedCount: row.reservedCount,\n createdAt: row.createdAt.toISOString(),\n };\n}\n\nfunction decodeDiscountCursor(raw: string): { createdAt: string; id: string } {\n const { at, key } = decodeCursor(\n raw,\n (c) => isDiscountId(c.key) && isCursorTimestamp(c.at),\n );\n return { createdAt: at, id: key };\n}\n\nexport async function listAdminDiscounts(\n db: CommerceDb,\n query: AdminDiscountListQuery,\n now: Date = new Date(),\n): Promise<AdminDiscountPage> {\n const rows = await selectDiscountPage(db, {\n status: query.status,\n q: query.q,\n now,\n after: query.cursor ? decodeDiscountCursor(query.cursor) : undefined,\n limit: query.limit + 1,\n });\n const page = rows.slice(0, query.limit);\n const last = page[page.length - 1];\n return {\n items: page.map(toDiscount),\n nextCursor:\n rows.length > query.limit && last\n ? encodeCursor({ at: last.createdAtKey, key: last.id })\n : null,\n };\n}\n\nexport async function getAdminDiscount(\n db: CommerceDb,\n id: string,\n): Promise<Discount> {\n const row = await findDiscountById(db, id);\n if (!row) throw new DiscountNotFoundError(id);\n return toDiscount(row);\n}\n\nexport async function createDiscount(\n db: CommerceDb,\n input: unknown,\n): Promise<Discount> {\n const data = discountInputSchema.parse(input);\n const id = await insertDiscount(db, {\n code: data.code,\n type: data.type,\n percentOff: data.percentOff ?? null,\n amountOffCents: data.amountOffCents ?? null,\n currency: data.currency ?? null,\n minSubtotalCents: data.minSubtotalCents ?? null,\n startsAt: parseDate(data.startsAt) ?? null,\n endsAt: parseDate(data.endsAt) ?? null,\n maxRedemptions: data.maxRedemptions ?? null,\n active: data.active,\n });\n if (!id) throw new DiscountCodeTakenError(data.code);\n return getAdminDiscount(db, id);\n}\n\nfunction sameValue(a: unknown, b: unknown): boolean {\n if (typeof a === 'string' && typeof b === 'string') {\n return a.toUpperCase() === b.toUpperCase();\n }\n return a === b;\n}\n\nfunction changedLockedFields(\n row: DiscountWithCounts,\n patch: ParsedPatch,\n): string[] {\n return LOCKED_AFTER_USE.filter(\n (field) =>\n patch[field] !== undefined && !sameValue(patch[field], row[field]),\n );\n}\n\nfunction assertCoherent(row: DiscountWithCounts, patch: ParsedPatch): void {\n const merged = discountInputSchema.safeParse({\n code: row.code,\n type: row.type,\n percentOff: row.percentOff,\n amountOffCents: row.amountOffCents,\n currency: row.currency,\n ...patch,\n });\n if (!merged.success) {\n throw new InvalidDiscountError(\n merged.error.issues[0]?.message ?? 'Invalid',\n );\n }\n}\n\nexport async function patchDiscount(\n db: CommerceDb,\n id: string,\n rawPatch: unknown,\n): Promise<Discount> {\n const patch = discountPatchSchema.parse(rawPatch);\n return db.transaction(async (tx) => {\n const row = await findDiscountById(tx, id, { forUpdate: true });\n if (!row) throw new DiscountNotFoundError(id);\n const usage = row.redeemedCount + row.reservedCount;\n const locked = changedLockedFields(row, patch);\n if (row.totalRedemptions > 0 && locked.length > 0)\n throw new DiscountInUseError(locked);\n if (\n typeof patch.maxRedemptions === 'number' &&\n patch.maxRedemptions < usage\n ) {\n throw new MaxRedemptionsBelowUsageError(usage);\n }\n assertCoherent(row, patch);\n try {\n await updateDiscount(tx, id, {\n code: patch.code,\n type: patch.type,\n percentOff: patch.percentOff,\n amountOffCents: patch.amountOffCents,\n currency: patch.currency,\n minSubtotalCents: patch.minSubtotalCents,\n startsAt: parseDate(patch.startsAt),\n endsAt: parseDate(patch.endsAt),\n maxRedemptions: patch.maxRedemptions,\n active: patch.active,\n });\n } catch (err) {\n const cause = (err as { cause?: { code?: string } }).cause;\n if ((cause ?? (err as { code?: string })).code === '23505') {\n throw new DiscountCodeTakenError(patch.code ?? row.code);\n }\n throw err;\n }\n const updated = await findDiscountById(tx, id);\n return toDiscount(updated!);\n });\n}\n","import type { ChosenShipping, ShippingAddress } from '../payment/gateway';\n\nexport interface DevBuyer {\n email?: string;\n name?: string;\n address?: ShippingAddress;\n}\n\nexport type DevBuyerSetting = DevBuyer | ((sessionId: string) => DevBuyer);\n\nexport interface DevCompletionDetails {\n email: string;\n shippingAddress: ShippingAddress;\n shipping?: ChosenShipping;\n}\n\nexport const DEFAULT_DEV_BUYER = {\n email: 'dev-buyer@example.com',\n name: 'Dev Buyer',\n address: {\n line1: '123 Example Street',\n city: 'Springfield',\n state: 'IL',\n postalCode: '62701',\n country: 'US',\n },\n} satisfies Required<DevBuyer>;\n\nexport function resolveDevCompletion(\n setting: DevBuyerSetting | undefined,\n sessionId: string,\n firstRate: ChosenShipping | undefined,\n): DevCompletionDetails {\n const buyer = typeof setting === 'function' ? setting(sessionId) : setting;\n const name = buyer?.name ?? DEFAULT_DEV_BUYER.name;\n return {\n email: buyer?.email ?? DEFAULT_DEV_BUYER.email,\n shippingAddress: { name, ...(buyer?.address ?? DEFAULT_DEV_BUYER.address) },\n shipping: firstRate && {\n label: firstRate.label,\n amountCents: firstRate.amountCents,\n },\n };\n}\n","import type { FastifyPluginAsync } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport type { FakeGateway } from '../payment/fake-gateway';\nimport { resolveDevCompletion, type DevBuyerSetting } from './dev-buyer';\nimport { handlePaymentEvent } from './order-service';\nimport type { CheckoutUrls, OrderHooks } from './types';\n\nexport interface DevCheckoutOptions {\n db: CommerceDb;\n payment: FakeGateway;\n urls: CheckoutUrls;\n hooks?: OrderHooks;\n buyer?: DevBuyerSetting;\n}\n\nexport function assertDevCheckoutAllowed(): void {\n if (process.env.NODE_ENV === 'production') {\n throw new Error('devCheckout is not allowed when NODE_ENV=production');\n }\n}\n\nexport const devCheckoutRoutes: FastifyPluginAsync<DevCheckoutOptions> = async (\n app,\n { db, payment, urls, hooks, buyer },\n) => {\n assertDevCheckoutAllowed();\n\n app.get<{\n Params: { sessionId: string };\n Querystring: { outcome?: string };\n }>('/checkout/dev/:sessionId/complete', async (req, reply) => {\n const { sessionId } = req.params;\n const session = payment.sessions.find((s) => s.id === sessionId);\n if (!session) {\n return reply.code(404).send({ error: 'SESSION_NOT_FOUND' });\n }\n const expire = req.query.outcome === 'expired';\n const { rawBody, signature } = expire\n ? payment.expired(sessionId)\n : payment.completed(\n sessionId,\n resolveDevCompletion(buyer, sessionId, session.shippingRates[0]),\n );\n const event = await payment.verifyWebhook(rawBody, signature);\n await handlePaymentEvent(db, event, hooks, req.log);\n return reply.redirect(expire ? urls.cancel : urls.success(sessionId), 302);\n });\n};\n","import {\n checkoutConflictSchema,\n discountPreviewRequestSchema,\n discountPreviewSchema,\n discountRejectionSchema,\n checkoutRequestSchema,\n checkoutResponseSchema,\n orderSummarySchema,\n} from '@develemit/commerce-contracts';\nimport type { FastifyPluginAsync } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport { DiscountRejectedError } from '../discounts/redemptions';\nimport { previewDiscount } from '../discounts/service';\nimport { createCheckout } from './checkout-service';\nimport { getOrderSummary } from './order-service';\nimport {\n CheckoutConflictError,\n OrderNotFoundError,\n PaymentGatewayError,\n type CheckoutSettings,\n} from './types';\nimport { webhookRoutes } from './webhook-routes';\n\nexport interface OrdersPluginOptions extends CheckoutSettings {\n db: CommerceDb;\n}\n\nexport const ordersPlugin: FastifyPluginAsync<OrdersPluginOptions> = async (\n app,\n { db, ...settings },\n) => {\n app.post('/checkout', async (req, reply) => {\n const parsed = checkoutRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n return reply.code(400).send({ error: 'INVALID_CHECKOUT_REQUEST' });\n }\n try {\n return checkoutResponseSchema.parse(\n await createCheckout(db, settings, parsed.data),\n );\n } catch (err) {\n if (err instanceof CheckoutConflictError) {\n return reply.code(409).send(\n checkoutConflictSchema.parse({\n unavailableProductIds: err.productIds,\n }),\n );\n }\n if (err instanceof DiscountRejectedError) {\n return reply.code(422).send(\n discountRejectionSchema.parse({\n error: err.code,\n reason: err.reason,\n }),\n );\n }\n if (err instanceof PaymentGatewayError) {\n req.log.error({ err: err.cause }, err.message);\n return reply.code(502).send({ error: err.code });\n }\n throw err;\n }\n });\n\n app.post('/checkout/discount/preview', async (req, reply) => {\n const parsed = discountPreviewRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n return reply.code(400).send({ error: 'INVALID_DISCOUNT_REQUEST' });\n }\n reply.header('Cache-Control', 'no-store');\n const result = await previewDiscount(db, parsed.data, settings.currency);\n if (!result.ok) {\n return reply.code(422).send(\n discountRejectionSchema.parse({\n error: 'DISCOUNT_INVALID',\n reason: result.reason,\n }),\n );\n }\n return discountPreviewSchema.parse(result.preview);\n });\n\n app.get<{ Params: { sessionId: string } }>(\n '/orders/session/:sessionId',\n async (req, reply) => {\n try {\n return orderSummarySchema.parse(\n await getOrderSummary(db, req.params.sessionId),\n );\n } catch (err) {\n if (err instanceof OrderNotFoundError) {\n return reply.code(404).send({ error: err.code });\n }\n throw err;\n }\n },\n );\n\n await app.register(webhookRoutes, {\n db,\n payment: settings.payment,\n hooks: settings.hooks,\n prefix: '/orders/webhooks',\n });\n};\n","import type {\n DiscountPreview,\n DiscountPreviewRequest,\n DiscountRejectionReason,\n} from '@develemit/commerce-contracts';\nimport { findPurchasable } from '../catalog/service';\nimport type { CommerceDb } from '../db/types';\nimport { evaluateDiscount } from './evaluate';\nimport { countActiveRedemptions, findDiscountByCode } from './repo';\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport type PreviewResult =\n | { ok: true; preview: DiscountPreview }\n | { ok: false; reason: DiscountRejectionReason };\n\nasync function availableSubtotal(\n db: CommerceDb,\n items: DiscountPreviewRequest['items'],\n currency: string,\n): Promise<number> {\n const quantities = new Map<string, number>();\n for (const { productId, quantity } of items) {\n quantities.set(productId, (quantities.get(productId) ?? 0) + quantity);\n }\n const found = await findPurchasable(\n db,\n [...quantities.keys()].filter((id) => UUID.test(id)),\n );\n return found\n .filter((p) => p.currency === currency)\n .reduce((sum, p) => sum + p.priceCents * quantities.get(p.id)!, 0);\n}\n\nexport async function previewDiscount(\n db: CommerceDb,\n request: DiscountPreviewRequest,\n currency: string,\n now: Date = new Date(),\n): Promise<PreviewResult> {\n const code = request.code.trim().toUpperCase();\n const discount = await findDiscountByCode(db, code);\n const subtotalCents = await availableSubtotal(db, request.items, currency);\n const result = evaluateDiscount(discount, {\n subtotalCents,\n currency,\n now,\n activeRedemptions: discount\n ? await countActiveRedemptions(db, discount.id)\n : 0,\n });\n if (!result.ok) {\n return {\n ok: false,\n reason: result.reason === 'inactive' ? 'not_found' : result.reason,\n };\n }\n return {\n ok: true,\n preview: {\n code,\n discountCents: result.discountCents,\n subtotalCents,\n currency,\n },\n };\n}\n","import { randomUUID } from 'node:crypto';\nimport type { CheckoutRequest } from '@develemit/commerce-contracts';\nimport { findPurchasable } from '../catalog/service';\nimport type { CommerceDb } from '../db/types';\nimport {\n evaluateForCheckout,\n releaseRedemptions,\n reserveRedemption,\n type AppliedDiscount,\n} from '../discounts/redemptions';\nimport {\n InsufficientStockError,\n release,\n reserve,\n type StockRequest,\n} from '../inventory/service';\nimport { listActiveRates } from '../shipping/repo';\nimport { SESSION_ID_PLACEHOLDER, type CheckoutLine } from '../payment/gateway';\nimport { insertOrder, setCheckoutSession, setOrderStatus } from './repo';\nimport {\n CheckoutConflictError,\n PaymentGatewayError,\n type CheckoutSettings,\n} from './types';\n\n// Stripe rejects expires_at under 30 minutes from its own clock, so the\n// session (and the hold that matches it) gets a minute of slack.\nexport const HOLD_DURATION_MS = 31 * 60 * 1000;\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nfunction mergeItems(items: CheckoutRequest['items']): StockRequest[] {\n const merged = new Map<string, number>();\n for (const { productId, quantity } of items) {\n merged.set(productId, (merged.get(productId) ?? 0) + quantity);\n }\n return [...merged].map(([productId, quantity]) => ({ productId, quantity }));\n}\n\nasync function priceLines(\n db: CommerceDb,\n requests: StockRequest[],\n currency: string,\n): Promise<CheckoutLine[]> {\n const ids = requests.map((r) => r.productId);\n const found = await findPurchasable(\n db,\n ids.filter((id) => UUID.test(id)),\n );\n const byId = new Map(found.map((p) => [p.id, p]));\n const unavailable = ids.filter((id) => byId.get(id)?.currency !== currency);\n if (unavailable.length > 0) throw new CheckoutConflictError(unavailable);\n return requests.map(({ productId, quantity }) => {\n const product = byId.get(productId)!;\n return {\n productId,\n name: product.name,\n unitAmountCents: product.priceCents,\n quantity,\n };\n });\n}\n\ninterface OrderDraft {\n orderId: string;\n lines: CheckoutLine[];\n currency: string;\n expiresAt: Date;\n discountCode?: string;\n}\n\nasync function reserveOrder(\n db: CommerceDb,\n { orderId, lines, currency, expiresAt, discountCode }: OrderDraft,\n): Promise<AppliedDiscount | undefined> {\n const subtotalCents = lines.reduce(\n (sum, l) => sum + l.unitAmountCents * l.quantity,\n 0,\n );\n try {\n return await db.transaction(async (tx) => {\n await reserve(tx, { reference: orderId, items: lines, expiresAt });\n const discount =\n discountCode === undefined\n ? undefined\n : await evaluateForCheckout(tx, {\n code: discountCode,\n subtotalCents,\n currency,\n });\n await insertOrder(\n tx,\n {\n id: orderId,\n currency,\n subtotalCents,\n discountCents: discount?.discountCents ?? 0,\n discountCode: discount?.code ?? null,\n },\n lines.map((l) => ({\n productId: l.productId,\n name: l.name,\n priceCents: l.unitAmountCents,\n quantity: l.quantity,\n })),\n );\n if (discount) await reserveRedemption(tx, orderId, discount);\n return discount;\n });\n } catch (err) {\n if (err instanceof InsufficientStockError) {\n throw new CheckoutConflictError(err.productIds);\n }\n throw err;\n }\n}\n\nexport async function createCheckout(\n db: CommerceDb,\n settings: CheckoutSettings,\n request: CheckoutRequest,\n): Promise<{ url: string }> {\n const lines = await priceLines(\n db,\n mergeItems(request.items),\n settings.currency,\n );\n const shippingRates = await listActiveRates(db, settings.currency);\n const orderId = randomUUID();\n const expiresAt = new Date(Date.now() + HOLD_DURATION_MS);\n const discount = await reserveOrder(db, {\n orderId,\n lines,\n currency: settings.currency,\n expiresAt,\n discountCode: request.discountCode,\n });\n\n let session;\n try {\n session = await settings.payment.createCheckoutSession({\n reference: orderId,\n currency: settings.currency,\n lines,\n successUrl: settings.urls.success(SESSION_ID_PLACEHOLDER),\n cancelUrl: settings.urls.cancel,\n shippingCountries: settings.shippingCountries,\n shippingRates,\n expiresAt,\n ...(discount &&\n discount.discountCents > 0 && {\n discount: {\n code: discount.code,\n amountCents: discount.discountCents,\n },\n }),\n });\n } catch (err) {\n await release(db, orderId);\n await releaseRedemptions(db, [orderId]);\n await setOrderStatus(db, orderId, 'cancelled');\n throw new PaymentGatewayError(err);\n }\n await setCheckoutSession(db, orderId, session.id);\n return { url: session.url };\n}\n","import type { FastifyPluginAsync } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport { WebhookSignatureError, type PaymentGateway } from '../payment/gateway';\nimport { handlePaymentEvent } from './order-service';\nimport type { OrderHooks } from './types';\n\nexport interface WebhookRoutesOptions {\n db: CommerceDb;\n payment: PaymentGateway;\n hooks?: OrderHooks;\n}\n\n// Signature checks need the exact bytes Stripe sent, so this encapsulated\n// context swaps the JSON parser for a raw buffer.\nexport const webhookRoutes: FastifyPluginAsync<WebhookRoutesOptions> = async (\n app,\n { db, payment, hooks },\n) => {\n app.removeAllContentTypeParsers();\n app.addContentTypeParser('*', { parseAs: 'buffer' }, (_req, body, done) =>\n done(null, body),\n );\n\n app.post('/stripe', async (req, reply) => {\n const header = req.headers['stripe-signature'];\n const signature = typeof header === 'string' ? header : undefined;\n const rawBody = Buffer.isBuffer(req.body) ? req.body : Buffer.alloc(0);\n let event;\n try {\n event = await payment.verifyWebhook(rawBody, signature);\n } catch (err) {\n if (err instanceof WebhookSignatureError) {\n return reply.code(400).send({ error: err.code });\n }\n throw err;\n }\n const outcome = await handlePaymentEvent(db, event, hooks, req.log);\n return { received: true, outcome };\n });\n};\n","import { createHmac, randomUUID, timingSafeEqual } from 'node:crypto';\nimport {\n SESSION_ID_PLACEHOLDER,\n RefundRejectedError,\n WebhookSignatureError,\n type ChosenShipping,\n type CreateCheckoutSessionInput,\n type GatewayRefundEvent,\n type PaymentEvent,\n type PaymentGateway,\n type RefundInput,\n type ShippingAddress,\n} from './gateway';\n\nexport interface SignedWebhook {\n rawBody: Buffer;\n signature: string;\n}\n\nexport interface FakeGateway extends PaymentGateway {\n readonly kind: 'fake';\n useSessionUrl(build: (sessionId: string) => string): void;\n readonly sessions: (CreateCheckoutSessionInput & { id: string })[];\n readonly refunds: (RefundInput & { gatewayRefundId: string })[];\n readonly refundCalls: RefundInput[];\n failNextSession(error?: Error): void;\n failNextRefund(error?: Error): void;\n failNextRefundUncertain(error?: Error): void;\n completed(\n sessionId: string,\n details?: {\n email?: string;\n shippingAddress?: ShippingAddress;\n shipping?: ChosenShipping;\n paymentPending?: boolean;\n },\n ): SignedWebhook;\n asyncPaid(sessionId: string): SignedWebhook;\n asyncFailed(sessionId: string): SignedWebhook;\n expired(sessionId: string): SignedWebhook;\n refunded(sessionId: string, refunds: GatewayRefundEvent[]): SignedWebhook;\n sign(event: PaymentEvent): SignedWebhook;\n}\n\nexport interface FakeGatewayOptions {\n secret?: string;\n}\n\nexport function createFakeGateway({\n secret = 'fake-webhook-secret',\n}: FakeGatewayOptions = {}): FakeGateway {\n const sessions: FakeGateway['sessions'] = [];\n const refunds: FakeGateway['refunds'] = [];\n const refundCalls: RefundInput[] = [];\n let pendingFailure: Error | undefined;\n let sessionUrl: ((sessionId: string) => string) | undefined;\n let pendingRefundFailure: Error | undefined;\n\n const hmac = (body: Buffer) =>\n createHmac('sha256', secret).update(body).digest('hex');\n\n function sign(event: PaymentEvent): SignedWebhook {\n const rawBody = Buffer.from(JSON.stringify(event));\n return { rawBody, signature: hmac(rawBody) };\n }\n\n return {\n kind: 'fake',\n useSessionUrl(build) {\n sessionUrl = build;\n },\n sessions,\n refunds,\n refundCalls,\n async createCheckoutSession(input) {\n if (pendingFailure) {\n const err = pendingFailure;\n pendingFailure = undefined;\n throw err;\n }\n const id = `fake_cs_${randomUUID()}`;\n sessions.push({ ...input, id });\n return {\n id,\n url:\n sessionUrl?.(id) ??\n input.successUrl.replace(SESSION_ID_PLACEHOLDER, id),\n };\n },\n async verifyWebhook(rawBody, signature) {\n const expected = Buffer.from(hmac(rawBody));\n const given = Buffer.from(signature ?? '');\n if (\n given.length !== expected.length ||\n !timingSafeEqual(given, expected)\n ) {\n throw new WebhookSignatureError();\n }\n return JSON.parse(rawBody.toString('utf8')) as PaymentEvent;\n },\n async refund(input) {\n refundCalls.push(input);\n if (pendingRefundFailure) {\n const err = pendingRefundFailure;\n pendingRefundFailure = undefined;\n throw err;\n }\n const prior = refunds.find(\n (r) => r.idempotencyKey === input.idempotencyKey,\n );\n if (prior) return { gatewayRefundId: prior.gatewayRefundId };\n const gatewayRefundId = `fake_re_${refunds.length + 1}_${input.sessionId}`;\n refunds.push({ ...input, gatewayRefundId });\n return { gatewayRefundId };\n },\n failNextSession(error = new Error('Fake gateway failure')) {\n pendingFailure = error;\n },\n failNextRefund(error = new RefundRejectedError('Fake refund failure')) {\n pendingRefundFailure = error;\n },\n failNextRefundUncertain(error = new Error('Fake refund timeout')) {\n pendingRefundFailure = error;\n },\n completed(sessionId, details = {}) {\n return sign({\n id: `fake_evt_${randomUUID()}`,\n type: 'checkout.completed',\n sessionId,\n ...details,\n });\n },\n asyncPaid(sessionId) {\n return sign({\n id: `fake_evt_${randomUUID()}`,\n type: 'checkout.async_paid',\n sessionId,\n });\n },\n asyncFailed(sessionId) {\n return sign({\n id: `fake_evt_${randomUUID()}`,\n type: 'checkout.async_failed',\n sessionId,\n });\n },\n expired(sessionId) {\n return sign({\n id: `fake_evt_${randomUUID()}`,\n type: 'checkout.expired',\n sessionId,\n });\n },\n refunded(sessionId, refunds) {\n return sign({\n id: `fake_evt_${randomUUID()}`,\n type: 'payment.refunded',\n sessionId,\n refunds,\n });\n },\n sign,\n };\n}\n\nexport function isFakeGateway(gateway: PaymentGateway): gateway is FakeGateway {\n return (gateway as Partial<FakeGateway>).kind === 'fake';\n}\n","import type { FastifyPluginAsync } from 'fastify';\nimport { adminPlugin, type AdminOptions } from './admin/routes';\nimport { catalogPlugin } from './catalog/routes';\nimport type { CatalogCacheOptions } from './catalog/cache-hooks';\nimport type { CategoryLabels } from './catalog/types';\nimport type { CommerceDb } from './db/types';\nimport { adminOrdersPlugin } from './orders/admin-routes';\nimport { adminDiscountsPlugin } from './discounts/admin-routes';\nimport {\n assertDevCheckoutAllowed,\n devCheckoutRoutes,\n} from './orders/dev-checkout';\nimport { ordersPlugin } from './orders/routes';\nimport type { CheckoutUrls, OrderHooks } from './orders/types';\nimport type { DevBuyerSetting } from './orders/dev-buyer';\nimport { isFakeGateway } from './payment/fake-gateway';\nimport type { PaymentGateway } from './payment/gateway';\n\ninterface CheckoutOptions {\n payment: PaymentGateway;\n urls: CheckoutUrls;\n shippingCountries?: string[];\n hooks?: OrderHooks;\n devCheckout?: boolean | DevCheckoutSetting;\n}\n\nexport interface DevCheckoutSetting {\n apiUrl?: string;\n buyer?: DevBuyerSetting;\n}\n\ninterface CatalogOnlyOptions {\n payment?: undefined;\n urls?: undefined;\n shippingCountries?: undefined;\n hooks?: undefined;\n devCheckout?: undefined;\n}\n\nexport type CommercePluginOptions = {\n db: CommerceDb;\n currency: string;\n admin?: AdminOptions;\n categoryLabels?: CategoryLabels;\n catalogCache?: CatalogCacheOptions;\n} & (CheckoutOptions | CatalogOnlyOptions);\n\nexport const commercePlugin: FastifyPluginAsync<CommercePluginOptions> = async (\n app,\n {\n db,\n currency,\n payment,\n urls,\n shippingCountries = ['US'],\n hooks,\n admin,\n categoryLabels,\n catalogCache,\n devCheckout,\n },\n) => {\n app.get('/health', async () => ({ status: 'ok' }));\n await app.register(catalogPlugin, {\n db,\n categoryLabels,\n catalogCache,\n prefix: '/catalog',\n });\n if (admin) {\n await app.register(adminPlugin, {\n db,\n currency,\n ...admin,\n prefix: '/admin',\n });\n }\n if (!payment || !urls) return;\n await payment.preflight?.();\n await app.register(ordersPlugin, {\n db,\n currency,\n payment,\n urls,\n shippingCountries,\n hooks,\n });\n if (devCheckout) {\n assertDevCheckoutAllowed();\n if (isFakeGateway(payment)) {\n const apiUrl = typeof devCheckout === 'object' ? devCheckout.apiUrl : '';\n payment.useSessionUrl(\n (id) => `${apiUrl ?? ''}${app.prefix}/checkout/dev/${id}/complete`,\n );\n const buyer =\n typeof devCheckout === 'object' ? devCheckout.buyer : undefined;\n await app.register(devCheckoutRoutes, {\n db,\n payment,\n urls,\n hooks,\n buyer,\n });\n }\n }\n if (admin) {\n await app.register(adminOrdersPlugin, {\n db,\n authorize: admin.authorize,\n payment,\n hooks,\n prefix: '/admin/orders',\n });\n await app.register(adminDiscountsPlugin, {\n db,\n authorize: admin.authorize,\n prefix: '/admin/discounts',\n });\n }\n};\n","import type Stripe from 'stripe';\n\n// stripe is an optional peer, loaded on first use so hosts that bring their\n// own gateway never need it installed.\nexport async function loadStripe(secretKey: string): Promise<Stripe> {\n try {\n const { default: StripeClient } = await import('stripe');\n return new StripeClient(secretKey);\n } catch (cause) {\n throw new Error(\n '@develemit/commerce-server: STRIPE_SECRET_KEY is set but the optional peer \"stripe\" is not installed. Run: pnpm add stripe',\n { cause },\n );\n }\n}\n","import type Stripe from 'stripe';\nimport {\n RefundRejectedError,\n WebhookSignatureError,\n type CreateCheckoutSessionInput,\n type ChosenShipping,\n type GatewayRefundEvent,\n type PaymentEvent,\n type PaymentGateway,\n type ShippingAddress,\n} from './gateway';\nimport { loadStripe } from './stripe-loader';\n\nexport interface StripeGatewayOptions {\n secretKey: string;\n webhookSecret: string;\n client?: Stripe;\n}\n\nconst REFUND_KEY_METADATA = 'commerceRefundKey';\n\nfunction rejectedRefund(err: unknown): unknown {\n if (err instanceof RefundRejectedError) return err;\n const status = (err as { statusCode?: unknown } | null)?.statusCode;\n if (typeof status === 'number' && status >= 400 && status < 500) {\n return new RefundRejectedError((err as Error).message, { cause: err });\n }\n return err;\n}\n\ntype StripeAddress = Stripe.Address | null | undefined;\n\nfunction toShippingAddress(\n name: string | null | undefined,\n address: StripeAddress,\n): ShippingAddress | undefined {\n if (!address) return undefined;\n const fields: ShippingAddress = {\n name: name ?? undefined,\n line1: address.line1 ?? undefined,\n line2: address.line2 ?? undefined,\n city: address.city ?? undefined,\n state: address.state ?? undefined,\n postalCode: address.postal_code ?? undefined,\n country: address.country ?? undefined,\n };\n return Object.fromEntries(\n Object.entries(fields).filter(([, v]) => v !== undefined),\n );\n}\n\nfunction toShippingOption(\n rate: CreateCheckoutSessionInput['shippingRates'][number],\n currency: string,\n): Stripe.Checkout.SessionCreateParams.ShippingOption {\n const days = (value: number) => ({ unit: 'business_day' as const, value });\n return {\n shipping_rate_data: {\n type: 'fixed_amount',\n display_name: rate.label,\n fixed_amount: { amount: rate.amountCents, currency },\n ...(rate.minDays !== undefined &&\n rate.maxDays !== undefined && {\n delivery_estimate: {\n minimum: days(rate.minDays),\n maximum: days(rate.maxDays),\n },\n }),\n },\n };\n}\n\nfunction toChosenShipping(\n session: Stripe.Checkout.Session,\n): ChosenShipping | undefined {\n const cost = session.shipping_cost;\n const rate = cost?.shipping_rate;\n if (!cost || !rate || typeof rate === 'string') return undefined;\n return {\n label: rate.display_name ?? 'Shipping',\n amountCents: cost.amount_total,\n };\n}\n\nexport function toPaymentEvent(\n event: Stripe.Event,\n expanded?: Stripe.Checkout.Session,\n): PaymentEvent {\n if (event.type === 'checkout.session.completed') {\n const session = event.data.object;\n const shipping = session.collected_information?.shipping_details;\n return {\n id: event.id,\n type: 'checkout.completed',\n sessionId: session.id,\n email: session.customer_details?.email ?? undefined,\n shippingAddress: toShippingAddress(shipping?.name, shipping?.address),\n shipping: expanded && toChosenShipping(expanded),\n paymentPending: session.payment_status === 'unpaid',\n };\n }\n if (event.type === 'checkout.session.async_payment_succeeded') {\n return {\n id: event.id,\n type: 'checkout.async_paid',\n sessionId: event.data.object.id,\n };\n }\n if (event.type === 'checkout.session.async_payment_failed') {\n return {\n id: event.id,\n type: 'checkout.async_failed',\n sessionId: event.data.object.id,\n };\n }\n if (event.type === 'checkout.session.expired') {\n return {\n id: event.id,\n type: 'checkout.expired',\n sessionId: event.data.object.id,\n };\n }\n return { id: event.id, type: 'ignored', sourceType: event.type };\n}\n\nfunction toRefundEvent(refund: Stripe.Refund): GatewayRefundEvent {\n return {\n gatewayRefundId: refund.id,\n amountCents: refund.amount,\n idempotencyKey: refund.metadata?.[REFUND_KEY_METADATA] || undefined,\n };\n}\n\n// Newer Stripe API versions leave refunds off the charge, so list them.\nasync function refundedEvent(\n stripe: Stripe,\n event: Stripe.ChargeRefundedEvent,\n): Promise<PaymentEvent> {\n const charge = event.data.object;\n const intent = charge.payment_intent;\n const ignored: PaymentEvent = {\n id: event.id,\n type: 'ignored',\n sourceType: event.type,\n };\n if (!intent) return ignored;\n const { data: sessions } = await stripe.checkout.sessions.list({\n payment_intent: typeof intent === 'string' ? intent : intent.id,\n limit: 1,\n });\n if (sessions.length === 0) return ignored;\n const refunds =\n charge.refunds?.data ??\n (await stripe.refunds.list({ charge: charge.id, limit: 100 })).data;\n return {\n id: event.id,\n type: 'payment.refunded',\n sessionId: sessions[0].id,\n refunds: refunds\n .filter((refund) => refund.status !== 'failed')\n .map(toRefundEvent),\n };\n}\n\n// A one-off coupon for the exact amount the kit computed, so Stripe displays\n// and applies the discount without owning any of its rules. Stripe applies\n// amount_off to line items only, leaving shipping undiscounted.\nfunction createCoupon(\n stripe: Stripe,\n { discount, currency, expiresAt }: CreateCheckoutSessionInput,\n): Promise<Stripe.Coupon> {\n return stripe.coupons.create({\n amount_off: discount!.amountCents,\n currency: currency.toLowerCase(),\n duration: 'once',\n max_redemptions: 1,\n redeem_by: Math.floor(expiresAt.getTime() / 1000),\n name: discount!.code,\n });\n}\n\nexport function createStripeGateway({\n secretKey,\n webhookSecret,\n client,\n}: StripeGatewayOptions): PaymentGateway {\n let stripe: Promise<Stripe> | undefined = client\n ? Promise.resolve(client)\n : undefined;\n const getStripe = () => (stripe ??= loadStripe(secretKey));\n\n return {\n async preflight() {\n await getStripe();\n },\n async createCheckoutSession(input) {\n const stripeClient = await getStripe();\n const coupon =\n input.discount && (await createCoupon(stripeClient, input));\n const session = await stripeClient.checkout.sessions.create({\n mode: 'payment',\n client_reference_id: input.reference,\n metadata: { reference: input.reference },\n line_items: input.lines.map((line) => ({\n quantity: line.quantity,\n price_data: {\n currency: input.currency.toLowerCase(),\n unit_amount: line.unitAmountCents,\n product_data: {\n name: line.name,\n metadata: { productId: line.productId },\n },\n },\n })),\n ...(coupon && { discounts: [{ coupon: coupon.id }] }),\n success_url: input.successUrl,\n cancel_url: input.cancelUrl,\n expires_at: Math.floor(input.expiresAt.getTime() / 1000),\n ...(input.shippingRates.length > 0 && {\n shipping_options: input.shippingRates.map((rate) =>\n toShippingOption(rate, input.currency.toLowerCase()),\n ),\n }),\n shipping_address_collection: {\n allowed_countries:\n input.shippingCountries as Stripe.Checkout.SessionCreateParams.ShippingAddressCollection.AllowedCountry[],\n },\n });\n if (!session.url) throw new Error('Stripe returned no checkout URL');\n return { id: session.id, url: session.url };\n },\n async verifyWebhook(rawBody, signature) {\n const stripeClient = await getStripe();\n let event: Stripe.Event;\n try {\n event = stripeClient.webhooks.constructEvent(\n rawBody,\n signature ?? '',\n webhookSecret,\n );\n } catch (err) {\n throw new WebhookSignatureError((err as Error).message);\n }\n if (event.type === 'charge.refunded') {\n return refundedEvent(stripeClient, event);\n }\n if (event.type !== 'checkout.session.completed') {\n return toPaymentEvent(event);\n }\n const session = await stripeClient.checkout.sessions.retrieve(\n event.data.object.id,\n { expand: ['shipping_cost.shipping_rate'] },\n );\n return toPaymentEvent(event, session);\n },\n async refund({ sessionId, amountCents, idempotencyKey }) {\n try {\n const stripeClient = await getStripe();\n const session =\n await stripeClient.checkout.sessions.retrieve(sessionId);\n const intent = session.payment_intent;\n if (!intent) {\n throw new RefundRejectedError(`Session ${sessionId} has no payment`);\n }\n const refund = await stripeClient.refunds.create(\n {\n payment_intent: typeof intent === 'string' ? intent : intent.id,\n amount: amountCents,\n metadata: { [REFUND_KEY_METADATA]: idempotencyKey },\n },\n { idempotencyKey },\n );\n return { gatewayRefundId: refund.id };\n } catch (err) {\n throw rejectedRefund(err);\n }\n },\n };\n}\n","import type { CommerceDb } from './db/types';\nimport { releaseRedemptions } from './discounts/redemptions';\nimport { releaseExpiredReferences } from './inventory/service';\n\nexport interface HoldSweeperOptions {\n intervalMs?: number;\n onError?: (err: unknown) => void;\n}\n\nexport interface HoldSweeper {\n stop(): void;\n}\n\n// An order whose holds lapsed gives its discount slot back with them.\nexport async function sweepExpiredHolds(db: CommerceDb): Promise<void> {\n await db.transaction(async (tx) => {\n await releaseRedemptions(tx, await releaseExpiredReferences(tx));\n });\n}\n\nexport function startHoldSweeper(\n db: CommerceDb,\n { intervalMs = 60_000, onError }: HoldSweeperOptions = {},\n): HoldSweeper {\n const timer = setInterval(() => {\n sweepExpiredHolds(db).catch((err) => onError?.(err));\n }, intervalMs);\n timer.unref();\n return { stop: () => clearInterval(timer) };\n}\n","import {\n formatOrderRef,\n type OrderDetail,\n} from '@develemit/commerce-contracts';\n\nexport interface EmailLine {\n label: string;\n amount: string;\n}\n\nexport interface OrderEmailLabels {\n subtotal: string;\n discount: (code: string | null) => string;\n shipping: (label: string | null) => string;\n total: string;\n}\n\nexport interface OrderEmailLinesOptions {\n locale?: string;\n labels?: Partial<OrderEmailLabels>;\n}\n\nexport interface OrderEmailLinesText {\n items: string;\n totals: string;\n address: string;\n}\n\nexport interface OrderEmailLinesHtml {\n items: string;\n totals: string;\n address: string;\n}\n\nexport interface OrderEmailLines {\n reference: string;\n items: EmailLine[];\n subtotal: EmailLine;\n discount?: EmailLine;\n shipping: EmailLine;\n total: EmailLine;\n address?: string[];\n text: OrderEmailLinesText;\n html: OrderEmailLinesHtml;\n}\n\nconst defaultLabels: OrderEmailLabels = {\n subtotal: 'Subtotal',\n discount: (code) => (code ? `Discount (${code})` : 'Discount'),\n shipping: (label) => (label ? `Shipping (${label})` : 'Shipping'),\n total: 'Total',\n};\n\nconst escapes: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n};\n\nexport function escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (c) => escapes[c]);\n}\n\nfunction addressLines(order: OrderDetail): string[] | undefined {\n const a = order.shippingAddress;\n if (!a) return undefined;\n const lines = [\n a.name,\n a.line1,\n a.line2,\n [a.city, a.state, a.postalCode].filter(Boolean).join(' '),\n a.country,\n ].filter((l): l is string => Boolean(l));\n return lines.length ? lines : undefined;\n}\n\nfunction textBlock(lines: EmailLine[]): string {\n return lines.map((l) => `${l.label} ${l.amount}`).join('\\n');\n}\n\nfunction htmlRows(lines: EmailLine[]): string {\n return lines\n .map(\n (l) =>\n `<tr><td>${escapeHtml(l.label)}</td><td align=\"right\">${escapeHtml(l.amount)}</td></tr>`,\n )\n .join('');\n}\n\nexport function orderEmailLines(\n order: OrderDetail,\n options: OrderEmailLinesOptions = {},\n): OrderEmailLines {\n const locale = options.locale ?? 'en-US';\n const labels = { ...defaultLabels, ...options.labels };\n const money = (cents: number) =>\n new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: order.currency,\n }).format(cents / 100);\n\n const items = order.items.map((i) => ({\n label: `${i.name} × ${i.quantity}`,\n amount: money(i.priceCents * i.quantity),\n }));\n const subtotal = {\n label: labels.subtotal,\n amount: money(order.subtotalCents),\n };\n const discount =\n order.discountCents > 0\n ? {\n label: labels.discount(order.discountCode),\n amount: `-${money(order.discountCents)}`,\n }\n : undefined;\n const shipping = {\n label: labels.shipping(order.shippingLabel),\n amount: money(order.shippingCents),\n };\n const total = { label: labels.total, amount: money(order.totalCents) };\n const address = addressLines(order);\n\n const totals = [subtotal, ...(discount ? [discount] : []), shipping, total];\n return {\n reference: formatOrderRef(order.id),\n items,\n subtotal,\n discount,\n shipping,\n total,\n address,\n text: {\n items: textBlock(items),\n totals: textBlock(totals),\n address: address?.join('\\n') ?? '',\n },\n html: {\n items: htmlRows(items),\n totals: htmlRows(totals),\n address: address?.map((l) => `<p>${escapeHtml(l)}</p>`).join('') ?? '',\n },\n };\n}\n","import path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { migrate } from 'drizzle-orm/node-postgres/migrator';\nimport type { NodePgDatabase } from 'drizzle-orm/node-postgres';\n\nconst here = path.dirname(fileURLToPath(import.meta.url));\n\n// Bundled into dist/index.js, but run from src/db/ under vitest: the two\n// sit at different depths from the package root.\nfunction resolveMigrationsFolder(): string {\n const fromSrc = path.resolve(here, '../../migrations');\n const fromDist = path.resolve(here, '../migrations');\n return here.endsWith(path.join('src', 'db')) ? fromSrc : fromDist;\n}\n\nexport async function runCommerceMigrations(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n db: NodePgDatabase<any>,\n): Promise<void> {\n await migrate(db, {\n migrationsFolder: resolveMigrationsFolder(),\n migrationsTable: '__commerce_migrations',\n });\n}\n","import { eq, sql } from 'drizzle-orm';\nimport type { CommerceDb } from './db/types';\nimport { productImages, products } from './db/schema';\n\nexport interface SeedProduct {\n slug: string;\n name: string;\n description?: string;\n materials?: string;\n dimensions?: string;\n priceCents: number;\n currency: string;\n category: string;\n quantity?: number;\n status?: 'draft' | 'active' | 'archived';\n featured?: boolean;\n images?: { url: string; alt?: string }[];\n}\n\nexport async function seedProducts(\n db: CommerceDb,\n items: SeedProduct[],\n): Promise<void> {\n await db.transaction(async (tx) => {\n for (const { images = [], ...item } of items) {\n const values = { status: 'active' as const, ...item };\n const [row] = await tx\n .insert(products)\n .values(values)\n .onConflictDoUpdate({\n target: products.slug,\n set: { ...values, updatedAt: sql`now()` },\n })\n .returning({ id: products.id });\n await tx.delete(productImages).where(eq(productImages.productId, row.id));\n if (images.length > 0) {\n await tx.insert(productImages).values(\n images.map((img, position) => ({\n productId: row.id,\n url: img.url,\n alt: img.alt ?? '',\n position,\n })),\n );\n }\n }\n });\n}\n","import type { CommerceDb } from '../db/types';\nimport { shippingRates } from '../db/schema';\n\nexport interface SeedShippingRate {\n label: string;\n amountCents: number;\n currency: string;\n minDays?: number;\n maxDays?: number;\n active?: boolean;\n sort?: number;\n}\n\nexport async function seedShippingRates(\n db: CommerceDb,\n rates: SeedShippingRate[],\n): Promise<void> {\n await db.transaction(async (tx) => {\n for (const rate of rates) {\n await tx\n .insert(shippingRates)\n .values(rate)\n .onConflictDoUpdate({\n target: shippingRates.label,\n set: {\n amountCents: rate.amountCents,\n currency: rate.currency,\n minDays: rate.minDays ?? null,\n maxDays: rate.maxDays ?? null,\n active: rate.active ?? true,\n sort: rate.sort ?? 0,\n },\n });\n }\n });\n}\n","import type { FileStorage } from './types';\n\nexport interface MemoryStorage extends FileStorage {\n files: Map<string, { body: Buffer; contentType: string }>;\n}\n\nexport function createMemoryStorage(baseUrl = 'memory://'): MemoryStorage {\n const files: MemoryStorage['files'] = new Map();\n return {\n files,\n async put(key, stream, contentType) {\n const chunks: Buffer[] = [];\n for await (const chunk of stream) chunks.push(Buffer.from(chunk));\n files.set(key, { body: Buffer.concat(chunks), contentType });\n return { key, url: `${baseUrl}${key}` };\n },\n async delete(key) {\n files.delete(key);\n },\n };\n}\n","import { createReadStream, createWriteStream } from 'node:fs';\nimport { mkdir, realpath, rm, stat } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport type { FileStorage } from './types';\n\nexport interface DiskStorageOptions {\n dir: string;\n publicUrl: string;\n}\n\nexport interface StoredFileStream {\n stream: Readable;\n contentType: string | undefined;\n size: number;\n}\n\nconst contentTypes: Record<string, string> = {\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.png': 'image/png',\n '.webp': 'image/webp',\n '.gif': 'image/gif',\n '.avif': 'image/avif',\n};\n\nfunction hasTraversalSegment(key: string): boolean {\n return key.split(/[\\\\/]/).some((segment) => segment === '..');\n}\n\nfunction decodesToTraversal(key: string): boolean {\n try {\n return hasTraversalSegment(decodeURIComponent(key));\n } catch {\n return true;\n }\n}\n\nfunction isInside(root: string, target: string): boolean {\n return target.startsWith(root + path.sep);\n}\n\nfunction resolveKey(dir: string, key: string): string | null {\n if (!key || key.includes('\\0') || path.isAbsolute(key)) return null;\n if (hasTraversalSegment(key) || decodesToTraversal(key)) return null;\n const root = path.resolve(dir);\n const target = path.resolve(root, key);\n return isInside(root, target) ? target : null;\n}\n\nasync function realInside(\n dir: string,\n target: string,\n allowRoot = false,\n): Promise<string | null> {\n const [root, real] = await Promise.all([\n realpath(dir),\n realpath(target),\n ]).catch(() => [null, null]);\n if (!root || !real) return null;\n return isInside(root, real) || (allowRoot && real === root) ? real : null;\n}\n\nexport function createDiskStorage(options: DiskStorageOptions): FileStorage {\n const { dir, publicUrl } = options;\n const base = publicUrl.replace(/\\/+$/, '');\n return {\n async put(key, stream) {\n const target = resolveKey(dir, key);\n if (!target)\n throw new Error(`Invalid storage key: ${JSON.stringify(key)}`);\n await mkdir(path.dirname(target), { recursive: true });\n if (\n !(await realInside(dir, path.dirname(target), true).catch(() => null))\n ) {\n throw new Error(`Invalid storage key: ${JSON.stringify(key)}`);\n }\n await pipeline(stream, createWriteStream(target));\n return { key, url: `${base}/${key}` };\n },\n async delete(key) {\n const target = resolveKey(dir, key);\n if (!target)\n throw new Error(`Invalid storage key: ${JSON.stringify(key)}`);\n if (\n !(await realInside(dir, path.dirname(target), true).catch(() => null))\n )\n return;\n await rm(target, { force: true });\n },\n };\n}\n\nexport async function openStoredFile(\n dir: string,\n key: string,\n): Promise<StoredFileStream | null> {\n const target = resolveKey(dir, key);\n if (!target) return null;\n const real = await realInside(dir, target);\n if (!real) return null;\n const info = await stat(real).catch(() => null);\n if (!info?.isFile()) return null;\n const contentType = contentTypes[path.extname(real).toLowerCase()];\n return { stream: createReadStream(real), contentType, size: info.size };\n}\n","import type { CheckoutUrls } from '../orders/types';\nimport { createFakeGateway } from './fake-gateway';\nimport { SESSION_ID_PLACEHOLDER, type PaymentGateway } from './gateway';\nimport { createStripeGateway } from './stripe-gateway';\n\nexport interface PaymentEnv {\n NODE_ENV?: string;\n PAYMENT_GATEWAY?: string;\n STRIPE_SECRET_KEY?: string;\n STRIPE_WEBHOOK_SECRET?: string;\n}\n\nexport function resolvePayment(env: PaymentEnv = process.env): PaymentGateway {\n const secretKey = env.STRIPE_SECRET_KEY || undefined;\n const webhookSecret = env.STRIPE_WEBHOOK_SECRET || undefined;\n if (!secretKey !== !webhookSecret) {\n throw new Error(\n 'Set STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET together, or neither',\n );\n }\n if (env.PAYMENT_GATEWAY === 'stripe' && !secretKey) {\n throw new Error('PAYMENT_GATEWAY=stripe needs the Stripe keys');\n }\n if (env.PAYMENT_GATEWAY === 'fake' || !secretKey) {\n if (env.NODE_ENV === 'production') {\n throw new Error(\n 'The fake payment gateway is not allowed in production; set the Stripe keys',\n );\n }\n return createFakeGateway();\n }\n return createStripeGateway({ secretKey, webhookSecret: webhookSecret! });\n}\n\nexport interface CheckoutUrlPaths {\n successPath?: string;\n cancelPath?: string;\n}\n\nexport function checkoutUrls(\n webUrl: string,\n {\n successPath = '/checkout/success',\n cancelPath = '/shop',\n }: CheckoutUrlPaths = {},\n): CheckoutUrls {\n const base = webUrl.replace(/\\/+$/, '');\n return {\n success: (sessionId) => {\n const id =\n sessionId === SESSION_ID_PLACEHOLDER\n ? sessionId\n : encodeURIComponent(sessionId);\n return `${base}${successPath}?session_id=${id}`;\n },\n cancel: `${base}${cancelPath}`,\n };\n}\n"],"mappings":"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","names":["adminProductInputSchema","adminProductPatchSchema","text","and","asc","eq","sql","asc","sql","eq","and","at","randomUUID","adminProductInputSchema","adminProductPatchSchema","and","asc","eq","gt","inArray","lt","sql","inArray","asc","sql","and","eq","gt","lt","and","asc","eq","gt","inArray","or","sql","and","eq","inArray","asc","conditions","sql","or","gt","withImages","decodeProductCursor","at","and","desc","eq","ilike","lt","or","sql","escapeLike","eq","sql","or","ilike","lt","and","desc","and","asc","eq","sql","and","eq","isNotNull","or","sql","or","isNotNull","sql","eq","idempotencyKey","and","sql","eq","asc","and","and","desc","eq","gt","ilike","inArray","isNull","lt","or","sql","eq","discountId","sql","and","inArray","escapeLike","or","isNull","gt","ilike","lt","desc","eq","sql","eq","sql","idempotencyKey","recordRefund","eq","sql","eq","sql","isZodError","UUID","BAD_REQUEST","isZodError","UUID","randomUUID","UUID","shippingRates","randomUUID","randomUUID","randomUUID","refunds","ignored","idempotencyKey","eq","sql","sql","eq","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/admin/routes.ts","../src/catalog/types.ts","../src/shared/cursor.ts","../src/shipping/repo.ts","../src/db/schema.ts","../src/shipping/service.ts","../src/admin/repo.ts","../src/admin/service.ts","../src/admin/types.ts","../src/admin/publish.ts","../src/admin/slug.ts","../src/admin/delete.ts","../src/admin/images.ts","../src/admin/upload.ts","../src/catalog/routes.ts","../src/catalog/cache-hooks.ts","../src/inventory/repo.ts","../src/inventory/types.ts","../src/inventory/service.ts","../src/catalog/repo.ts","../src/catalog/service.ts","../src/orders/admin-routes.ts","../src/orders/admin-service.ts","../src/orders/admin-repo.ts","../src/orders/repo.ts","../src/orders/refund-repo.ts","../src/orders/order-detail.ts","../src/orders/refund-errors.ts","../src/discounts/evaluate.ts","../src/discounts/repo.ts","../src/discounts/redemptions.ts","../src/payment/gateway.ts","../src/orders/gateway-refunds.ts","../src/orders/apply-event.ts","../src/orders/types.ts","../src/orders/order-service.ts","../src/orders/ship-service.ts","../src/orders/refund-service.ts","../src/orders/resolve-service.ts","../src/discounts/admin-routes.ts","../src/discounts/admin-service.ts","../src/orders/dev-buyer.ts","../src/orders/dev-checkout.ts","../src/orders/routes.ts","../src/discounts/service.ts","../src/orders/checkout-service.ts","../src/orders/webhook-routes.ts","../src/payment/fake-gateway.ts","../src/plugin.ts","../src/payment/stripe-loader.ts","../src/payment/stripe-gateway.ts","../src/hold-sweeper.ts","../src/emails/order-lines.ts","../src/db/migrate.ts","../src/seed.ts","../src/shipping/seed.ts","../src/storage/memory.ts","../src/storage/disk.ts","../src/payment/resolve.ts"],"sourcesContent":["import {\n adminProductInputSchema,\n adminProductPatchSchema,\n adminProductPageSchema,\n imageOrderInputSchema,\n productImagePatchSchema,\n shippingRatesInputSchema,\n} from '@develemit/commerce-contracts';\nimport type { FastifyPluginAsync, FastifyRequest } from 'fastify';\nimport { ProductNotFoundError } from '../catalog/types';\nimport { InvalidCursorError } from '../shared/cursor';\nimport type { CommerceDb } from '../db/types';\nimport { listShippingRates, setShippingRates } from '../shipping/service';\nimport type { FileStorage } from '../storage/types';\nimport { deleteAdminProduct } from './delete';\nimport {\n addProductImage,\n removeProductImage,\n reorderProductImages,\n updateProductImageAlt,\n} from './images';\nimport {\n createAdminProduct,\n getAdminProduct,\n getAdminProductCounts,\n listAdminProducts,\n patchAdminProduct,\n} from './service';\nimport {\n InvalidImageOrderError,\n ProductImageNotFoundError,\n ProductIncompleteError,\n ProductNotDeletableError,\n} from './types';\nimport { InvalidImageError, MAX_IMAGE_BYTES } from './upload';\n\nexport interface AdminOptions {\n authorize(req: FastifyRequest): Promise<boolean>;\n storage: FileStorage;\n requirePhotoToPublish?: boolean;\n}\n\nexport interface AdminPluginOptions extends AdminOptions {\n db: CommerceDb;\n currency: string;\n}\n\nfunction isZodError(err: unknown): err is Error & { issues: unknown[] } {\n return err instanceof Error && err.name === 'ZodError';\n}\n\nexport const adminPlugin: FastifyPluginAsync<AdminPluginOptions> = async (\n app,\n { db, currency, authorize, storage, requirePhotoToPublish },\n) => {\n const { default: multipart } = await import('@fastify/multipart');\n await app.register(multipart, {\n limits: { fileSize: MAX_IMAGE_BYTES, files: 1 },\n });\n\n app.setErrorHandler((err, _req, reply) => {\n if (isZodError(err)) {\n return reply\n .code(400)\n .send({ error: 'INVALID_INPUT', issues: err.issues });\n }\n if (\n err instanceof ProductNotFoundError ||\n err instanceof ProductImageNotFoundError\n ) {\n return reply.code(404).send({ error: err.code, message: err.message });\n }\n if (err instanceof ProductIncompleteError) {\n return reply.code(422).send({\n error: err.code,\n message: err.message,\n missing: err.missing,\n });\n }\n if (err instanceof ProductNotDeletableError) {\n return reply.code(409).send({\n error: err.code,\n message: err.message,\n reason: err.reason,\n });\n }\n if (\n err instanceof InvalidCursorError ||\n err instanceof InvalidImageError ||\n err instanceof InvalidImageOrderError\n ) {\n return reply.code(400).send({ error: err.code, message: err.message });\n }\n if ((err as { code?: string }).code === 'FST_REQ_FILE_TOO_LARGE') {\n return reply.code(413).send({ error: 'IMAGE_TOO_LARGE' });\n }\n return reply.send(err);\n });\n\n app.addHook('preHandler', async (req, reply) => {\n if (!(await authorize(req))) {\n return reply.code(401).send({ error: 'UNAUTHORIZED' });\n }\n });\n\n app.get('/products', async (req) =>\n adminProductPageSchema.parse(\n await listAdminProducts(db, req.query as object),\n ),\n );\n\n app.get('/products/counts', async () => getAdminProductCounts(db));\n\n app.post('/products', async (req, reply) => {\n const input = adminProductInputSchema.parse(req.body);\n const created = await createAdminProduct(db, currency, input, {\n requirePhotoToPublish,\n });\n return reply.code(201).send(created);\n });\n\n app.get<{ Params: { id: string } }>('/products/:id', async (req) =>\n getAdminProduct(db, req.params.id),\n );\n\n app.patch<{ Params: { id: string } }>('/products/:id', async (req) =>\n patchAdminProduct(\n db,\n req.params.id,\n adminProductPatchSchema.parse(req.body),\n { requirePhotoToPublish },\n ),\n );\n\n app.delete<{ Params: { id: string } }>(\n '/products/:id',\n async (req, reply) => {\n await deleteAdminProduct(db, storage, req.params.id);\n return reply.code(204).send();\n },\n );\n\n app.post<{ Params: { id: string } }>(\n '/products/:id/images',\n async (req, reply) => {\n const part = await req.file();\n if (!part) throw new InvalidImageError('An image file is required');\n const bytes = await part.toBuffer();\n const fields = part.fields as Record<string, { value?: unknown }>;\n const alt =\n typeof fields.alt?.value === 'string'\n ? productImagePatchSchema.parse({ alt: fields.alt.value }).alt\n : undefined;\n const product = await addProductImage(db, storage, req.params.id, {\n bytes,\n alt,\n });\n return reply.code(201).send(product);\n },\n );\n\n app.patch<{ Params: { id: string } }>(\n '/products/:id/images/order',\n async (req) =>\n reorderProductImages(\n db,\n req.params.id,\n imageOrderInputSchema.parse(req.body).imageIds,\n ),\n );\n\n app.patch<{ Params: { id: string; imageId: string } }>(\n '/products/:id/images/:imageId',\n async (req) =>\n updateProductImageAlt(\n db,\n req.params.id,\n req.params.imageId,\n productImagePatchSchema.parse(req.body).alt,\n ),\n );\n\n app.delete<{ Params: { id: string; imageId: string } }>(\n '/products/:id/images/:imageId',\n async (req) =>\n removeProductImage(db, storage, req.params.id, req.params.imageId),\n );\n\n app.get('/shipping-rates', async () => listShippingRates(db));\n\n app.put('/shipping-rates', async (req) =>\n setShippingRates(db, shippingRatesInputSchema.parse(req.body)),\n );\n};\n","export class ProductNotFoundError extends Error {\n readonly code = 'PRODUCT_NOT_FOUND';\n constructor(public readonly slug: string) {\n super(`Product not found: ${slug}`);\n this.name = 'ProductNotFoundError';\n }\n}\n\nexport interface CatalogFilter {\n category?: string;\n featured?: boolean;\n limit: number;\n cursor?: string;\n}\n\nexport interface PurchasableProduct {\n id: string;\n name: string;\n priceCents: number;\n currency: string;\n taxCode: string | null;\n}\n\nexport type CategoryLabels =\n Record<string, string> | ((slug: string) => string | undefined);\n","export class InvalidCursorError extends Error {\n readonly code = 'INVALID_CURSOR';\n constructor() {\n super('The cursor is malformed');\n this.name = 'InvalidCursorError';\n }\n}\n\nexport interface Cursor {\n at: string;\n key: string;\n}\n\nexport const PG_TIMESTAMP =\n /^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(\\.\\d+)?[+-]\\d{2}(:?\\d{2})?$/;\n\nexport function isCursorTimestamp(at: string): boolean {\n return PG_TIMESTAMP.test(at) || !Number.isNaN(Date.parse(at));\n}\n\nexport function encodeCursor({ at, key }: Cursor): string {\n return Buffer.from(`${at}|${key}`).toString('base64url');\n}\n\nexport function decodeCursor(\n raw: string,\n accepts: (cursor: Cursor) => boolean,\n): Cursor {\n const text = Buffer.from(raw, 'base64url').toString('utf8');\n const split = text.indexOf('|');\n const cursor = { at: text.slice(0, split), key: text.slice(split + 1) };\n if (split < 0 || !cursor.key || !accepts(cursor)) {\n throw new InvalidCursorError();\n }\n return cursor;\n}\n","import { and, asc, eq, notInArray } from 'drizzle-orm';\nimport type { CommerceExecutor } from '../db/types';\nimport { shippingRates } from '../db/schema';\n\nexport interface ShippingRateOption {\n label: string;\n amountCents: number;\n minDays?: number;\n maxDays?: number;\n}\n\nexport async function listActiveRates(\n db: CommerceExecutor,\n currency: string,\n): Promise<ShippingRateOption[]> {\n const rows = await db\n .select()\n .from(shippingRates)\n .where(\n and(eq(shippingRates.active, true), eq(shippingRates.currency, currency)),\n )\n .orderBy(asc(shippingRates.sort), asc(shippingRates.amountCents));\n return rows.map((r) => ({\n label: r.label,\n amountCents: r.amountCents,\n ...(r.minDays !== null && { minDays: r.minDays }),\n ...(r.maxDays !== null && { maxDays: r.maxDays }),\n }));\n}\n\nexport type ShippingRateRow = typeof shippingRates.$inferSelect;\n\nexport async function listAllRates(\n db: CommerceExecutor,\n): Promise<ShippingRateRow[]> {\n return db\n .select()\n .from(shippingRates)\n .orderBy(asc(shippingRates.sort), asc(shippingRates.label));\n}\n\nexport async function replaceRates(\n tx: CommerceExecutor,\n rates: (typeof shippingRates.$inferInsert)[],\n): Promise<void> {\n const labels = rates.map((r) => r.label);\n await tx\n .delete(shippingRates)\n .where(\n labels.length > 0 ? notInArray(shippingRates.label, labels) : undefined,\n );\n for (const rate of rates) {\n await tx\n .insert(shippingRates)\n .values(rate)\n .onConflictDoUpdate({\n target: shippingRates.label,\n set: {\n amountCents: rate.amountCents,\n currency: rate.currency,\n minDays: rate.minDays ?? null,\n maxDays: rate.maxDays ?? null,\n active: rate.active ?? true,\n sort: rate.sort ?? 0,\n },\n });\n }\n}\n","import { sql } from 'drizzle-orm';\nimport {\n boolean,\n check,\n index,\n integer,\n jsonb,\n pgEnum,\n pgTable,\n text,\n unique,\n timestamp,\n uuid,\n} from 'drizzle-orm/pg-core';\n\nexport const productStatus = pgEnum('commerce_product_status', [\n 'draft',\n 'active',\n 'archived',\n]);\n\nexport const products = pgTable(\n 'commerce_products',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n slug: text('slug').notNull().unique(),\n name: text('name').notNull().default(''),\n description: text('description').notNull().default(''),\n materials: text('materials'),\n dimensions: text('dimensions'),\n priceCents: integer('price_cents'),\n currency: text('currency').notNull(),\n category: text('category'),\n quantity: integer('quantity').notNull().default(1),\n status: productStatus('status').notNull().default('draft'),\n featured: boolean('featured').notNull().default(false),\n taxCode: text('tax_code'),\n publishedAt: timestamp('published_at', { withTimezone: true }),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (t) => [check('commerce_products_quantity_check', sql`${t.quantity} >= 0`)],\n);\n\nexport const productImages = pgTable('commerce_product_images', {\n id: uuid('id').primaryKey().defaultRandom(),\n productId: uuid('product_id')\n .notNull()\n .references(() => products.id, { onDelete: 'cascade' }),\n url: text('url').notNull(),\n alt: text('alt').notNull().default(''),\n storageKey: text('storage_key'),\n position: integer('position').notNull().default(0),\n});\n\nexport const holdStatus = pgEnum('commerce_hold_status', [\n 'active',\n 'released',\n 'committed',\n]);\n\nexport const inventoryHolds = pgTable(\n 'commerce_inventory_holds',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n productId: uuid('product_id')\n .notNull()\n .references(() => products.id, { onDelete: 'cascade' }),\n quantity: integer('quantity').notNull(),\n reference: text('reference').notNull(),\n expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),\n status: holdStatus('status').notNull().default('active'),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (t) => [\n check('commerce_inventory_holds_quantity_check', sql`${t.quantity} > 0`),\n index('commerce_inventory_holds_reference_idx').on(t.reference),\n index('commerce_inventory_holds_product_status_idx').on(\n t.productId,\n t.status,\n ),\n ],\n);\n\nexport const orderStatus = pgEnum('commerce_order_status', [\n 'pending',\n 'paid',\n 'shipped',\n 'cancelled',\n 'expired',\n 'needs_attention',\n 'refunded',\n]);\n\nexport const orders = pgTable('commerce_orders', {\n id: uuid('id').primaryKey(),\n status: orderStatus('status').notNull().default('pending'),\n email: text('email'),\n subtotalCents: integer('subtotal_cents').notNull(),\n discountCents: integer('discount_cents').notNull().default(0),\n discountCode: text('discount_code'),\n shippingCents: integer('shipping_cents').notNull().default(0),\n shippingLabel: text('shipping_label'),\n taxCents: integer('tax_cents').notNull().default(0),\n currency: text('currency').notNull(),\n checkoutSessionId: text('checkout_session_id').unique(),\n shippingAddress: jsonb('shipping_address'),\n fulfillmentCarrier: text('fulfillment_carrier'),\n fulfillmentTrackingNumber: text('fulfillment_tracking_number'),\n fulfillmentTrackingUrl: text('fulfillment_tracking_url'),\n shippedAt: timestamp('shipped_at', { withTimezone: true }),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n});\n\nexport const orderItems = pgTable('commerce_order_items', {\n id: uuid('id').primaryKey().defaultRandom(),\n orderId: uuid('order_id')\n .notNull()\n .references(() => orders.id, { onDelete: 'cascade' }),\n productId: uuid('product_id').notNull(),\n name: text('name').notNull(),\n priceCents: integer('price_cents').notNull(),\n quantity: integer('quantity').notNull(),\n});\n\nexport const orderRefunds = pgTable(\n 'commerce_order_refunds',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n orderId: uuid('order_id')\n .notNull()\n .references(() => orders.id, { onDelete: 'cascade' }),\n amountCents: integer('amount_cents').notNull(),\n currency: text('currency').notNull(),\n reason: text('reason'),\n restocked: boolean('restocked').notNull().default(false),\n source: text('source', { enum: ['admin', 'gateway'] }).notNull(),\n gatewayRefundId: text('gateway_refund_id').unique(),\n idempotencyKey: text('idempotency_key'),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (t) => [\n check('commerce_order_refunds_amount_check', sql`${t.amountCents} > 0`),\n index('commerce_order_refunds_order_idx').on(t.orderId),\n unique('commerce_order_refunds_idempotency_unique').on(\n t.orderId,\n t.idempotencyKey,\n ),\n ],\n);\n\nexport const stripeEvents = pgTable('commerce_stripe_events', {\n id: text('id').primaryKey(),\n type: text('type').notNull(),\n processedAt: timestamp('processed_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n});\n\nexport const shippingRates = pgTable(\n 'commerce_shipping_rates',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n label: text('label').notNull().unique(),\n amountCents: integer('amount_cents').notNull(),\n currency: text('currency').notNull(),\n minDays: integer('min_days'),\n maxDays: integer('max_days'),\n active: boolean('active').notNull().default(true),\n sort: integer('sort').notNull().default(0),\n },\n (t) => [\n check('commerce_shipping_rates_amount_check', sql`${t.amountCents} >= 0`),\n ],\n);\n\nexport const discounts = pgTable(\n 'commerce_discounts',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n code: text('code').notNull().unique(),\n type: text('type', { enum: ['percent', 'fixed'] }).notNull(),\n percentOff: integer('percent_off'),\n amountOffCents: integer('amount_off_cents'),\n currency: text('currency'),\n minSubtotalCents: integer('min_subtotal_cents'),\n startsAt: timestamp('starts_at', { withTimezone: true }),\n endsAt: timestamp('ends_at', { withTimezone: true }),\n maxRedemptions: integer('max_redemptions'),\n active: boolean('active').notNull().default(true),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (t) => [\n check('commerce_discounts_code_check', sql`${t.code} = upper(${t.code})`),\n check(\n 'commerce_discounts_percent_check',\n sql`${t.percentOff} is null or (${t.percentOff} between 1 and 100)`,\n ),\n check(\n 'commerce_discounts_amount_check',\n sql`${t.amountOffCents} is null or ${t.amountOffCents} > 0`,\n ),\n check(\n 'commerce_discounts_max_redemptions_check',\n sql`${t.maxRedemptions} is null or ${t.maxRedemptions} > 0`,\n ),\n check(\n 'commerce_discounts_type_fields_check',\n sql`(${t.type} = 'percent' and ${t.percentOff} is not null and ${t.amountOffCents} is null and ${t.currency} is null) or (${t.type} = 'fixed' and ${t.percentOff} is null and ${t.amountOffCents} is not null and ${t.currency} is not null)`,\n ),\n ],\n);\n\nexport const discountRedemptions = pgTable(\n 'commerce_discount_redemptions',\n {\n id: uuid('id').primaryKey().defaultRandom(),\n discountId: uuid('discount_id')\n .notNull()\n .references(() => discounts.id, { onDelete: 'cascade' }),\n orderId: uuid('order_id')\n .notNull()\n .unique()\n .references(() => orders.id, { onDelete: 'cascade' }),\n status: text('status', { enum: ['reserved', 'committed', 'released'] })\n .notNull()\n .default('reserved'),\n amountCents: integer('amount_cents').notNull(),\n createdAt: timestamp('created_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n updatedAt: timestamp('updated_at', { withTimezone: true })\n .notNull()\n .defaultNow(),\n },\n (t) => [\n check(\n 'commerce_discount_redemptions_amount_check',\n sql`${t.amountCents} > 0`,\n ),\n check(\n 'commerce_discount_redemptions_status_check',\n sql`${t.status} in ('reserved', 'committed', 'released')`,\n ),\n index('commerce_discount_redemptions_discount_status_idx').on(\n t.discountId,\n t.status,\n ),\n ],\n);\n\nexport const commerceSchema = {\n products,\n productImages,\n productStatus,\n inventoryHolds,\n holdStatus,\n orders,\n orderItems,\n orderStatus,\n orderRefunds,\n stripeEvents,\n shippingRates,\n discounts,\n discountRedemptions,\n};\nexport type CommerceSchema = typeof commerceSchema;\n","import type { ShippingRate } from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport { listAllRates, replaceRates } from './repo';\n\nexport async function listShippingRates(\n db: CommerceDb,\n): Promise<ShippingRate[]> {\n return (await listAllRates(db)).map((r) => ({\n label: r.label,\n amountCents: r.amountCents,\n currency: r.currency,\n minDays: r.minDays,\n maxDays: r.maxDays,\n active: r.active,\n sort: r.sort,\n }));\n}\n\nexport async function setShippingRates(\n db: CommerceDb,\n rates: ShippingRate[],\n): Promise<ShippingRate[]> {\n await db.transaction((tx) => replaceRates(tx, rates));\n return listShippingRates(db);\n}\n","import {\n and,\n asc,\n desc,\n eq,\n gt,\n ilike,\n inArray,\n lt,\n ne,\n or,\n sql,\n type SQL,\n} from 'drizzle-orm';\nimport type { AdminProductCounts } from '@develemit/commerce-contracts';\nimport {\n inventoryHolds,\n orderItems,\n productImages,\n products,\n} from '../db/schema';\nimport type { CommerceExecutor } from '../db/types';\n\nexport type ProductRow = typeof products.$inferSelect;\nexport type ImageRow = typeof productImages.$inferSelect;\nexport interface ProductRecord {\n row: ProductRow;\n images: ImageRow[];\n}\n\nasync function withImages(\n db: CommerceExecutor,\n rows: ProductRow[],\n): Promise<ProductRecord[]> {\n if (rows.length === 0) return [];\n const images = await db\n .select()\n .from(productImages)\n .where(\n inArray(\n productImages.productId,\n rows.map((r) => r.id),\n ),\n )\n .orderBy(asc(productImages.position), asc(productImages.id));\n return rows.map((row) => ({\n row,\n images: images.filter((i) => i.productId === row.id),\n }));\n}\n\nexport interface ProductPageFilter {\n status?: ProductRow['status'];\n excludeArchived?: boolean;\n incomplete?: boolean;\n stock?: 'in' | 'sold';\n q?: string;\n after?: { createdAt: string; slug: string };\n limit: number;\n}\n\nexport interface ProductPageRecord extends ProductRecord {\n createdAtKey: string;\n}\n\nfunction escapeLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (c) => `\\\\${c}`);\n}\n\nconst incompleteDraft = sql`(${products.status} = 'draft' and (${products.name} = '' or ${products.priceCents} is null or ${products.category} is null))`;\n\nfunction pageConditions(filter: ProductPageFilter): SQL | undefined {\n const parts: (SQL | undefined)[] = [];\n if (filter.status) parts.push(eq(products.status, filter.status));\n if (filter.excludeArchived) parts.push(ne(products.status, 'archived'));\n if (filter.incomplete) parts.push(incompleteDraft);\n if (filter.stock === 'in') parts.push(gt(products.quantity, 0));\n if (filter.stock === 'sold') parts.push(eq(products.quantity, 0));\n if (filter.q) parts.push(ilike(products.name, `%${escapeLike(filter.q)}%`));\n if (filter.after) {\n const createdAt = sql`${filter.after.createdAt}::timestamptz`;\n parts.push(\n or(\n lt(products.createdAt, createdAt),\n and(\n eq(products.createdAt, createdAt),\n lt(products.slug, filter.after.slug),\n ),\n ),\n );\n }\n return and(...parts);\n}\n\nexport async function selectProductPage(\n db: CommerceExecutor,\n filter: ProductPageFilter,\n): Promise<ProductPageRecord[]> {\n const selected = await db\n .select({\n product: products,\n createdAtKey: sql<string>`${products.createdAt}::text`,\n })\n .from(products)\n .where(pageConditions(filter))\n .orderBy(desc(products.createdAt), desc(products.slug))\n .limit(filter.limit);\n const records = await withImages(\n db,\n selected.map((s) => s.product),\n );\n return records.map((r, i) => ({\n ...r,\n createdAtKey: selected[i].createdAtKey,\n }));\n}\n\nexport async function findProductById(\n db: CommerceExecutor,\n id: string,\n): Promise<ProductRecord | undefined> {\n const rows = await db.select().from(products).where(eq(products.id, id));\n return (await withImages(db, rows))[0];\n}\n\nexport async function slugsStartingWith(\n db: CommerceExecutor,\n base: string,\n): Promise<Set<string>> {\n const rows = await db\n .select({ slug: products.slug })\n .from(products)\n .where(sql`${products.slug} like ${`${base}%`}`);\n return new Set(rows.map((r) => r.slug));\n}\n\nexport async function insertProduct(\n db: CommerceExecutor,\n values: typeof products.$inferInsert,\n): Promise<string> {\n const [row] = await db\n .insert(products)\n .values(values)\n .returning({ id: products.id });\n return row.id;\n}\n\nexport async function updateProduct(\n db: CommerceExecutor,\n id: string,\n set: Partial<typeof products.$inferInsert>,\n): Promise<void> {\n await db\n .update(products)\n .set({ ...set, updatedAt: sql`now()` })\n .where(eq(products.id, id));\n}\n\nexport async function nextImagePosition(\n db: CommerceExecutor,\n productId: string,\n): Promise<number> {\n const [row] = await db\n .select({\n next: sql<number>`coalesce(max(${productImages.position}) + 1, 0)::int`,\n })\n .from(productImages)\n .where(eq(productImages.productId, productId));\n return row.next;\n}\n\nexport async function insertImage(\n db: CommerceExecutor,\n values: typeof productImages.$inferInsert,\n): Promise<ImageRow> {\n const [row] = await db.insert(productImages).values(values).returning();\n return row;\n}\n\nexport async function deleteImageRow(\n db: CommerceExecutor,\n id: string,\n): Promise<void> {\n await db.delete(productImages).where(eq(productImages.id, id));\n}\n\nexport async function setImagePosition(\n db: CommerceExecutor,\n id: string,\n position: number,\n): Promise<void> {\n await db\n .update(productImages)\n .set({ position })\n .where(eq(productImages.id, id));\n}\n\nexport async function setImageAlt(\n db: CommerceExecutor,\n id: string,\n alt: string,\n): Promise<void> {\n await db.update(productImages).set({ alt }).where(eq(productImages.id, id));\n}\n\nexport async function selectProductCounts(\n db: CommerceExecutor,\n): Promise<AdminProductCounts> {\n const [row] = await db\n .select({\n all: sql<number>`count(*) filter (where ${products.status} <> 'archived')::int`,\n live: sql<number>`count(*) filter (where ${products.status} = 'active' and ${products.quantity} > 0)::int`,\n draft: sql<number>`count(*) filter (where ${products.status} = 'draft')::int`,\n sold: sql<number>`count(*) filter (where ${products.status} = 'active' and ${products.quantity} = 0)::int`,\n archived: sql<number>`count(*) filter (where ${products.status} = 'archived')::int`,\n incompleteDrafts: sql<number>`count(*) filter (where ${products.status} = 'draft' and (${products.name} = '' or ${products.priceCents} is null or ${products.category} is null))::int`,\n })\n .from(products);\n return row;\n}\n\nexport async function productHasOrderLines(\n db: CommerceExecutor,\n productId: string,\n): Promise<boolean> {\n const rows = await db\n .select({ id: orderItems.id })\n .from(orderItems)\n .where(eq(orderItems.productId, productId))\n .limit(1);\n return rows.length > 0;\n}\n\nexport async function deleteProductRows(\n db: CommerceExecutor,\n id: string,\n): Promise<void> {\n await db.delete(inventoryHolds).where(eq(inventoryHolds.productId, id));\n await db.delete(productImages).where(eq(productImages.productId, id));\n await db.delete(products).where(eq(products.id, id));\n}\n","import type {\n AdminProduct,\n AdminProductListQuery,\n AdminProductPage,\n AdminProductInput,\n AdminProductPatch,\n AdminProductCounts,\n} from '@develemit/commerce-contracts';\nimport {\n adminProductInputSchema,\n adminProductListQuerySchema,\n adminProductPatchSchema,\n} from '@develemit/commerce-contracts';\nimport { randomUUID } from 'node:crypto';\nimport { ProductNotFoundError } from '../catalog/types';\nimport { decodeCursor, encodeCursor, PG_TIMESTAMP } from '../shared/cursor';\nimport type { products } from '../db/schema';\nimport type { CommerceDb } from '../db/types';\nimport {\n findProductById,\n insertProduct,\n selectProductCounts,\n selectProductPage,\n slugsStartingWith,\n updateProduct,\n type ProductRecord,\n} from './repo';\nimport { assertPublishable, temporarySlug } from './publish';\nimport { slugify, uniqueSlug } from './slug';\nimport { ProductIncompleteError } from './types';\n\nexport function toAdminProduct({ row, images }: ProductRecord): AdminProduct {\n return {\n id: row.id,\n slug: row.slug,\n name: row.name,\n description: row.description,\n materials: row.materials,\n dimensions: row.dimensions,\n priceCents: row.priceCents,\n currency: row.currency,\n category: row.category,\n quantity: row.quantity,\n status: row.status,\n featured: row.featured,\n everPublished: row.publishedAt !== null,\n taxCode: row.taxCode,\n images: images.map((i) => ({\n id: i.id,\n url: i.url,\n alt: i.alt,\n position: i.position,\n })),\n };\n}\n\nexport async function requireProduct(\n db: CommerceDb,\n id: string,\n): Promise<ProductRecord> {\n const found = await findProductById(db, id);\n if (!found) throw new ProductNotFoundError(id);\n return found;\n}\n\nfunction decodeProductCursor(raw: string): { createdAt: string; slug: string } {\n const { at, key } = decodeCursor(raw, ({ at }) => PG_TIMESTAMP.test(at));\n return { createdAt: at, slug: key };\n}\n\nexport async function listAdminProducts(\n db: CommerceDb,\n rawQuery: AdminProductListQuery = {},\n): Promise<AdminProductPage> {\n const query = adminProductListQuerySchema.parse(rawQuery);\n const found = await selectProductPage(db, {\n status: query.status,\n excludeArchived: query.excludeArchived === 'true',\n incomplete: query.incomplete === 'true',\n stock: query.stock,\n q: query.q,\n after: query.cursor ? decodeProductCursor(query.cursor) : undefined,\n limit: query.limit + 1,\n });\n const page = found.slice(0, query.limit);\n const last = page[page.length - 1];\n return {\n items: page.map(toAdminProduct),\n nextCursor:\n found.length > query.limit && last\n ? encodeCursor({ at: last.createdAtKey, key: last.row.slug })\n : null,\n };\n}\n\nexport async function getAdminProduct(\n db: CommerceDb,\n id: string,\n): Promise<AdminProduct> {\n return toAdminProduct(await requireProduct(db, id));\n}\n\nexport async function createAdminProduct(\n db: CommerceDb,\n currency: string,\n input: AdminProductInput,\n { requirePhotoToPublish = true }: { requirePhotoToPublish?: boolean } = {},\n): Promise<AdminProduct> {\n const data = adminProductInputSchema.parse(input);\n if (data.status === 'active' && requirePhotoToPublish) {\n throw new ProductIncompleteError(['images']);\n }\n const newId = randomUUID();\n const base = data.name ? slugify(data.name) : temporarySlug(newId);\n const id = await db.transaction(async (tx) => {\n const slug = uniqueSlug(base, await slugsStartingWith(tx, base));\n return insertProduct(tx, {\n ...data,\n publishedAt: data.status === 'active' ? new Date() : undefined,\n id: newId,\n slug,\n currency: data.currency ?? currency,\n });\n });\n return getAdminProduct(db, id);\n}\n\nexport async function patchAdminProduct(\n db: CommerceDb,\n id: string,\n input: AdminProductPatch,\n { requirePhotoToPublish = true }: { requirePhotoToPublish?: boolean } = {},\n): Promise<AdminProduct> {\n const { sold, ...fields } = adminProductPatchSchema.parse(input);\n const record = await requireProduct(db, id);\n if (fields.status === 'active') {\n const photoNeeded = requirePhotoToPublish && record.row.status !== 'active';\n assertPublishable(record, fields, photoNeeded);\n }\n const set: Partial<typeof products.$inferInsert> = sold\n ? { ...fields, quantity: 0 }\n : { ...fields };\n if (fields.status === 'active' && !record.row.publishedAt) {\n set.publishedAt = new Date();\n }\n const renames = fields.name && record.row.slug === temporarySlug(id);\n await db.transaction(async (tx) => {\n if (renames) {\n const base = slugify(fields.name!);\n set.slug = uniqueSlug(base, await slugsStartingWith(tx, base));\n }\n if (Object.keys(set).length > 0) await updateProduct(tx, id, set);\n });\n return getAdminProduct(db, id);\n}\n\nexport async function getAdminProductCounts(\n db: CommerceDb,\n): Promise<AdminProductCounts> {\n return selectProductCounts(db);\n}\n","import type { ProductIncompleteField } from '@develemit/commerce-contracts';\n\nexport class ProductImageNotFoundError extends Error {\n readonly code = 'IMAGE_NOT_FOUND';\n constructor(public readonly imageId: string) {\n super(`Image not found: ${imageId}`);\n this.name = 'ProductImageNotFoundError';\n }\n}\n\nexport class InvalidImageOrderError extends Error {\n readonly code = 'INVALID_IMAGE_ORDER';\n constructor() {\n super('imageIds must list every image of the product exactly once');\n this.name = 'InvalidImageOrderError';\n }\n}\n\nexport class ProductIncompleteError extends Error {\n readonly code = 'PRODUCT_INCOMPLETE';\n constructor(public readonly missing: ProductIncompleteField[]) {\n super(`Product is missing: ${missing.join(', ')}`);\n this.name = 'ProductIncompleteError';\n }\n}\n\nexport class ProductNotDeletableError extends Error {\n readonly code = 'PRODUCT_NOT_DELETABLE';\n constructor(public readonly reason: 'published' | 'has_orders') {\n super(\n reason === 'published'\n ? 'This product has been live, so it can only be archived'\n : 'This product is on an order, so it can only be archived',\n );\n this.name = 'ProductNotDeletableError';\n }\n}\n","import type { ProductIncompleteField } from '@develemit/commerce-contracts';\nimport type { ProductRecord } from './repo';\nimport { ProductIncompleteError } from './types';\n\ninterface PublishFields {\n name?: string;\n priceCents?: number | null;\n category?: string | null;\n}\n\nexport function temporarySlug(id: string): string {\n return `draft-${id.slice(0, 8)}`;\n}\n\nexport function missingForPublish(\n { row, images }: ProductRecord,\n patch: PublishFields,\n requirePhoto: boolean,\n): ProductIncompleteField[] {\n const name = patch.name ?? row.name;\n const priceCents = patch.priceCents ?? row.priceCents;\n const category = patch.category ?? row.category;\n const missing: ProductIncompleteField[] = [];\n if (!name) missing.push('name');\n if (priceCents === null) missing.push('priceCents');\n if (!category) missing.push('category');\n if (requirePhoto && images.length === 0) missing.push('images');\n return missing;\n}\n\nexport function assertPublishable(\n record: ProductRecord,\n patch: PublishFields,\n requirePhoto: boolean,\n): void {\n const missing = missingForPublish(record, patch, requirePhoto);\n if (missing.length > 0) throw new ProductIncompleteError(missing);\n}\n","export function slugify(name: string): string {\n const slug = name\n .normalize('NFKD')\n .replace(/[̀-ͯ]/g, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n return slug || 'product';\n}\n\nexport function uniqueSlug(base: string, taken: Set<string>): string {\n if (!taken.has(base)) return base;\n let n = 2;\n while (taken.has(`${base}-${n}`)) n += 1;\n return `${base}-${n}`;\n}\n","import type { CommerceDb } from '../db/types';\nimport type { FileStorage } from '../storage/types';\nimport { deleteProductRows, productHasOrderLines } from './repo';\nimport { requireProduct } from './service';\nimport { ProductNotDeletableError } from './types';\n\nexport async function deleteAdminProduct(\n db: CommerceDb,\n storage: FileStorage,\n id: string,\n): Promise<void> {\n const { row, images } = await requireProduct(db, id);\n if (row.status !== 'draft' || row.publishedAt) {\n throw new ProductNotDeletableError('published');\n }\n if (await productHasOrderLines(db, id)) {\n throw new ProductNotDeletableError('has_orders');\n }\n await db.transaction((tx) => deleteProductRows(tx, id));\n for (const image of images) {\n if (image.storageKey) await storage.delete(image.storageKey);\n }\n}\n","import { Readable } from 'node:stream';\nimport type { AdminProduct } from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport type { FileStorage } from '../storage/types';\nimport {\n deleteImageRow,\n insertImage,\n nextImagePosition,\n setImageAlt,\n setImagePosition,\n} from './repo';\nimport { getAdminProduct, requireProduct } from './service';\nimport { InvalidImageOrderError, ProductImageNotFoundError } from './types';\nimport { detectImage, imageKey } from './upload';\n\nexport async function addProductImage(\n db: CommerceDb,\n storage: FileStorage,\n productId: string,\n file: { bytes: Buffer; alt?: string },\n): Promise<AdminProduct> {\n await requireProduct(db, productId);\n const { mime, ext } = await detectImage(file.bytes);\n const stored = await storage.put(\n imageKey(productId, ext),\n Readable.from(file.bytes),\n mime,\n );\n try {\n await insertImage(db, {\n productId,\n url: stored.url,\n alt: file.alt ?? '',\n storageKey: stored.key,\n position: await nextImagePosition(db, productId),\n });\n } catch (err) {\n await storage.delete(stored.key);\n throw err;\n }\n return getAdminProduct(db, productId);\n}\n\nexport async function reorderProductImages(\n db: CommerceDb,\n productId: string,\n imageIds: string[],\n): Promise<AdminProduct> {\n const { images } = await requireProduct(db, productId);\n const existing = new Set(images.map((i) => i.id));\n if (\n imageIds.length !== existing.size ||\n new Set(imageIds).size !== imageIds.length ||\n !imageIds.every((id) => existing.has(id))\n ) {\n throw new InvalidImageOrderError();\n }\n await db.transaction(async (tx) => {\n for (const [position, id] of imageIds.entries()) {\n await setImagePosition(tx, id, position);\n }\n });\n return getAdminProduct(db, productId);\n}\n\nexport async function removeProductImage(\n db: CommerceDb,\n storage: FileStorage,\n productId: string,\n imageId: string,\n): Promise<AdminProduct> {\n const { images } = await requireProduct(db, productId);\n const image = images.find((i) => i.id === imageId);\n if (!image) throw new ProductImageNotFoundError(imageId);\n await deleteImageRow(db, imageId);\n if (image.storageKey) await storage.delete(image.storageKey);\n return getAdminProduct(db, productId);\n}\n\nexport async function updateProductImageAlt(\n db: CommerceDb,\n productId: string,\n imageId: string,\n alt: string,\n): Promise<AdminProduct> {\n const { images } = await requireProduct(db, productId);\n if (!images.some((i) => i.id === imageId)) {\n throw new ProductImageNotFoundError(imageId);\n }\n await setImageAlt(db, imageId, alt);\n return getAdminProduct(db, productId);\n}\n","import { randomUUID } from 'node:crypto';\nimport { fileTypeFromBuffer } from 'file-type';\n\nexport const MAX_IMAGE_BYTES = 8 * 1024 * 1024;\n\nconst EXTENSIONS: Record<string, string> = {\n 'image/jpeg': 'jpg',\n 'image/png': 'png',\n 'image/webp': 'webp',\n 'image/heic': 'heic',\n};\n\nexport class InvalidImageError extends Error {\n readonly code = 'INVALID_IMAGE';\n constructor(message: string) {\n super(message);\n this.name = 'InvalidImageError';\n }\n}\n\nexport async function detectImage(\n bytes: Buffer,\n): Promise<{ mime: string; ext: string }> {\n const type = await fileTypeFromBuffer(bytes);\n const ext = type && EXTENSIONS[type.mime];\n if (!type || !ext) {\n throw new InvalidImageError('File must be a jpeg, png, webp or heic image');\n }\n return { mime: type.mime, ext };\n}\n\nexport function imageKey(productId: string, ext: string): string {\n return `products/${productId}/${randomUUID()}.${ext}`;\n}\n","import {\n categorySchema,\n productCardPageSchema,\n productListQuerySchema,\n productSchema,\n} from '@develemit/commerce-contracts';\nimport type { FastifyPluginAsync } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport { InvalidCursorError } from '../shared/cursor';\nimport { addCatalogCaching, type CatalogCacheOptions } from './cache-hooks';\nimport { getProduct, listCategories, listProducts } from './service';\nimport { ProductNotFoundError, type CategoryLabels } from './types';\n\nexport interface CatalogPluginOptions {\n db: CommerceDb;\n categoryLabels?: CategoryLabels;\n catalogCache?: CatalogCacheOptions;\n}\n\nexport const catalogPlugin: FastifyPluginAsync<CatalogPluginOptions> = async (\n app,\n { db, categoryLabels, catalogCache },\n) => {\n addCatalogCaching(app, catalogCache);\n app.setErrorHandler((err, _req, reply) => {\n if (err instanceof ProductNotFoundError) {\n return reply.code(404).send({ error: err.code, message: err.message });\n }\n if (err instanceof InvalidCursorError) {\n return reply.code(400).send({ error: err.code, message: err.message });\n }\n return reply.send(err);\n });\n\n app.get('/products', async (req, reply) => {\n const raw = req.query as Record<string, string | undefined>;\n const parsed = productListQuerySchema.safeParse({\n category: raw.category || undefined,\n featured:\n raw.featured === undefined ? undefined : raw.featured === 'true',\n limit: raw.limit,\n cursor: raw.cursor || undefined,\n });\n if (!parsed.success) {\n return reply.code(400).send({ error: 'INVALID_QUERY' });\n }\n return productCardPageSchema.parse(await listProducts(db, parsed.data));\n });\n\n app.get<{ Params: { slug: string } }>('/products/:slug', async (req) => {\n return productSchema.parse(await getProduct(db, req.params.slug));\n });\n\n app.get('/categories', async () => {\n const categories = await listCategories(db, categoryLabels);\n return categories.map((c) => categorySchema.parse(c));\n });\n};\n","import { createHash } from 'node:crypto';\nimport type { FastifyInstance } from 'fastify';\n\nexport interface CatalogCacheOptions {\n cacheControl?: string | false;\n}\n\nexport const DEFAULT_CATALOG_CACHE_CONTROL =\n 'public, max-age=30, stale-while-revalidate=300';\n\nfunction etagOf(body: string): string {\n return `\"${createHash('sha1').update(body).digest('base64url')}\"`;\n}\n\nexport function matchesIfNoneMatch(\n header: string | undefined,\n etag: string,\n): boolean {\n if (!header) return false;\n if (header.trim() === '*') return true;\n return header\n .split(',')\n .map((t) => t.trim().replace(/^W\\//, ''))\n .includes(etag);\n}\n\nexport function addCatalogCaching(\n app: FastifyInstance,\n { cacheControl = DEFAULT_CATALOG_CACHE_CONTROL }: CatalogCacheOptions = {},\n): void {\n app.addHook('onSend', async (req, reply, payload) => {\n if (reply.statusCode >= 400) {\n reply.header('cache-control', 'no-store');\n return payload;\n }\n if (reply.statusCode !== 200 || typeof payload !== 'string') {\n return payload;\n }\n const etag = etagOf(payload);\n reply.header('etag', etag);\n if (cacheControl !== false) reply.header('cache-control', cacheControl);\n if (matchesIfNoneMatch(req.headers['if-none-match'], etag)) {\n reply.code(304);\n reply.removeHeader('content-length');\n reply.removeHeader('content-type');\n return '';\n }\n return payload;\n });\n}\n","import { and, asc, eq, gt, inArray, lt, sql } from 'drizzle-orm';\nimport type { CommerceExecutor } from '../db/types';\nimport { inventoryHolds, products } from '../db/schema';\nimport type { StockRequest } from './types';\n\nexport async function lockProductQuantities(\n tx: CommerceExecutor,\n productIds: string[],\n): Promise<Map<string, number>> {\n const rows = await tx\n .select({ id: products.id, quantity: products.quantity })\n .from(products)\n .where(inArray(products.id, productIds))\n .orderBy(asc(products.id))\n .for('update');\n return new Map(rows.map((r) => [r.id, r.quantity]));\n}\n\nexport async function activeHeldQuantities(\n db: CommerceExecutor,\n productIds: string[],\n): Promise<Map<string, number>> {\n if (productIds.length === 0) return new Map();\n const rows = await db\n .select({\n productId: inventoryHolds.productId,\n held: sql<number>`sum(${inventoryHolds.quantity})::int`,\n })\n .from(inventoryHolds)\n .where(\n and(\n inArray(inventoryHolds.productId, productIds),\n eq(inventoryHolds.status, 'active'),\n gt(inventoryHolds.expiresAt, sql`now()`),\n ),\n )\n .groupBy(inventoryHolds.productId);\n return new Map(rows.map((r) => [r.productId, r.held]));\n}\n\nexport async function productQuantities(\n db: CommerceExecutor,\n productIds: string[],\n): Promise<Map<string, number>> {\n if (productIds.length === 0) return new Map();\n const rows = await db\n .select({ id: products.id, quantity: products.quantity })\n .from(products)\n .where(inArray(products.id, productIds));\n return new Map(rows.map((r) => [r.id, r.quantity]));\n}\n\nexport async function insertHolds(\n tx: CommerceExecutor,\n reference: string,\n items: StockRequest[],\n expiresAt: Date,\n): Promise<void> {\n await tx.insert(inventoryHolds).values(\n items.map((item) => ({\n productId: item.productId,\n quantity: item.quantity,\n reference,\n expiresAt,\n })),\n );\n}\n\nexport async function releaseHolds(\n db: CommerceExecutor,\n reference: string,\n): Promise<void> {\n await db\n .update(inventoryHolds)\n .set({ status: 'released' })\n .where(\n and(\n eq(inventoryHolds.reference, reference),\n eq(inventoryHolds.status, 'active'),\n ),\n );\n}\n\nexport interface UncommittedHold extends StockRequest {\n lapsed: boolean;\n}\n\nexport async function uncommittedHolds(\n tx: CommerceExecutor,\n reference: string,\n): Promise<UncommittedHold[]> {\n return tx\n .select({\n productId: inventoryHolds.productId,\n quantity: inventoryHolds.quantity,\n lapsed: sql<boolean>`(${inventoryHolds.status} = 'released' or ${inventoryHolds.expiresAt} <= now())`,\n })\n .from(inventoryHolds)\n .where(\n and(\n eq(inventoryHolds.reference, reference),\n inArray(inventoryHolds.status, ['active', 'released']),\n ),\n );\n}\n\nexport async function markHoldsCommitted(\n tx: CommerceExecutor,\n reference: string,\n): Promise<void> {\n await tx\n .update(inventoryHolds)\n .set({ status: 'committed' })\n .where(\n and(\n eq(inventoryHolds.reference, reference),\n inArray(inventoryHolds.status, ['active', 'released']),\n ),\n );\n}\n\nexport async function extendActiveHolds(\n db: CommerceExecutor,\n reference: string,\n expiresAt: Date,\n): Promise<void> {\n await db\n .update(inventoryHolds)\n .set({ expiresAt })\n .where(\n and(\n eq(inventoryHolds.reference, reference),\n eq(inventoryHolds.status, 'active'),\n gt(inventoryHolds.expiresAt, sql`now()`),\n ),\n );\n}\n\nexport async function decrementQuantity(\n tx: CommerceExecutor,\n productId: string,\n quantity: number,\n): Promise<void> {\n await tx\n .update(products)\n .set({\n quantity: sql`greatest(${products.quantity} - ${quantity}, 0)`,\n updatedAt: sql`now()`,\n })\n .where(eq(products.id, productId));\n}\n\nexport async function incrementQuantity(\n tx: CommerceExecutor,\n productId: string,\n quantity: number,\n): Promise<void> {\n await tx\n .update(products)\n .set({\n quantity: sql`${products.quantity} + ${quantity}`,\n updatedAt: sql`now()`,\n })\n .where(eq(products.id, productId));\n}\n\nexport async function releaseExpiredHolds(\n db: CommerceExecutor,\n): Promise<string[]> {\n const released = await db\n .update(inventoryHolds)\n .set({ status: 'released' })\n .where(\n and(\n eq(inventoryHolds.status, 'active'),\n lt(inventoryHolds.expiresAt, sql`now()`),\n ),\n )\n .returning({ reference: inventoryHolds.reference });\n return released.map((r) => r.reference);\n}\n","export interface StockRequest {\n productId: string;\n quantity: number;\n}\n\nexport interface ReserveInput {\n reference: string;\n items: StockRequest[];\n expiresAt: Date;\n}\n\nexport class InsufficientStockError extends Error {\n readonly code = 'INSUFFICIENT_STOCK';\n constructor(public readonly productIds: string[]) {\n super(`Not enough stock for: ${productIds.join(', ')}`);\n this.name = 'InsufficientStockError';\n }\n}\n","import type { CommerceExecutor } from '../db/types';\nimport {\n activeHeldQuantities,\n decrementQuantity,\n extendActiveHolds,\n incrementQuantity,\n insertHolds,\n lockProductQuantities,\n productQuantities,\n releaseExpiredHolds,\n releaseHolds,\n markHoldsCommitted,\n uncommittedHolds,\n} from './repo';\nimport {\n InsufficientStockError,\n type ReserveInput,\n type StockRequest,\n} from './types';\n\nfunction mergeRequests(items: StockRequest[]): StockRequest[] {\n const merged = new Map<string, number>();\n for (const item of items) {\n merged.set(\n item.productId,\n (merged.get(item.productId) ?? 0) + item.quantity,\n );\n }\n return [...merged].map(([productId, quantity]) => ({ productId, quantity }));\n}\n\nfunction freeOf(quantity: number, held: number): number {\n return Math.max(quantity - held, 0);\n}\n\nexport async function freeQuantity(\n db: CommerceExecutor,\n productIds: string[],\n): Promise<Map<string, number>> {\n const [quantities, held] = await Promise.all([\n productQuantities(db, productIds),\n activeHeldQuantities(db, productIds),\n ]);\n return new Map(\n [...quantities].map(([id, qty]) => [id, freeOf(qty, held.get(id) ?? 0)]),\n );\n}\n\n// Locking the product rows serializes concurrent reservations of the same\n// item, so the held total read afterwards includes every committed rival hold.\nexport async function reserve(\n db: CommerceExecutor,\n { reference, items, expiresAt }: ReserveInput,\n): Promise<void> {\n const requests = mergeRequests(items);\n const ids = requests.map((r) => r.productId);\n await db.transaction(async (tx) => {\n const quantities = await lockProductQuantities(tx, ids);\n const held = await activeHeldQuantities(tx, ids);\n const short = requests\n .filter((r) => {\n const qty = quantities.get(r.productId);\n if (qty === undefined) return true;\n return freeOf(qty, held.get(r.productId) ?? 0) < r.quantity;\n })\n .map((r) => r.productId);\n if (short.length > 0) throw new InsufficientStockError(short);\n await insertHolds(tx, reference, requests, expiresAt);\n });\n}\n\nexport async function release(\n db: CommerceExecutor,\n reference: string,\n): Promise<void> {\n await releaseHolds(db, reference);\n}\n\nexport async function releaseExpired(db: CommerceExecutor): Promise<number> {\n return (await releaseExpiredHolds(db)).length;\n}\n\nexport async function releaseExpiredReferences(\n db: CommerceExecutor,\n): Promise<string[]> {\n return [...new Set(await releaseExpiredHolds(db))];\n}\n\nexport type CommitResult = 'committed' | 'unavailable';\n\nexport type CommitOutcome =\n | { result: 'committed' }\n | { result: 'unavailable'; shortProductIds: string[] };\n\n// A payment can complete after its hold lapsed, by which time another buyer\n// may have taken the stock. Lapsed holds are re-checked against what is free\n// now; if any is short nothing is decremented and the caller must escalate.\nexport async function commitWithin(\n tx: CommerceExecutor,\n reference: string,\n): Promise<CommitOutcome> {\n const holds = await uncommittedHolds(tx, reference);\n if (holds.length === 0) return { result: 'committed' };\n const quantities = await lockProductQuantities(\n tx,\n holds.map((h) => h.productId),\n );\n const lapsed = holds.filter((h) => h.lapsed);\n const held = await activeHeldQuantities(\n tx,\n lapsed.map((h) => h.productId),\n );\n const shortProductIds = lapsed\n .filter(\n (h) =>\n freeOf(quantities.get(h.productId) ?? 0, held.get(h.productId) ?? 0) <\n h.quantity,\n )\n .map((h) => h.productId);\n if (shortProductIds.length > 0) {\n await releaseHolds(tx, reference);\n return { result: 'unavailable', shortProductIds };\n }\n for (const hold of holds) {\n await decrementQuantity(tx, hold.productId, hold.quantity);\n }\n await markHoldsCommitted(tx, reference);\n return { result: 'committed' };\n}\n\nexport async function commit(\n db: CommerceExecutor,\n reference: string,\n): Promise<CommitResult> {\n return (await db.transaction((tx) => commitWithin(tx, reference))).result;\n}\n\nexport async function extendHolds(\n db: CommerceExecutor,\n reference: string,\n expiresAt: Date,\n): Promise<void> {\n await extendActiveHolds(db, reference, expiresAt);\n}\n\nexport async function restock(\n tx: CommerceExecutor,\n items: StockRequest[],\n): Promise<void> {\n const requests = mergeRequests(items);\n if (requests.length === 0) return;\n await lockProductQuantities(\n tx,\n requests.map((r) => r.productId),\n );\n for (const r of requests) {\n await incrementQuantity(tx, r.productId, r.quantity);\n }\n}\n\nexport { InsufficientStockError } from './types';\nexport type { ReserveInput, StockRequest } from './types';\n","import { and, asc, eq, gt, inArray, isNotNull, or, sql } from 'drizzle-orm';\nimport type { CommerceDb } from '../db/types';\nimport { productImages, products } from '../db/schema';\nimport type { CatalogFilter } from './types';\n\nconst isListed = and(\n eq(products.status, 'active'),\n isNotNull(products.priceCents),\n isNotNull(products.category),\n)!;\n\nexport type ProductRow = typeof products.$inferSelect;\nexport type ProductImageRow = typeof productImages.$inferSelect;\nexport interface ProductWithImages {\n row: ProductRow;\n images: ProductImageRow[];\n createdAtKey?: string;\n}\n\nasync function attachImages(\n db: CommerceDb,\n rows: ProductRow[],\n firstOnly = false,\n): Promise<ProductWithImages[]> {\n if (rows.length === 0) return [];\n const where = inArray(\n productImages.productId,\n rows.map((r) => r.id),\n );\n const images = firstOnly\n ? await db\n .selectDistinctOn([productImages.productId])\n .from(productImages)\n .where(where)\n .orderBy(productImages.productId, asc(productImages.position))\n : await db\n .select()\n .from(productImages)\n .where(where)\n .orderBy(asc(productImages.position));\n return rows.map((row) => ({\n row,\n images: images.filter((i) => i.productId === row.id),\n }));\n}\n\nexport interface ProductPageFilter extends CatalogFilter {\n after?: { createdAt: string; slug: string };\n}\n\nexport async function listActiveProducts(\n db: CommerceDb,\n filter: ProductPageFilter,\n): Promise<ProductWithImages[]> {\n const conditions = [isListed];\n if (filter.category) conditions.push(eq(products.category, filter.category));\n if (filter.featured !== undefined)\n conditions.push(eq(products.featured, filter.featured));\n if (filter.after) {\n const createdAt = sql`${filter.after.createdAt}::timestamptz`;\n conditions.push(\n or(\n gt(products.createdAt, createdAt),\n and(\n eq(products.createdAt, createdAt),\n gt(products.slug, filter.after.slug),\n ),\n )!,\n );\n }\n const selected = await db\n .select({\n product: products,\n createdAtKey: sql<string>`${products.createdAt}::text`,\n })\n .from(products)\n .where(and(...conditions))\n .orderBy(asc(products.createdAt), asc(products.slug))\n .limit(filter.limit + 1);\n const withImages = await attachImages(\n db,\n selected.map((s) => s.product),\n true,\n );\n return withImages.map((p, i) => ({\n ...p,\n createdAtKey: selected[i].createdAtKey,\n }));\n}\n\nexport async function findActiveProductBySlug(\n db: CommerceDb,\n slug: string,\n): Promise<ProductWithImages | undefined> {\n const rows = await db\n .select()\n .from(products)\n .where(and(eq(products.slug, slug), isListed))\n .limit(1);\n return (await attachImages(db, rows))[0];\n}\n\nexport async function countActiveByCategory(\n db: CommerceDb,\n): Promise<{ category: string; count: number }[]> {\n const rows = await db\n .select({\n category: products.category,\n count: sql<number>`count(*)::int`,\n })\n .from(products)\n .where(isListed)\n .groupBy(products.category)\n .orderBy(asc(products.category));\n return rows.flatMap((r) =>\n r.category === null ? [] : [{ category: r.category, count: r.count }],\n );\n}\n\nexport async function findActiveProductsByIds(\n db: CommerceDb,\n ids: string[],\n): Promise<ProductRow[]> {\n if (ids.length === 0) return [];\n return db\n .select()\n .from(products)\n .where(and(inArray(products.id, ids), isListed));\n}\n","import type {\n Category,\n Product,\n ProductCard,\n ProductCardPage,\n} from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport { decodeCursor, encodeCursor, PG_TIMESTAMP } from '../shared/cursor';\nimport { freeQuantity } from '../inventory/service';\nimport {\n countActiveByCategory,\n findActiveProductBySlug,\n findActiveProductsByIds,\n listActiveProducts,\n type ProductWithImages,\n} from './repo';\nimport {\n ProductNotFoundError,\n type CatalogFilter,\n type CategoryLabels,\n type PurchasableProduct,\n} from './types';\n\nfunction completeFields(row: {\n id: string;\n priceCents: number | null;\n category: string | null;\n}) {\n if (row.priceCents === null || row.category === null) {\n throw new Error(`Active product ${row.id} is missing price or category`);\n }\n return { priceCents: row.priceCents, category: row.category };\n}\n\nfunction availabilityOf(quantity: number, free: number) {\n if (free > 0) return 'available' as const;\n return quantity > 0 ? ('reserved' as const) : ('sold' as const);\n}\n\nfunction toProduct(\n { row, images }: ProductWithImages,\n free: Map<string, number>,\n): Product {\n const freeQty = free.get(row.id) ?? row.quantity;\n return {\n id: row.id,\n slug: row.slug,\n name: row.name,\n description: row.description,\n ...(row.materials ? { materials: row.materials } : {}),\n ...(row.dimensions ? { dimensions: row.dimensions } : {}),\n ...completeFields(row),\n currency: row.currency,\n images: images.map((i) => ({ url: i.url, alt: i.alt })),\n quantity: freeQty,\n availability: availabilityOf(row.quantity, freeQty),\n featured: row.featured,\n };\n}\n\nfunction toCard(product: Product): ProductCard {\n const { description, materials, dimensions, ...card } = product;\n void [description, materials, dimensions];\n return card;\n}\n\nfunction labelFor(slug: string, labels?: CategoryLabels): string {\n const label = typeof labels === 'function' ? labels(slug) : labels?.[slug];\n return label ?? slug;\n}\n\nfunction decodeProductCursor(raw: string): { createdAt: string; slug: string } {\n const { at, key } = decodeCursor(raw, ({ at }) => PG_TIMESTAMP.test(at));\n return { createdAt: at, slug: key };\n}\n\nexport async function listProducts(\n db: CommerceDb,\n filter: CatalogFilter,\n): Promise<ProductCardPage> {\n const found = await listActiveProducts(db, {\n ...filter,\n after: filter.cursor ? decodeProductCursor(filter.cursor) : undefined,\n });\n const page = found.slice(0, filter.limit);\n const last = page[page.length - 1];\n const free = await freeQuantity(\n db,\n page.map((p) => p.row.id),\n );\n return {\n items: page.map((p) => toCard(toProduct(p, free))),\n nextCursor:\n found.length > filter.limit && last\n ? encodeCursor({ at: last.createdAtKey ?? '', key: last.row.slug })\n : null,\n };\n}\n\nexport async function getProduct(\n db: CommerceDb,\n slug: string,\n): Promise<Product> {\n const found = await findActiveProductBySlug(db, slug);\n if (!found) throw new ProductNotFoundError(slug);\n return toProduct(found, await freeQuantity(db, [found.row.id]));\n}\n\nexport async function listCategories(\n db: CommerceDb,\n labels?: CategoryLabels,\n): Promise<Category[]> {\n return (await countActiveByCategory(db)).map((c) => ({\n slug: c.category,\n label: labelFor(c.category, labels),\n count: c.count,\n }));\n}\n\nexport async function findPurchasable(\n db: CommerceDb,\n ids: string[],\n): Promise<PurchasableProduct[]> {\n const rows = await findActiveProductsByIds(db, ids);\n return rows.map((row) => ({\n id: row.id,\n name: row.name,\n priceCents: completeFields(row).priceCents,\n currency: row.currency,\n taxCode: row.taxCode,\n }));\n}\n","import {\n adminOrderListQuerySchema,\n adminOrderPageSchema,\n refundOrderInputSchema,\n shipOrderInputSchema,\n} from '@develemit/commerce-contracts';\nimport type { FastifyPluginAsync, FastifyRequest } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport { InsufficientStockError } from '../inventory/types';\nimport type { PaymentGateway } from '../payment/gateway';\nimport { InvalidCursorError } from '../shared/cursor';\nimport {\n AdminOrderNotFoundError,\n getAdminOrder,\n InvalidOrderIdError,\n isOrderId,\n listAdminOrders,\n} from './admin-service';\nimport {\n IdempotencyKeyRequiredError,\n RefundExceedsBalanceError,\n RefundGatewayError,\n RefundOutcomeUnknownError,\n RestockNotApplicableError,\n RestockRequiresFullRefundError,\n} from './refund-errors';\nimport { refundOrder } from './refund-service';\nimport { resolveOrder } from './resolve-service';\nimport { InvalidOrderStateError, shipOrder } from './ship-service';\nimport type { HookLogger, OrderHooks } from './types';\n\nexport interface AdminOrdersPluginOptions {\n db: CommerceDb;\n authorize(req: FastifyRequest): Promise<boolean>;\n payment: PaymentGateway;\n hooks?: OrderHooks;\n}\n\nfunction idempotencyKey(req: FastifyRequest): string {\n const key = req.headers['idempotency-key'];\n if (typeof key !== 'string' || key.length === 0 || key.length > 255) {\n throw new IdempotencyKeyRequiredError();\n }\n return key;\n}\n\nconst BAD_REQUEST = [\n InvalidCursorError,\n InvalidOrderIdError,\n IdempotencyKeyRequiredError,\n RestockRequiresFullRefundError,\n RestockNotApplicableError,\n];\n\nfunction isZodError(err: unknown): err is Error & { issues: unknown[] } {\n return err instanceof Error && err.name === 'ZodError';\n}\n\nexport const adminOrdersPlugin: FastifyPluginAsync<\n AdminOrdersPluginOptions\n> = async (app, { db, authorize, payment, hooks }) => {\n app.setErrorHandler((err, _req, reply) => {\n if (isZodError(err)) {\n return reply\n .code(400)\n .send({ error: 'INVALID_INPUT', issues: err.issues });\n }\n if (BAD_REQUEST.some((type) => err instanceof type)) {\n const { code, message } = err as Error & { code: string };\n return reply.code(400).send({ error: code, message });\n }\n if (err instanceof RefundExceedsBalanceError) {\n return reply.code(400).send({\n error: err.code,\n message: err.message,\n balanceCents: err.balanceCents,\n });\n }\n if (\n err instanceof RefundGatewayError ||\n err instanceof RefundOutcomeUnknownError\n ) {\n return reply.code(502).send({ error: err.code, message: err.message });\n }\n if (err instanceof InvalidOrderStateError) {\n return reply\n .code(409)\n .send({ error: err.code, message: err.message, status: err.status });\n }\n if (err instanceof InsufficientStockError) {\n return reply.code(409).send({\n error: 'INSUFFICIENT_STOCK',\n message: err.message,\n productIds: err.productIds,\n });\n }\n if (err instanceof AdminOrderNotFoundError) {\n return reply.code(404).send({ error: err.code, message: err.message });\n }\n return reply.send(err);\n });\n\n app.addHook('preHandler', async (req, reply) => {\n if (!(await authorize(req))) {\n return reply.code(401).send({ error: 'UNAUTHORIZED' });\n }\n });\n\n app.get('/', async (req) =>\n adminOrderPageSchema.parse(\n await listAdminOrders(db, adminOrderListQuerySchema.parse(req.query)),\n ),\n );\n\n app.get<{ Params: { id: string } }>('/:id', async (req) => {\n if (!isOrderId(req.params.id)) throw new InvalidOrderIdError();\n return getAdminOrder(db, req.params.id);\n });\n\n app.post<{ Params: { id: string } }>('/:id/ship', async (req) => {\n if (!isOrderId(req.params.id)) throw new InvalidOrderIdError();\n const log: HookLogger = req.log;\n return shipOrder(\n db,\n req.params.id,\n shipOrderInputSchema.parse(req.body ?? {}),\n hooks,\n log,\n );\n });\n\n app.post<{ Params: { id: string } }>('/:id/resolve', async (req) => {\n if (!isOrderId(req.params.id)) throw new InvalidOrderIdError();\n const log: HookLogger = req.log;\n return resolveOrder(db, req.params.id, hooks, log);\n });\n\n app.post<{ Params: { id: string } }>('/:id/refunds', async (req) => {\n if (!isOrderId(req.params.id)) throw new InvalidOrderIdError();\n const key = idempotencyKey(req);\n const log: HookLogger = req.log;\n return refundOrder(\n db,\n payment,\n req.params.id,\n refundOrderInputSchema.parse(req.body ?? {}),\n key,\n hooks,\n log,\n );\n });\n};\n","import { orderRefSearchTerm } from '@develemit/commerce-contracts';\nimport type {\n AdminOrderDetail,\n AdminOrderListQuery,\n AdminOrderPage,\n OrderRefund,\n} from '@develemit/commerce-contracts';\nimport type { CommerceExecutor } from '../db/types';\nimport {\n decodeCursor,\n encodeCursor,\n isCursorTimestamp,\n} from '../shared/cursor';\nimport { findOrderRefunds, selectOrderPage } from './admin-repo';\nimport { detailById } from './order-detail';\nimport type { RefundRow } from './refund-repo';\nimport { findOrderById, sumRefundedCents } from './repo';\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport class InvalidOrderIdError extends Error {\n readonly code = 'INVALID_ORDER_ID';\n constructor() {\n super('The order id is malformed');\n this.name = 'InvalidOrderIdError';\n }\n}\n\nexport class AdminOrderNotFoundError extends Error {\n readonly code = 'ORDER_NOT_FOUND';\n constructor(public readonly orderId: string) {\n super(`No order: ${orderId}`);\n this.name = 'AdminOrderNotFoundError';\n }\n}\n\nexport function toOrderRefund(row: RefundRow): OrderRefund {\n return {\n id: row.id,\n amountCents: row.amountCents,\n reason: row.reason,\n restocked: row.restocked,\n source: row.source,\n status:\n row.idempotencyKey && !row.gatewayRefundId ? 'pending' : 'succeeded',\n createdAt: row.createdAt.toISOString(),\n };\n}\n\nexport function isOrderId(value: string): boolean {\n return UUID.test(value);\n}\n\nfunction decodeOrderCursor(raw: string): { createdAt: string; id: string } {\n const { at, key } = decodeCursor(\n raw,\n (c) => isOrderId(c.key) && isCursorTimestamp(c.at),\n );\n return { createdAt: at, id: key };\n}\n\nexport async function listAdminOrders(\n db: CommerceExecutor,\n query: AdminOrderListQuery,\n): Promise<AdminOrderPage> {\n const rows = await selectOrderPage(db, {\n status: query.status,\n q: query.q ? orderRefSearchTerm(query.q) || undefined : undefined,\n hasPendingRefund: query.hasPendingRefund,\n after: query.cursor ? decodeOrderCursor(query.cursor) : undefined,\n limit: query.limit + 1,\n });\n const page = rows.slice(0, query.limit);\n const last = page[page.length - 1];\n return {\n items: page.map((r) => ({\n id: r.id,\n status: r.status,\n email: r.email,\n taxCents: r.taxCents,\n totalCents: r.totalCents,\n currency: r.currency,\n itemCount: r.itemCount,\n createdAt: r.createdAt.toISOString(),\n })),\n nextCursor:\n rows.length > query.limit && last\n ? encodeCursor({ at: last.createdAtKey, key: last.id })\n : null,\n };\n}\n\nexport async function getAdminOrder(\n db: CommerceExecutor,\n id: string,\n): Promise<AdminOrderDetail> {\n const detail = await detailById(db, id);\n const row = await findOrderById(db, id);\n if (!detail || !row) throw new AdminOrderNotFoundError(id);\n const [refunds, refundedCents] = await Promise.all([\n findOrderRefunds(db, id),\n sumRefundedCents(db, id),\n ]);\n return {\n ...detail,\n refunds: refunds.map(toOrderRefund),\n refundedCents,\n checkoutSessionId: row.checkoutSessionId,\n updatedAt: row.updatedAt.toISOString(),\n };\n}\n","import { and, desc, eq, ilike, lt, or, sql, type SQL } from 'drizzle-orm';\nimport type { CommerceExecutor } from '../db/types';\nimport { orderRefunds, orders } from '../db/schema';\nimport type { OrderRow } from './repo';\n\nexport type OrderStatus = OrderRow['status'];\n\nexport interface OrderListFilter {\n status?: OrderStatus;\n q?: string;\n hasPendingRefund?: boolean;\n after?: { createdAt: string; id: string };\n limit: number;\n}\n\nexport interface OrderListRow {\n id: string;\n status: OrderStatus;\n email: string | null;\n taxCents: number;\n totalCents: number;\n currency: string;\n itemCount: number;\n createdAt: Date;\n createdAtKey: string;\n}\n\nfunction escapeLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (c) => `\\\\${c}`);\n}\n\nfunction conditions(filter: OrderListFilter): SQL | undefined {\n const parts: (SQL | undefined)[] = [];\n if (filter.status) parts.push(eq(orders.status, filter.status));\n if (filter.hasPendingRefund !== undefined) {\n const pending = sql`exists (\n select 1 from commerce_order_refunds r\n where r.order_id = commerce_orders.id and r.idempotency_key is not null\n and r.gateway_refund_id is null\n )`;\n parts.push(filter.hasPendingRefund ? pending : sql`not ${pending}`);\n }\n if (filter.q) {\n const prefix = `${escapeLike(filter.q)}%`;\n parts.push(\n or(ilike(orders.email, prefix), sql`${orders.id}::text ilike ${prefix}`),\n );\n }\n if (filter.after) {\n const { id } = filter.after;\n const createdAt = sql`${filter.after.createdAt}::timestamptz`;\n parts.push(\n or(\n lt(orders.createdAt, createdAt),\n and(eq(orders.createdAt, createdAt), lt(orders.id, id)),\n ),\n );\n }\n return and(...parts);\n}\n\nexport async function selectOrderPage(\n db: CommerceExecutor,\n filter: OrderListFilter,\n): Promise<OrderListRow[]> {\n const itemCount = sql<number>`coalesce((\n select sum(i.quantity) from commerce_order_items i\n where i.order_id = commerce_orders.id\n ), 0)::int`;\n return db\n .select({\n id: orders.id,\n status: orders.status,\n email: orders.email,\n taxCents: orders.taxCents,\n totalCents: sql<number>`${orders.subtotalCents} - ${orders.discountCents} + ${orders.shippingCents} + ${orders.taxCents}`,\n currency: orders.currency,\n itemCount,\n createdAt: orders.createdAt,\n createdAtKey: sql<string>`${orders.createdAt}::text`,\n })\n .from(orders)\n .where(conditions(filter))\n .orderBy(desc(orders.createdAt), desc(orders.id))\n .limit(filter.limit);\n}\n\nexport async function findOrderRefunds(db: CommerceExecutor, orderId: string) {\n return db\n .select()\n .from(orderRefunds)\n .where(eq(orderRefunds.orderId, orderId))\n .orderBy(orderRefunds.createdAt, orderRefunds.id);\n}\n","import { and, asc, eq, sql } from 'drizzle-orm';\nimport type { CommerceExecutor } from '../db/types';\nimport { orderItems, orderRefunds, orders, stripeEvents } from '../db/schema';\nimport type { ChosenShipping, ShippingAddress } from '../payment/gateway';\nimport { isRecordedRefund } from './refund-repo';\n\nexport type OrderRow = typeof orders.$inferSelect;\nexport type OrderItemRow = typeof orderItems.$inferSelect;\ntype NewOrder = typeof orders.$inferInsert;\ntype NewOrderItem = Omit<typeof orderItems.$inferInsert, 'orderId'>;\ntype OrderStatus = OrderRow['status'];\n\nexport async function insertOrder(\n db: CommerceExecutor,\n order: NewOrder & { id: string },\n items: NewOrderItem[],\n): Promise<void> {\n await db.insert(orders).values(order);\n await db\n .insert(orderItems)\n .values(items.map((item) => ({ ...item, orderId: order.id })));\n}\n\nexport async function setCheckoutSession(\n db: CommerceExecutor,\n orderId: string,\n sessionId: string,\n): Promise<void> {\n await db\n .update(orders)\n .set({ checkoutSessionId: sessionId, updatedAt: sql`now()` })\n .where(eq(orders.id, orderId));\n}\n\nexport async function setOrderStatus(\n db: CommerceExecutor,\n orderId: string,\n status: OrderStatus,\n): Promise<void> {\n await db\n .update(orders)\n .set({ status, updatedAt: sql`now()` })\n .where(eq(orders.id, orderId));\n}\n\ninterface OrderDetails {\n email?: string;\n shippingAddress?: ShippingAddress;\n shipping?: ChosenShipping;\n taxCents?: number;\n}\n\nexport async function saveOrderDetails(\n db: CommerceExecutor,\n orderId: string,\n details: OrderDetails,\n status?: OrderStatus,\n): Promise<void> {\n await db\n .update(orders)\n .set({\n ...(status && { status }),\n email: details.email ?? null,\n shippingAddress: details.shippingAddress ?? null,\n shippingCents: details.shipping?.amountCents ?? 0,\n shippingLabel: details.shipping?.label ?? null,\n taxCents: details.taxCents ?? 0,\n updatedAt: sql`now()`,\n })\n .where(eq(orders.id, orderId));\n}\n\nexport async function findOrderBySession(\n db: CommerceExecutor,\n sessionId: string,\n): Promise<OrderRow | undefined> {\n const [row] = await db\n .select()\n .from(orders)\n .where(eq(orders.checkoutSessionId, sessionId))\n .limit(1);\n return row;\n}\n\nexport async function findOrderById(\n db: CommerceExecutor,\n orderId: string,\n): Promise<OrderRow | undefined> {\n const [row] = await db\n .select()\n .from(orders)\n .where(eq(orders.id, orderId))\n .limit(1);\n return row;\n}\n\nexport async function findOrderItems(\n db: CommerceExecutor,\n orderId: string,\n): Promise<OrderItemRow[]> {\n return db\n .select()\n .from(orderItems)\n .where(eq(orderItems.orderId, orderId))\n .orderBy(asc(orderItems.name));\n}\n\nexport async function recordEvent(\n db: CommerceExecutor,\n id: string,\n type: string,\n): Promise<boolean> {\n const inserted = await db\n .insert(stripeEvents)\n .values({ id, type })\n .onConflictDoNothing()\n .returning({ id: stripeEvents.id });\n return inserted.length > 0;\n}\n\nexport async function sumRefundedCents(\n db: CommerceExecutor,\n orderId: string,\n): Promise<number> {\n const [row] = await db\n .select({\n total: sql<number>`coalesce(sum(${orderRefunds.amountCents}), 0)::int`,\n })\n .from(orderRefunds)\n .where(and(eq(orderRefunds.orderId, orderId), isRecordedRefund));\n return row.total;\n}\n","import { and, eq, isNotNull, isNull, or, sql } from 'drizzle-orm';\nimport { orderRefunds, orders } from '../db/schema';\nimport type { CommerceExecutor } from '../db/types';\n\nexport type RefundRow = typeof orderRefunds.$inferSelect;\ntype NewRefund = typeof orderRefunds.$inferInsert;\n\n// An admin refund is written as a pending row (key set, no gateway id) before\n// the gateway call so its amount counts against the balance while in flight.\nexport const isRecordedRefund = or(\n isNull(orderRefunds.idempotencyKey),\n isNotNull(orderRefunds.gatewayRefundId),\n);\n\nexport async function lockOrderForRefund(tx: CommerceExecutor, id: string) {\n const [row] = await tx\n .select({\n status: orders.status,\n totalCents: sql<number>`${orders.subtotalCents} - ${orders.discountCents} + ${orders.shippingCents} + ${orders.taxCents}`,\n currency: orders.currency,\n checkoutSessionId: orders.checkoutSessionId,\n })\n .from(orders)\n .where(eq(orders.id, id))\n .for('update');\n return row;\n}\n\nexport async function sumReservedCents(\n tx: CommerceExecutor,\n orderId: string,\n): Promise<number> {\n const [row] = await tx\n .select({\n total: sql<number>`coalesce(sum(${orderRefunds.amountCents}), 0)::int`,\n })\n .from(orderRefunds)\n .where(eq(orderRefunds.orderId, orderId));\n return row.total;\n}\n\nexport async function findRefundByKey(\n tx: CommerceExecutor,\n orderId: string,\n idempotencyKey: string,\n): Promise<RefundRow | undefined> {\n const [row] = await tx\n .select()\n .from(orderRefunds)\n .where(\n and(\n eq(orderRefunds.orderId, orderId),\n eq(orderRefunds.idempotencyKey, idempotencyKey),\n ),\n );\n return row;\n}\n\nexport async function insertPendingRefund(\n tx: CommerceExecutor,\n refund: NewRefund & { idempotencyKey: string },\n): Promise<RefundRow> {\n const [row] = await tx.insert(orderRefunds).values(refund).returning();\n return row;\n}\n\nexport async function deletePendingRefund(\n db: CommerceExecutor,\n id: string,\n): Promise<void> {\n await db\n .delete(orderRefunds)\n .where(and(eq(orderRefunds.id, id), isNull(orderRefunds.gatewayRefundId)));\n}\n\n// A concurrent retry that failed may have deleted the pending row, so fall\n// back to inserting; the unique gateway id makes a second record a no-op.\nexport async function confirmRefund(\n tx: CommerceExecutor,\n pending: RefundRow,\n gatewayRefundId: string,\n): Promise<RefundRow | undefined> {\n const [existing] = await tx\n .select({ id: orderRefunds.id })\n .from(orderRefunds)\n .where(eq(orderRefunds.gatewayRefundId, gatewayRefundId));\n if (existing) {\n await deletePendingRefund(tx, pending.id);\n return undefined;\n }\n const [updated] = await tx\n .update(orderRefunds)\n .set({ gatewayRefundId })\n .where(\n and(\n eq(orderRefunds.id, pending.id),\n isNull(orderRefunds.gatewayRefundId),\n ),\n )\n .returning();\n if (updated) return updated;\n const [inserted] = await tx\n .insert(orderRefunds)\n .values({\n orderId: pending.orderId,\n amountCents: pending.amountCents,\n currency: pending.currency,\n reason: pending.reason,\n restocked: pending.restocked,\n source: pending.source,\n idempotencyKey: pending.idempotencyKey,\n gatewayRefundId,\n })\n .onConflictDoNothing()\n .returning();\n return inserted;\n}\n\nexport async function findRefundByGatewayId(\n tx: CommerceExecutor,\n gatewayRefundId: string,\n): Promise<RefundRow | undefined> {\n const [row] = await tx\n .select()\n .from(orderRefunds)\n .where(eq(orderRefunds.gatewayRefundId, gatewayRefundId));\n return row;\n}\n\n// Matches an in-flight admin refund by the scoped key it sent the gateway\n// (`orderId:idempotencyKey`) and fills in its gateway id.\nexport async function confirmPendingByKey(\n tx: CommerceExecutor,\n orderId: string,\n scopedKey: string,\n gatewayRefundId: string,\n): Promise<RefundRow | undefined> {\n const prefix = `${orderId}:`;\n if (!scopedKey.startsWith(prefix)) return undefined;\n const [updated] = await tx\n .update(orderRefunds)\n .set({ gatewayRefundId })\n .where(\n and(\n eq(orderRefunds.orderId, orderId),\n eq(orderRefunds.idempotencyKey, scopedKey.slice(prefix.length)),\n isNull(orderRefunds.gatewayRefundId),\n ),\n )\n .returning();\n return updated;\n}\n\nexport async function insertGatewayRefund(\n tx: CommerceExecutor,\n refund: Pick<RefundRow, 'orderId' | 'amountCents' | 'currency'> & {\n gatewayRefundId: string;\n },\n): Promise<RefundRow | undefined> {\n const [row] = await tx\n .insert(orderRefunds)\n .values({ ...refund, source: 'gateway', restocked: false })\n .onConflictDoNothing()\n .returning();\n return row;\n}\n","import type { OrderDetail, OrderSummary } from '@develemit/commerce-contracts';\nimport type { CommerceExecutor } from '../db/types';\nimport type { ShippingAddress } from '../payment/gateway';\nimport {\n findOrderById,\n findOrderBySession,\n findOrderItems,\n type OrderRow,\n} from './repo';\n\nfunction toFulfillment(order: OrderRow): OrderSummary['fulfillment'] {\n if (!order.shippedAt) return null;\n return {\n carrier: order.fulfillmentCarrier,\n trackingNumber: order.fulfillmentTrackingNumber,\n trackingUrl: order.fulfillmentTrackingUrl,\n shippedAt: order.shippedAt.toISOString(),\n };\n}\n\nasync function toSummary(\n db: CommerceExecutor,\n order: OrderRow,\n): Promise<OrderSummary> {\n const items = await findOrderItems(db, order.id);\n return {\n id: order.id,\n status: order.status,\n subtotalCents: order.subtotalCents,\n discountCents: order.discountCents,\n discountCode: order.discountCode,\n shippingCents: order.shippingCents,\n shippingLabel: order.shippingLabel,\n taxCents: order.taxCents,\n totalCents:\n order.subtotalCents -\n order.discountCents +\n order.shippingCents +\n order.taxCents,\n currency: order.currency,\n items: items.map((i) => ({\n productId: i.productId,\n name: i.name,\n priceCents: i.priceCents,\n quantity: i.quantity,\n })),\n fulfillment: toFulfillment(order),\n createdAt: order.createdAt.toISOString(),\n };\n}\n\nexport async function summaryBySession(\n db: CommerceExecutor,\n sessionId: string,\n): Promise<OrderSummary | undefined> {\n const order = await findOrderBySession(db, sessionId);\n return order && toSummary(db, order);\n}\n\nexport async function detailById(\n db: CommerceExecutor,\n orderId: string,\n): Promise<OrderDetail | undefined> {\n const order = await findOrderById(db, orderId);\n if (!order) return undefined;\n return {\n ...(await toSummary(db, order)),\n email: order.email,\n shippingAddress: (order.shippingAddress as ShippingAddress | null) ?? null,\n };\n}\n","export class IdempotencyKeyRequiredError extends Error {\n readonly code = 'IDEMPOTENCY_KEY_REQUIRED';\n constructor() {\n super('An Idempotency-Key header of 1-255 characters is required');\n this.name = 'IdempotencyKeyRequiredError';\n }\n}\n\nexport class RefundExceedsBalanceError extends Error {\n readonly code = 'REFUND_EXCEEDS_BALANCE';\n constructor(public readonly balanceCents: number) {\n super(`The refundable balance is ${balanceCents} cents`);\n this.name = 'RefundExceedsBalanceError';\n }\n}\n\nexport class RestockRequiresFullRefundError extends Error {\n readonly code = 'RESTOCK_REQUIRES_FULL_REFUND';\n constructor() {\n super('Restock is only allowed when refunding the remaining balance');\n this.name = 'RestockRequiresFullRefundError';\n }\n}\n\nexport class RestockNotApplicableError extends Error {\n readonly code = 'RESTOCK_NOT_APPLICABLE';\n constructor() {\n super('This order never took stock, so there is nothing to restock');\n this.name = 'RestockNotApplicableError';\n }\n}\n\nexport class RefundGatewayError extends Error {\n readonly code = 'REFUND_GATEWAY_ERROR';\n constructor(cause: unknown) {\n super('The payment gateway could not create the refund', { cause });\n this.name = 'RefundGatewayError';\n }\n}\n\nexport class RefundOutcomeUnknownError extends Error {\n readonly code = 'REFUND_OUTCOME_UNKNOWN';\n constructor(cause: unknown) {\n super(\n 'The payment gateway did not confirm the refund and it may still have happened. Retry with the same Idempotency-Key or wait for the charge.refunded webhook',\n { cause },\n );\n this.name = 'RefundOutcomeUnknownError';\n }\n}\n","import type { DiscountRejectionReason } from '@develemit/commerce-contracts';\n\nexport interface EvaluableDiscount {\n type: 'percent' | 'fixed';\n percentOff: number | null;\n amountOffCents: number | null;\n currency: string | null;\n minSubtotalCents: number | null;\n startsAt: Date | null;\n endsAt: Date | null;\n maxRedemptions: number | null;\n active: boolean;\n}\n\nexport interface EvaluationContext {\n subtotalCents: number;\n currency: string;\n now: Date;\n activeRedemptions: number;\n}\n\nexport type Evaluation =\n | { ok: true; discountCents: number }\n | { ok: false; reason: DiscountRejectionReason };\n\nfunction rejection(reason: DiscountRejectionReason): Evaluation {\n return { ok: false, reason };\n}\n\nexport function discountAmount(\n discount: EvaluableDiscount,\n subtotalCents: number,\n): number {\n if (discount.type === 'percent') {\n return Math.floor((subtotalCents * (discount.percentOff ?? 0)) / 100);\n }\n return Math.min(discount.amountOffCents ?? 0, subtotalCents);\n}\n\nexport function evaluateDiscount(\n discount: EvaluableDiscount | undefined,\n ctx: EvaluationContext,\n): Evaluation {\n if (!discount) return rejection('not_found');\n if (!discount.active) return rejection('inactive');\n if (discount.startsAt && ctx.now < discount.startsAt) {\n return rejection('not_started');\n }\n if (discount.endsAt && ctx.now >= discount.endsAt) {\n return rejection('expired');\n }\n if (discount.type === 'fixed' && discount.currency !== ctx.currency) {\n return rejection('currency');\n }\n if (\n discount.minSubtotalCents !== null &&\n ctx.subtotalCents < discount.minSubtotalCents\n ) {\n return rejection('min_subtotal');\n }\n if (\n discount.maxRedemptions !== null &&\n ctx.activeRedemptions >= discount.maxRedemptions\n ) {\n return rejection('exhausted');\n }\n return {\n ok: true,\n discountCents: discountAmount(discount, ctx.subtotalCents),\n };\n}\n","import {\n and,\n desc,\n eq,\n getTableColumns,\n gt,\n ilike,\n inArray,\n isNull,\n lt,\n lte,\n or,\n sql,\n type SQL,\n} from 'drizzle-orm';\nimport { discountRedemptions, discounts } from '../db/schema';\nimport type { CommerceExecutor } from '../db/types';\n\nexport type DiscountRow = typeof discounts.$inferSelect;\nexport type RedemptionRow = typeof discountRedemptions.$inferSelect;\n\nexport async function findDiscountByCode(\n db: CommerceExecutor,\n code: string,\n options: { forUpdate?: boolean } = {},\n): Promise<DiscountRow | undefined> {\n const query = db.select().from(discounts).where(eq(discounts.code, code));\n const [row] = await (options.forUpdate ? query.for('update') : query);\n return row;\n}\n\nexport async function countActiveRedemptions(\n db: CommerceExecutor,\n discountId: string,\n): Promise<number> {\n const [row] = await db\n .select({ n: sql<number>`count(*)::int` })\n .from(discountRedemptions)\n .where(\n and(\n eq(discountRedemptions.discountId, discountId),\n inArray(discountRedemptions.status, ['reserved', 'committed']),\n ),\n );\n return row.n;\n}\n\nexport async function insertRedemption(\n db: CommerceExecutor,\n redemption: { discountId: string; orderId: string; amountCents: number },\n): Promise<void> {\n await db.insert(discountRedemptions).values(redemption);\n}\n\nexport async function transitionRedemptions(\n db: CommerceExecutor,\n orderIds: string[],\n from: RedemptionRow['status'][],\n to: RedemptionRow['status'],\n): Promise<void> {\n if (orderIds.length === 0) return;\n await db\n .update(discountRedemptions)\n .set({ status: to, updatedAt: sql`now()` })\n .where(\n and(\n inArray(discountRedemptions.orderId, orderIds),\n inArray(discountRedemptions.status, from),\n ),\n );\n}\n\nexport type DiscountStatus = 'active' | 'inactive' | 'scheduled' | 'expired';\n\nexport interface DiscountListFilter {\n status?: DiscountStatus;\n q?: string;\n now: Date;\n after?: { createdAt: string; id: string };\n limit: number;\n}\n\nexport type DiscountWithCounts = DiscountRow & {\n redeemedCount: number;\n reservedCount: number;\n totalRedemptions: number;\n};\n\nfunction escapeLike(value: string): string {\n return value.replace(/[\\\\%_]/g, (c) => `\\\\${c}`);\n}\n\nfunction statusCondition(status: DiscountStatus, now: Date): SQL | undefined {\n const started = or(isNull(discounts.startsAt), lte(discounts.startsAt, now));\n const open = or(isNull(discounts.endsAt), gt(discounts.endsAt, now));\n if (status === 'inactive') return eq(discounts.active, false);\n if (status === 'scheduled') {\n return and(eq(discounts.active, true), gt(discounts.startsAt, now));\n }\n if (status === 'expired') {\n return and(eq(discounts.active, true), lte(discounts.endsAt, now));\n }\n return and(eq(discounts.active, true), started, open);\n}\n\nfunction listConditions(filter: DiscountListFilter): SQL | undefined {\n const parts: (SQL | undefined)[] = [];\n if (filter.status) parts.push(statusCondition(filter.status, filter.now));\n if (filter.q) parts.push(ilike(discounts.code, `${escapeLike(filter.q)}%`));\n if (filter.after) {\n const { id } = filter.after;\n const createdAt = sql`${filter.after.createdAt}::timestamptz`;\n parts.push(\n or(\n lt(discounts.createdAt, createdAt),\n and(eq(discounts.createdAt, createdAt), lt(discounts.id, id)),\n ),\n );\n }\n return and(...parts);\n}\n\nfunction redemptionCount(status?: 'reserved' | 'committed') {\n const filter = status ? sql`and r.status = ${status}` : sql``;\n return sql<number>`(\n select count(*) from commerce_discount_redemptions r\n where r.discount_id = commerce_discounts.id ${filter}\n )::int`;\n}\n\nconst withCounts = {\n ...getTableColumns(discounts),\n redeemedCount: redemptionCount('committed'),\n reservedCount: redemptionCount('reserved'),\n totalRedemptions: redemptionCount(),\n};\n\nexport async function selectDiscountPage(\n db: CommerceExecutor,\n filter: DiscountListFilter,\n): Promise<(DiscountWithCounts & { createdAtKey: string })[]> {\n return db\n .select({\n ...withCounts,\n createdAtKey: sql<string>`${discounts.createdAt}::text`,\n })\n .from(discounts)\n .where(listConditions(filter))\n .orderBy(desc(discounts.createdAt), desc(discounts.id))\n .limit(filter.limit);\n}\n\nexport async function findDiscountById(\n db: CommerceExecutor,\n id: string,\n options: { forUpdate?: boolean } = {},\n): Promise<DiscountWithCounts | undefined> {\n const query = db\n .select(withCounts)\n .from(discounts)\n .where(eq(discounts.id, id));\n const [row] = await (options.forUpdate ? query.for('update') : query);\n return row;\n}\n\nexport async function insertDiscount(\n db: CommerceExecutor,\n values: typeof discounts.$inferInsert,\n): Promise<string | undefined> {\n const [row] = await db\n .insert(discounts)\n .values(values)\n .onConflictDoNothing({ target: discounts.code })\n .returning({ id: discounts.id });\n return row?.id;\n}\n\nexport async function updateDiscount(\n db: CommerceExecutor,\n id: string,\n values: Partial<typeof discounts.$inferInsert>,\n): Promise<void> {\n await db\n .update(discounts)\n .set({ ...values, updatedAt: sql`now()` })\n .where(eq(discounts.id, id));\n}\n","import type { DiscountRejectionReason } from '@develemit/commerce-contracts';\nimport type { CommerceExecutor } from '../db/types';\nimport { evaluateDiscount } from './evaluate';\nimport {\n countActiveRedemptions,\n findDiscountByCode,\n insertRedemption,\n transitionRedemptions,\n} from './repo';\n\nexport class DiscountRejectedError extends Error {\n readonly code = 'DISCOUNT_INVALID';\n constructor(public readonly reason: DiscountRejectionReason) {\n super(`Discount rejected: ${reason}`);\n this.name = 'DiscountRejectedError';\n }\n}\n\nexport interface AppliedDiscount {\n code: string;\n discountCents: number;\n}\n\ninterface CheckoutDiscountInput {\n code: string;\n subtotalCents: number;\n currency: string;\n now?: Date;\n}\n\n// The discount row lock serializes rival checkouts for the same code, so the\n// count read after it includes every redemption a committed rival reserved.\n// A code worth nothing on this cart is still recorded on the order, but takes\n// no redemption slot.\nexport async function evaluateForCheckout(\n tx: CommerceExecutor,\n { code, subtotalCents, currency, now = new Date() }: CheckoutDiscountInput,\n): Promise<AppliedDiscount & { discountId: string }> {\n const discount = await findDiscountByCode(tx, code, { forUpdate: true });\n const result = evaluateDiscount(discount, {\n subtotalCents,\n currency,\n now,\n activeRedemptions: discount\n ? await countActiveRedemptions(tx, discount.id)\n : 0,\n });\n if (!result.ok) {\n throw new DiscountRejectedError(\n result.reason === 'inactive' ? 'not_found' : result.reason,\n );\n }\n return {\n code,\n discountCents: result.discountCents,\n discountId: discount!.id,\n };\n}\n\nexport async function reserveRedemption(\n tx: CommerceExecutor,\n orderId: string,\n applied: AppliedDiscount & { discountId: string },\n): Promise<void> {\n if (applied.discountCents === 0) return;\n await insertRedemption(tx, {\n discountId: applied.discountId,\n orderId,\n amountCents: applied.discountCents,\n });\n}\n\n// A payment can land after the sweeper released the redemption; the buyer was\n// charged the discounted price, so it is committed regardless.\nexport async function commitRedemption(\n db: CommerceExecutor,\n orderId: string,\n): Promise<void> {\n await transitionRedemptions(\n db,\n [orderId],\n ['reserved', 'released'],\n 'committed',\n );\n}\n\nexport async function releaseRedemptions(\n db: CommerceExecutor,\n orderIds: string[],\n): Promise<void> {\n await transitionRedemptions(db, orderIds, ['reserved'], 'released');\n}\n","import type { ShippingRateOption } from '../shipping/repo';\n\nexport interface CheckoutLine {\n productId: string;\n name: string;\n unitAmountCents: number;\n quantity: number;\n taxCode?: string;\n}\n\nexport interface CreateCheckoutSessionInput {\n reference: string;\n currency: string;\n lines: CheckoutLine[];\n successUrl: string;\n cancelUrl: string;\n shippingCountries: string[];\n shippingRates: ShippingRateOption[];\n expiresAt: Date;\n // Off the merchandise subtotal, never shipping. The kit has already\n // evaluated it; gateways apply this exact amount.\n discount?: CheckoutDiscount;\n}\n\nexport interface CheckoutDiscount {\n code: string;\n amountCents: number;\n}\n\nexport interface CheckoutSession {\n id: string;\n url: string;\n}\n\nexport interface ShippingAddress {\n name?: string;\n line1?: string;\n line2?: string;\n city?: string;\n state?: string;\n postalCode?: string;\n country?: string;\n}\n\nexport interface ChosenShipping {\n label: string;\n amountCents: number;\n}\n\nexport interface GatewayRefundEvent {\n gatewayRefundId: string;\n amountCents: number;\n // The idempotencyKey the kit passed to refund(), when the refund came from it.\n idempotencyKey?: string;\n}\n\nexport type PaymentEvent =\n | {\n id: string;\n type: 'checkout.completed';\n sessionId: string;\n email?: string;\n shippingAddress?: ShippingAddress;\n shipping?: ChosenShipping;\n taxCents?: number;\n paymentPending?: boolean;\n }\n | { id: string; type: 'checkout.async_paid'; sessionId: string }\n | { id: string; type: 'checkout.async_failed'; sessionId: string }\n | { id: string; type: 'checkout.expired'; sessionId: string }\n | {\n id: string;\n type: 'payment.refunded';\n sessionId: string;\n refunds: GatewayRefundEvent[];\n }\n | { id: string; type: 'ignored'; sourceType: string };\n\nexport interface RefundInput {\n sessionId: string;\n amountCents: number;\n idempotencyKey: string;\n}\n\nexport interface GatewayRefund {\n gatewayRefundId: string;\n}\n\nexport interface PaymentGateway {\n // Awaited while the plugin registers, so a gateway that can't work (a\n // missing optional peer) fails startup instead of the first checkout.\n preflight?(): Promise<void>;\n createCheckoutSession(\n input: CreateCheckoutSessionInput,\n ): Promise<CheckoutSession>;\n verifyWebhook(\n rawBody: Buffer,\n signature: string | undefined,\n ): Promise<PaymentEvent>;\n refund(input: RefundInput): Promise<GatewayRefund>;\n}\n\n// Thrown by a gateway when it is certain no refund was created. Any other\n// error from refund() is treated as an unknown outcome.\nexport class RefundRejectedError extends Error {\n readonly definitelyFailed = true;\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'RefundRejectedError';\n }\n}\n\nexport function isDefiniteRefundFailure(err: unknown): boolean {\n return err instanceof RefundRejectedError;\n}\n\nexport const SESSION_ID_PLACEHOLDER = '{CHECKOUT_SESSION_ID}';\n\nexport class WebhookSignatureError extends Error {\n readonly code = 'INVALID_WEBHOOK_SIGNATURE';\n constructor(message = 'Webhook signature verification failed') {\n super(message);\n this.name = 'WebhookSignatureError';\n }\n}\n","import type { GatewayRefundEvent } from '../payment/gateway';\nimport type { CommerceExecutor } from '../db/types';\nimport { releaseRedemptions } from '../discounts/redemptions';\nimport { restock } from '../inventory/service';\nimport { toOrderRefund } from './admin-service';\nimport type { Applied } from './apply-event';\nimport {\n confirmPendingByKey,\n findRefundByGatewayId,\n insertGatewayRefund,\n lockOrderForRefund,\n type RefundRow,\n} from './refund-repo';\nimport {\n findOrderItems,\n setOrderStatus,\n sumRefundedCents,\n type OrderRow,\n} from './repo';\n\nconst ACCEPTS_REFUNDS = new Set([\n 'paid',\n 'shipped',\n 'needs_attention',\n 'refunded',\n]);\nconst SETTLES_AS_REFUNDED = new Set(['paid', 'shipped', 'needs_attention']);\n\n// A refund the admin API started may still be pending when its echo arrives;\n// the echo then completes that row, including its restock, so the admin\n// path's own confirm becomes a no-op and the hook fires exactly once.\nasync function recordRefund(\n tx: CommerceExecutor,\n order: OrderRow,\n refund: GatewayRefundEvent,\n): Promise<RefundRow | undefined> {\n if (await findRefundByGatewayId(tx, refund.gatewayRefundId)) return undefined;\n const confirmed =\n refund.idempotencyKey &&\n (await confirmPendingByKey(\n tx,\n order.id,\n refund.idempotencyKey,\n refund.gatewayRefundId,\n ));\n if (confirmed) {\n if (confirmed.restocked) {\n await restock(tx, await findOrderItems(tx, order.id));\n }\n return confirmed;\n }\n return insertGatewayRefund(tx, {\n orderId: order.id,\n amountCents: refund.amountCents,\n currency: order.currency,\n gatewayRefundId: refund.gatewayRefundId,\n });\n}\n\nexport async function applyGatewayRefunds(\n tx: CommerceExecutor,\n order: OrderRow,\n refunds: GatewayRefundEvent[],\n): Promise<Applied> {\n const locked = await lockOrderForRefund(tx, order.id);\n if (!locked || !ACCEPTS_REFUNDS.has(locked.status)) {\n return { outcome: 'ignored' };\n }\n const hooks: NonNullable<Applied['hooks']> = [];\n for (const refund of refunds) {\n const recorded = await recordRefund(tx, order, refund);\n if (recorded) {\n hooks.push(['onOrderRefunded', order.id, toOrderRefund(recorded)]);\n }\n }\n if (\n SETTLES_AS_REFUNDED.has(locked.status) &&\n (await sumRefundedCents(tx, order.id)) >= locked.totalCents\n ) {\n await setOrderStatus(tx, order.id, 'refunded');\n await releaseRedemptions(tx, [order.id]);\n }\n return { outcome: 'processed', hooks };\n}\n","import type { CommerceExecutor } from '../db/types';\nimport { commitRedemption, releaseRedemptions } from '../discounts/redemptions';\nimport { commit, extendHolds, release } from '../inventory/service';\nimport type { PaymentEvent } from '../payment/gateway';\nimport { saveOrderDetails, setOrderStatus, type OrderRow } from './repo';\nimport { applyGatewayRefunds } from './gateway-refunds';\nimport type { OrderHooks } from './types';\n\nexport type HookName = keyof OrderHooks;\nexport type HookCall = [HookName, string, ...unknown[]];\nexport type Applied = {\n outcome: 'processed' | 'ignored';\n hooks?: HookCall[];\n};\n\nconst ignored: Applied = { outcome: 'ignored' };\n\n// Bank debits can take days to settle; the hold has to outlive them.\nexport const ASYNC_PAYMENT_HOLD_MS = 14 * 24 * 60 * 60 * 1000;\n\ntype Completed = Extract<PaymentEvent, { type: 'checkout.completed' }>;\n\nasync function settle(tx: CommerceExecutor, order: OrderRow): Promise<Applied> {\n if ((await commit(tx, order.id)) === 'unavailable') {\n await setOrderStatus(tx, order.id, 'needs_attention');\n return {\n outcome: 'processed',\n hooks: [['onOrderNeedsAttention', order.id]],\n };\n }\n await setOrderStatus(tx, order.id, 'paid');\n await commitRedemption(tx, order.id);\n return { outcome: 'processed', hooks: [['onOrderPaid', order.id]] };\n}\n\nasync function completed(\n tx: CommerceExecutor,\n order: OrderRow,\n event: Completed,\n): Promise<Applied> {\n if (['paid', 'shipped', 'needs_attention'].includes(order.status)) {\n return ignored;\n }\n await saveOrderDetails(tx, order.id, event);\n if (event.paymentPending) {\n await extendHolds(\n tx,\n order.id,\n new Date(Date.now() + ASYNC_PAYMENT_HOLD_MS),\n );\n return { outcome: 'processed' };\n }\n return settle(tx, order);\n}\n\nexport async function applyToOrder(\n tx: CommerceExecutor,\n order: OrderRow,\n event: Exclude<PaymentEvent, { type: 'ignored' }>,\n): Promise<Applied> {\n switch (event.type) {\n case 'checkout.completed':\n return completed(tx, order, event);\n case 'checkout.async_paid':\n return order.status === 'pending' ? settle(tx, order) : ignored;\n case 'checkout.async_failed':\n if (order.status !== 'pending') return ignored;\n await setOrderStatus(tx, order.id, 'cancelled');\n await release(tx, order.id);\n await releaseRedemptions(tx, [order.id]);\n return {\n outcome: 'processed',\n hooks: [['onOrderPaymentFailed', order.id]],\n };\n case 'checkout.expired':\n if (order.status !== 'pending') return ignored;\n await setOrderStatus(tx, order.id, 'expired');\n await release(tx, order.id);\n await releaseRedemptions(tx, [order.id]);\n return { outcome: 'processed', hooks: [['onOrderExpired', order.id]] };\n case 'payment.refunded':\n return applyGatewayRefunds(tx, order, event.refunds);\n }\n}\n","import type { OrderDetail, OrderRefund } from '@develemit/commerce-contracts';\nimport type { PaymentGateway } from '../payment/gateway';\n\nexport interface CheckoutUrls {\n success(sessionIdPlaceholder: string): string;\n cancel: string;\n}\n\nexport interface CheckoutSettings {\n currency: string;\n payment: PaymentGateway;\n urls: CheckoutUrls;\n shippingCountries: string[];\n hooks?: OrderHooks;\n}\n\nexport interface OrderHooks {\n onOrderPaid?(order: OrderDetail): Promise<void>;\n onOrderExpired?(order: OrderDetail): Promise<void>;\n onOrderNeedsAttention?(order: OrderDetail): Promise<void>;\n onOrderPaymentFailed?(order: OrderDetail): Promise<void>;\n onOrderShipped?(order: OrderDetail): Promise<void>;\n onOrderRefunded?(order: OrderDetail, refund: OrderRefund): Promise<void>;\n}\n\nexport interface HookLogger {\n error(obj: { err: unknown }, msg: string): void;\n warn?(obj: object, msg: string): void;\n}\n\nexport class CheckoutConflictError extends Error {\n readonly code = 'CHECKOUT_CONFLICT';\n constructor(public readonly productIds: string[]) {\n super(`Unavailable products: ${productIds.join(', ')}`);\n this.name = 'CheckoutConflictError';\n }\n}\n\nexport class PaymentGatewayError extends Error {\n readonly code = 'PAYMENT_GATEWAY_ERROR';\n constructor(cause: unknown) {\n super('The payment gateway could not create a checkout session', {\n cause,\n });\n this.name = 'PaymentGatewayError';\n }\n}\n\nexport class OrderNotFoundError extends Error {\n readonly code = 'ORDER_NOT_FOUND';\n constructor(public readonly sessionId: string) {\n super(`No order for checkout session: ${sessionId}`);\n this.name = 'OrderNotFoundError';\n }\n}\n","import type { OrderDetail, OrderSummary } from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport type { PaymentEvent } from '../payment/gateway';\nimport { applyToOrder, type HookCall, type HookName } from './apply-event';\nimport { detailById, summaryBySession } from './order-detail';\nimport { findOrderBySession, recordEvent } from './repo';\nimport { OrderNotFoundError, type HookLogger, type OrderHooks } from './types';\n\nexport type PaymentEventOutcome = 'processed' | 'duplicate' | 'ignored';\n\nexport async function getOrderSummary(\n db: CommerceDb,\n sessionId: string,\n): Promise<OrderSummary> {\n const summary = await summaryBySession(db, sessionId);\n if (!summary) throw new OrderNotFoundError(sessionId);\n return summary;\n}\n\n// The event row is written in the same transaction as its effects, so a\n// failure rolls both back and the gateway's retry processes it again.\nasync function applyEvent(\n db: CommerceDb,\n event: PaymentEvent,\n): Promise<{ outcome: PaymentEventOutcome; hooks?: HookCall[] }> {\n return db.transaction(async (tx) => {\n const type = event.type === 'ignored' ? event.sourceType : event.type;\n if (!(await recordEvent(tx, event.id, type))) {\n return { outcome: 'duplicate' };\n }\n if (event.type === 'ignored') return { outcome: 'ignored' };\n const order = await findOrderBySession(tx, event.sessionId);\n if (!order) return { outcome: 'ignored' };\n return applyToOrder(tx, order, event);\n });\n}\n\n// Hooks run after commit and only for the delivery that processed the event,\n// so a replay never re-fires them. A throwing hook is logged, not retried:\n// failing the webhook would make the gateway redeliver an already-applied event.\nexport async function runHook(\n db: CommerceDb,\n hooks: OrderHooks,\n [name, orderId]: [HookName, string],\n log?: HookLogger,\n ...args: unknown[]\n): Promise<void> {\n const hook = hooks[name] as\n ((order: OrderDetail, ...rest: unknown[]) => Promise<void>) | undefined;\n if (!hook) return;\n try {\n const order = await detailById(db, orderId);\n if (order) await hook.call(hooks, order, ...args);\n } catch (err) {\n log?.error({ err }, `${name} hook failed for order ${orderId}`);\n }\n}\n\nexport async function handlePaymentEvent(\n db: CommerceDb,\n event: PaymentEvent,\n hooks: OrderHooks = {},\n log?: HookLogger,\n): Promise<PaymentEventOutcome> {\n const { outcome, hooks: calls = [] } = await applyEvent(db, event);\n for (const [name, orderId, ...args] of calls) {\n await runHook(db, hooks, [name, orderId], log, ...args);\n }\n if (outcome === 'ignored' && event.type === 'payment.refunded') {\n log?.warn?.(\n { sessionId: event.sessionId },\n 'refund event for an unknown or unrefundable order',\n );\n }\n return outcome;\n}\n","import type {\n AdminOrderDetail,\n ShipOrderInput,\n} from '@develemit/commerce-contracts';\nimport { eq, sql } from 'drizzle-orm';\nimport { orders } from '../db/schema';\nimport type { CommerceDb } from '../db/types';\nimport { AdminOrderNotFoundError, getAdminOrder } from './admin-service';\nimport { runHook } from './order-service';\nimport type { HookLogger, OrderHooks } from './types';\n\nexport class InvalidOrderStateError extends Error {\n readonly code = 'INVALID_ORDER_STATE';\n constructor(public readonly status: string) {\n super(`The order is ${status}`);\n this.name = 'InvalidOrderStateError';\n }\n}\n\nasync function applyShipment(\n db: CommerceDb,\n id: string,\n input: ShipOrderInput,\n): Promise<boolean> {\n return db.transaction(async (tx) => {\n const [row] = await tx\n .select({ status: orders.status })\n .from(orders)\n .where(eq(orders.id, id))\n .for('update');\n if (!row) throw new AdminOrderNotFoundError(id);\n if (row.status !== 'paid' && row.status !== 'shipped') {\n throw new InvalidOrderStateError(row.status);\n }\n const first = row.status === 'paid';\n await tx\n .update(orders)\n .set({\n fulfillmentCarrier: input.carrier ?? null,\n fulfillmentTrackingNumber: input.trackingNumber ?? null,\n fulfillmentTrackingUrl: input.trackingUrl ?? null,\n ...(first && { status: 'shipped' as const, shippedAt: sql`now()` }),\n updatedAt: sql`now()`,\n })\n .where(eq(orders.id, id));\n return first;\n });\n}\n\nexport async function shipOrder(\n db: CommerceDb,\n id: string,\n input: ShipOrderInput,\n hooks: OrderHooks = {},\n log?: HookLogger,\n): Promise<AdminOrderDetail> {\n const first = await applyShipment(db, id, input);\n if (first) await runHook(db, hooks, ['onOrderShipped', id], log);\n return getAdminOrder(db, id);\n}\n","import type {\n AdminOrderDetail,\n RefundOrderInput,\n} from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport { releaseRedemptions } from '../discounts/redemptions';\nimport { restock } from '../inventory/service';\nimport {\n isDefiniteRefundFailure,\n type PaymentGateway,\n} from '../payment/gateway';\nimport {\n AdminOrderNotFoundError,\n getAdminOrder,\n toOrderRefund,\n} from './admin-service';\nimport { runHook } from './order-service';\nimport {\n confirmRefund,\n deletePendingRefund,\n findRefundByKey,\n insertPendingRefund,\n lockOrderForRefund,\n sumReservedCents,\n type RefundRow,\n} from './refund-repo';\nimport { findOrderItems, setOrderStatus, sumRefundedCents } from './repo';\nimport {\n RefundExceedsBalanceError,\n RefundGatewayError,\n RefundOutcomeUnknownError,\n RestockNotApplicableError,\n RestockRequiresFullRefundError,\n} from './refund-errors';\nimport { InvalidOrderStateError } from './ship-service';\nimport type { HookLogger, OrderHooks } from './types';\n\nconst REFUNDABLE = new Set(['paid', 'shipped', 'needs_attention']);\n\ntype Reservation =\n { kind: 'done' } | { kind: 'pending'; refund: RefundRow; sessionId: string };\n\nasync function reserveRefund(\n db: CommerceDb,\n orderId: string,\n input: RefundOrderInput,\n idempotencyKey: string,\n): Promise<Reservation> {\n return db.transaction(async (tx) => {\n const order = await lockOrderForRefund(tx, orderId);\n if (!order) throw new AdminOrderNotFoundError(orderId);\n const existing = await findRefundByKey(tx, orderId, idempotencyKey);\n if (existing?.gatewayRefundId) return { kind: 'done' };\n if (!REFUNDABLE.has(order.status) || !order.checkoutSessionId) {\n throw new InvalidOrderStateError(order.status);\n }\n const sessionId = order.checkoutSessionId;\n if (existing) return { kind: 'pending', refund: existing, sessionId };\n const balance = order.totalCents - (await sumReservedCents(tx, orderId));\n const amountCents = input.amountCents ?? balance;\n if (amountCents <= 0 || amountCents > balance) {\n throw new RefundExceedsBalanceError(balance);\n }\n if (input.restock && amountCents !== balance) {\n throw new RestockRequiresFullRefundError();\n }\n // needs_attention orders never committed their stock, so there is\n // nothing to give back.\n if (input.restock && order.status === 'needs_attention') {\n throw new RestockNotApplicableError();\n }\n const refund = await insertPendingRefund(tx, {\n orderId,\n amountCents,\n currency: order.currency,\n reason: input.reason ?? null,\n restocked: input.restock,\n source: 'admin',\n idempotencyKey,\n });\n return { kind: 'pending', refund, sessionId };\n });\n}\n\nasync function recordRefund(\n db: CommerceDb,\n pending: RefundRow,\n gatewayRefundId: string,\n): Promise<RefundRow | undefined> {\n return db.transaction(async (tx) => {\n const order = await lockOrderForRefund(tx, pending.orderId);\n const refund = await confirmRefund(tx, pending, gatewayRefundId);\n if (!order || !refund) return undefined;\n if (refund.restocked) {\n const items = await findOrderItems(tx, refund.orderId);\n await restock(tx, items);\n }\n if ((await sumRefundedCents(tx, refund.orderId)) >= order.totalCents) {\n await setOrderStatus(tx, refund.orderId, 'refunded');\n await releaseRedemptions(tx, [refund.orderId]);\n }\n return refund;\n });\n}\n\n// The gateway is called between two transactions so no row lock is held\n// across the network. The pending row keeps parallel refunds within the\n// balance, and the scoped idempotency key makes a retry return the same\n// gateway refund, which the unique gateway id then records only once. When\n// the gateway may have refunded anyway the pending row stays, holding its\n// amount until a same-key retry or the charge.refunded webhook confirms it.\nexport async function refundOrder(\n db: CommerceDb,\n gateway: PaymentGateway,\n orderId: string,\n input: RefundOrderInput,\n idempotencyKey: string,\n hooks: OrderHooks = {},\n log?: HookLogger,\n): Promise<AdminOrderDetail> {\n const reservation = await reserveRefund(db, orderId, input, idempotencyKey);\n if (reservation.kind === 'done') return getAdminOrder(db, orderId);\n const { refund: pending, sessionId } = reservation;\n let gatewayRefundId: string;\n try {\n ({ gatewayRefundId } = await gateway.refund({\n sessionId,\n amountCents: pending.amountCents,\n idempotencyKey: `${orderId}:${idempotencyKey}`,\n }));\n } catch (err) {\n if (!isDefiniteRefundFailure(err)) throw new RefundOutcomeUnknownError(err);\n await deletePendingRefund(db, pending.id);\n throw new RefundGatewayError(err);\n }\n const recorded = await recordRefund(db, pending, gatewayRefundId);\n if (recorded) {\n await runHook(\n db,\n hooks,\n ['onOrderRefunded', orderId],\n log,\n toOrderRefund(recorded),\n );\n }\n return getAdminOrder(db, orderId);\n}\n","import type { AdminOrderDetail } from '@develemit/commerce-contracts';\nimport { eq, sql } from 'drizzle-orm';\nimport { orders } from '../db/schema';\nimport type { CommerceDb } from '../db/types';\nimport { commitRedemption } from '../discounts/redemptions';\nimport { commitWithin } from '../inventory/service';\nimport { InsufficientStockError } from '../inventory/types';\nimport { AdminOrderNotFoundError, getAdminOrder } from './admin-service';\nimport { runHook } from './order-service';\nimport { InvalidOrderStateError } from './ship-service';\nimport type { HookLogger, OrderHooks } from './types';\n\nasync function applyResolution(db: CommerceDb, id: string): Promise<void> {\n await db.transaction(async (tx) => {\n const [row] = await tx\n .select({ status: orders.status })\n .from(orders)\n .where(eq(orders.id, id))\n .for('update');\n if (!row) throw new AdminOrderNotFoundError(id);\n if (row.status !== 'needs_attention') {\n throw new InvalidOrderStateError(row.status);\n }\n const outcome = await commitWithin(tx, id);\n if (outcome.result === 'unavailable') {\n throw new InsufficientStockError(outcome.shortProductIds);\n }\n await tx\n .update(orders)\n .set({ status: 'paid', updatedAt: sql`now()` })\n .where(eq(orders.id, id));\n await commitRedemption(tx, id);\n });\n}\n\nexport async function resolveOrder(\n db: CommerceDb,\n id: string,\n hooks: OrderHooks = {},\n log?: HookLogger,\n): Promise<AdminOrderDetail> {\n await applyResolution(db, id);\n await runHook(db, hooks, ['onOrderPaid', id], log);\n return getAdminOrder(db, id);\n}\n","import { adminDiscountListQuerySchema } from '@develemit/commerce-contracts';\nimport type { FastifyPluginAsync, FastifyRequest } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport { InvalidCursorError } from '../shared/cursor';\nimport {\n createDiscount,\n DiscountCodeTakenError,\n DiscountInUseError,\n DiscountNotFoundError,\n getAdminDiscount,\n InvalidDiscountError,\n InvalidDiscountIdError,\n isDiscountId,\n listAdminDiscounts,\n MaxRedemptionsBelowUsageError,\n patchDiscount,\n} from './admin-service';\n\nexport interface AdminDiscountsPluginOptions {\n db: CommerceDb;\n authorize(req: FastifyRequest): Promise<boolean>;\n}\n\nconst BAD_REQUEST = [\n InvalidCursorError,\n InvalidDiscountIdError,\n InvalidDiscountError,\n MaxRedemptionsBelowUsageError,\n];\n\nconst CONFLICT = [DiscountCodeTakenError, DiscountInUseError];\n\nfunction isZodError(err: unknown): err is Error & { issues: unknown[] } {\n return err instanceof Error && err.name === 'ZodError';\n}\n\nfunction discountId(req: FastifyRequest<{ Params: { id: string } }>): string {\n if (!isDiscountId(req.params.id)) throw new InvalidDiscountIdError();\n return req.params.id;\n}\n\nexport const adminDiscountsPlugin: FastifyPluginAsync<\n AdminDiscountsPluginOptions\n> = async (app, { db, authorize }) => {\n app.setErrorHandler((err, _req, reply) => {\n if (isZodError(err)) {\n return reply\n .code(400)\n .send({ error: 'INVALID_INPUT', issues: err.issues });\n }\n if (BAD_REQUEST.some((type) => err instanceof type)) {\n const { code, message } = err as Error & { code: string };\n return reply.code(400).send({ error: code, message });\n }\n if (CONFLICT.some((type) => err instanceof type)) {\n const { code, message } = err as Error & { code: string };\n return reply.code(409).send({ error: code, message });\n }\n if (err instanceof DiscountNotFoundError) {\n return reply.code(404).send({ error: err.code, message: err.message });\n }\n return reply.send(err);\n });\n\n app.addHook('preHandler', async (req, reply) => {\n if (!(await authorize(req))) {\n return reply.code(401).send({ error: 'UNAUTHORIZED' });\n }\n });\n\n app.get('/', async (req) =>\n listAdminDiscounts(db, adminDiscountListQuerySchema.parse(req.query)),\n );\n\n app.post('/', async (req, reply) => {\n const created = await createDiscount(db, (req.body ?? {}) as object);\n return reply.code(201).send(created);\n });\n\n app.get<{ Params: { id: string } }>('/:id', async (req) =>\n getAdminDiscount(db, discountId(req)),\n );\n\n app.patch<{ Params: { id: string } }>('/:id', async (req) =>\n patchDiscount(db, discountId(req), (req.body ?? {}) as object),\n );\n};\n","import {\n discountInputSchema,\n discountPatchSchema,\n type AdminDiscountListQuery,\n type AdminDiscountPage,\n type Discount,\n} from '@develemit/commerce-contracts';\nimport type { CommerceDb } from '../db/types';\nimport {\n decodeCursor,\n encodeCursor,\n isCursorTimestamp,\n} from '../shared/cursor';\nimport {\n findDiscountById,\n insertDiscount,\n selectDiscountPage,\n updateDiscount,\n type DiscountWithCounts,\n} from './repo';\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\ntype ParsedPatch = ReturnType<typeof discountPatchSchema.parse>;\n\nconst LOCKED_AFTER_USE = [\n 'code',\n 'type',\n 'percentOff',\n 'amountOffCents',\n 'currency',\n] as const;\n\nexport class DiscountNotFoundError extends Error {\n readonly code = 'DISCOUNT_NOT_FOUND';\n constructor(id: string) {\n super(`No discount: ${id}`);\n this.name = 'DiscountNotFoundError';\n }\n}\n\nexport class InvalidDiscountIdError extends Error {\n readonly code = 'INVALID_DISCOUNT_ID';\n constructor() {\n super('The discount id is malformed');\n this.name = 'InvalidDiscountIdError';\n }\n}\n\nexport class DiscountCodeTakenError extends Error {\n readonly code = 'DISCOUNT_CODE_TAKEN';\n constructor(code: string) {\n super(`The code ${code} already exists`);\n this.name = 'DiscountCodeTakenError';\n }\n}\n\nexport class DiscountInUseError extends Error {\n readonly code = 'DISCOUNT_IN_USE';\n constructor(public readonly fields: string[]) {\n super(`A redeemed discount cannot change ${fields.join(', ')}`);\n this.name = 'DiscountInUseError';\n }\n}\n\nexport class MaxRedemptionsBelowUsageError extends Error {\n readonly code = 'MAX_REDEMPTIONS_BELOW_USAGE';\n constructor(public readonly usage: number) {\n super(`maxRedemptions cannot be below the ${usage} already in use`);\n this.name = 'MaxRedemptionsBelowUsageError';\n }\n}\n\nexport class InvalidDiscountError extends Error {\n readonly code = 'INVALID_INPUT';\n constructor(message: string) {\n super(message);\n this.name = 'InvalidDiscountError';\n }\n}\n\nexport function isDiscountId(value: string): boolean {\n return UUID.test(value);\n}\n\nfunction parseDate(value: string | null | undefined): Date | null | undefined {\n if (value === undefined || value === null) return value;\n const date = new Date(value);\n if (Number.isNaN(+date)) throw new InvalidDiscountError('Invalid date');\n return date;\n}\n\nfunction toDiscount(row: DiscountWithCounts): Discount {\n return {\n id: row.id,\n code: row.code,\n type: row.type,\n percentOff: row.percentOff,\n amountOffCents: row.amountOffCents,\n currency: row.currency,\n minSubtotalCents: row.minSubtotalCents,\n startsAt: row.startsAt?.toISOString() ?? null,\n endsAt: row.endsAt?.toISOString() ?? null,\n maxRedemptions: row.maxRedemptions,\n active: row.active,\n redeemedCount: row.redeemedCount,\n reservedCount: row.reservedCount,\n createdAt: row.createdAt.toISOString(),\n };\n}\n\nfunction decodeDiscountCursor(raw: string): { createdAt: string; id: string } {\n const { at, key } = decodeCursor(\n raw,\n (c) => isDiscountId(c.key) && isCursorTimestamp(c.at),\n );\n return { createdAt: at, id: key };\n}\n\nexport async function listAdminDiscounts(\n db: CommerceDb,\n query: AdminDiscountListQuery,\n now: Date = new Date(),\n): Promise<AdminDiscountPage> {\n const rows = await selectDiscountPage(db, {\n status: query.status,\n q: query.q,\n now,\n after: query.cursor ? decodeDiscountCursor(query.cursor) : undefined,\n limit: query.limit + 1,\n });\n const page = rows.slice(0, query.limit);\n const last = page[page.length - 1];\n return {\n items: page.map(toDiscount),\n nextCursor:\n rows.length > query.limit && last\n ? encodeCursor({ at: last.createdAtKey, key: last.id })\n : null,\n };\n}\n\nexport async function getAdminDiscount(\n db: CommerceDb,\n id: string,\n): Promise<Discount> {\n const row = await findDiscountById(db, id);\n if (!row) throw new DiscountNotFoundError(id);\n return toDiscount(row);\n}\n\nexport async function createDiscount(\n db: CommerceDb,\n input: unknown,\n): Promise<Discount> {\n const data = discountInputSchema.parse(input);\n const id = await insertDiscount(db, {\n code: data.code,\n type: data.type,\n percentOff: data.percentOff ?? null,\n amountOffCents: data.amountOffCents ?? null,\n currency: data.currency ?? null,\n minSubtotalCents: data.minSubtotalCents ?? null,\n startsAt: parseDate(data.startsAt) ?? null,\n endsAt: parseDate(data.endsAt) ?? null,\n maxRedemptions: data.maxRedemptions ?? null,\n active: data.active,\n });\n if (!id) throw new DiscountCodeTakenError(data.code);\n return getAdminDiscount(db, id);\n}\n\nfunction sameValue(a: unknown, b: unknown): boolean {\n if (typeof a === 'string' && typeof b === 'string') {\n return a.toUpperCase() === b.toUpperCase();\n }\n return a === b;\n}\n\nfunction changedLockedFields(\n row: DiscountWithCounts,\n patch: ParsedPatch,\n): string[] {\n return LOCKED_AFTER_USE.filter(\n (field) =>\n patch[field] !== undefined && !sameValue(patch[field], row[field]),\n );\n}\n\nfunction assertCoherent(row: DiscountWithCounts, patch: ParsedPatch): void {\n const merged = discountInputSchema.safeParse({\n code: row.code,\n type: row.type,\n percentOff: row.percentOff,\n amountOffCents: row.amountOffCents,\n currency: row.currency,\n ...patch,\n });\n if (!merged.success) {\n throw new InvalidDiscountError(\n merged.error.issues[0]?.message ?? 'Invalid',\n );\n }\n}\n\nexport async function patchDiscount(\n db: CommerceDb,\n id: string,\n rawPatch: unknown,\n): Promise<Discount> {\n const patch = discountPatchSchema.parse(rawPatch);\n return db.transaction(async (tx) => {\n const row = await findDiscountById(tx, id, { forUpdate: true });\n if (!row) throw new DiscountNotFoundError(id);\n const usage = row.redeemedCount + row.reservedCount;\n const locked = changedLockedFields(row, patch);\n if (row.totalRedemptions > 0 && locked.length > 0)\n throw new DiscountInUseError(locked);\n if (\n typeof patch.maxRedemptions === 'number' &&\n patch.maxRedemptions < usage\n ) {\n throw new MaxRedemptionsBelowUsageError(usage);\n }\n assertCoherent(row, patch);\n try {\n await updateDiscount(tx, id, {\n code: patch.code,\n type: patch.type,\n percentOff: patch.percentOff,\n amountOffCents: patch.amountOffCents,\n currency: patch.currency,\n minSubtotalCents: patch.minSubtotalCents,\n startsAt: parseDate(patch.startsAt),\n endsAt: parseDate(patch.endsAt),\n maxRedemptions: patch.maxRedemptions,\n active: patch.active,\n });\n } catch (err) {\n const cause = (err as { cause?: { code?: string } }).cause;\n if ((cause ?? (err as { code?: string })).code === '23505') {\n throw new DiscountCodeTakenError(patch.code ?? row.code);\n }\n throw err;\n }\n const updated = await findDiscountById(tx, id);\n return toDiscount(updated!);\n });\n}\n","import type { ChosenShipping, ShippingAddress } from '../payment/gateway';\n\nexport interface DevBuyer {\n email?: string;\n name?: string;\n address?: ShippingAddress;\n}\n\nexport type DevBuyerSetting = DevBuyer | ((sessionId: string) => DevBuyer);\n\nexport interface DevCompletionDetails {\n email: string;\n shippingAddress: ShippingAddress;\n shipping?: ChosenShipping;\n}\n\nexport const DEFAULT_DEV_BUYER = {\n email: 'dev-buyer@example.com',\n name: 'Dev Buyer',\n address: {\n line1: '123 Example Street',\n city: 'Springfield',\n state: 'IL',\n postalCode: '62701',\n country: 'US',\n },\n} satisfies Required<DevBuyer>;\n\nexport function resolveDevCompletion(\n setting: DevBuyerSetting | undefined,\n sessionId: string,\n firstRate: ChosenShipping | undefined,\n): DevCompletionDetails {\n const buyer = typeof setting === 'function' ? setting(sessionId) : setting;\n const name = buyer?.name ?? DEFAULT_DEV_BUYER.name;\n return {\n email: buyer?.email ?? DEFAULT_DEV_BUYER.email,\n shippingAddress: { name, ...(buyer?.address ?? DEFAULT_DEV_BUYER.address) },\n shipping: firstRate && {\n label: firstRate.label,\n amountCents: firstRate.amountCents,\n },\n };\n}\n","import type { FastifyPluginAsync } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport type { FakeGateway } from '../payment/fake-gateway';\nimport { resolveDevCompletion, type DevBuyerSetting } from './dev-buyer';\nimport { handlePaymentEvent } from './order-service';\nimport type { CheckoutUrls, OrderHooks } from './types';\n\nexport interface DevCheckoutOptions {\n db: CommerceDb;\n payment: FakeGateway;\n urls: CheckoutUrls;\n hooks?: OrderHooks;\n buyer?: DevBuyerSetting;\n}\n\nexport function assertDevCheckoutAllowed(): void {\n if (process.env.NODE_ENV === 'production') {\n throw new Error('devCheckout is not allowed when NODE_ENV=production');\n }\n}\n\nfunction completedWithTax(\n payment: FakeGateway,\n session: FakeGateway['sessions'][number],\n buyer: DevBuyerSetting | undefined,\n) {\n const details = resolveDevCompletion(\n buyer,\n session.id,\n session.shippingRates[0],\n );\n const taxCents = payment.taxFor(session.id, details.shipping?.amountCents);\n return payment.completed(session.id, { ...details, taxCents });\n}\n\nexport const devCheckoutRoutes: FastifyPluginAsync<DevCheckoutOptions> = async (\n app,\n { db, payment, urls, hooks, buyer },\n) => {\n assertDevCheckoutAllowed();\n\n app.get<{\n Params: { sessionId: string };\n Querystring: { outcome?: string };\n }>('/checkout/dev/:sessionId/complete', async (req, reply) => {\n const { sessionId } = req.params;\n const session = payment.sessions.find((s) => s.id === sessionId);\n if (!session) {\n return reply.code(404).send({ error: 'SESSION_NOT_FOUND' });\n }\n const expire = req.query.outcome === 'expired';\n const { rawBody, signature } = expire\n ? payment.expired(sessionId)\n : completedWithTax(payment, session, buyer);\n const event = await payment.verifyWebhook(rawBody, signature);\n await handlePaymentEvent(db, event, hooks, req.log);\n return reply.redirect(expire ? urls.cancel : urls.success(sessionId), 302);\n });\n};\n","import {\n checkoutConflictSchema,\n discountPreviewRequestSchema,\n discountPreviewSchema,\n discountRejectionSchema,\n checkoutRequestSchema,\n checkoutResponseSchema,\n orderSummarySchema,\n} from '@develemit/commerce-contracts';\nimport type { FastifyPluginAsync } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport { DiscountRejectedError } from '../discounts/redemptions';\nimport { previewDiscount } from '../discounts/service';\nimport { createCheckout } from './checkout-service';\nimport { getOrderSummary } from './order-service';\nimport {\n CheckoutConflictError,\n OrderNotFoundError,\n PaymentGatewayError,\n type CheckoutSettings,\n} from './types';\nimport { webhookRoutes } from './webhook-routes';\n\nexport interface OrdersPluginOptions extends CheckoutSettings {\n db: CommerceDb;\n}\n\nexport const ordersPlugin: FastifyPluginAsync<OrdersPluginOptions> = async (\n app,\n { db, ...settings },\n) => {\n app.post('/checkout', async (req, reply) => {\n const parsed = checkoutRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n return reply.code(400).send({ error: 'INVALID_CHECKOUT_REQUEST' });\n }\n try {\n return checkoutResponseSchema.parse(\n await createCheckout(db, settings, parsed.data),\n );\n } catch (err) {\n if (err instanceof CheckoutConflictError) {\n return reply.code(409).send(\n checkoutConflictSchema.parse({\n unavailableProductIds: err.productIds,\n }),\n );\n }\n if (err instanceof DiscountRejectedError) {\n return reply.code(422).send(\n discountRejectionSchema.parse({\n error: err.code,\n reason: err.reason,\n }),\n );\n }\n if (err instanceof PaymentGatewayError) {\n req.log.error({ err: err.cause }, err.message);\n return reply.code(502).send({ error: err.code });\n }\n throw err;\n }\n });\n\n app.post('/checkout/discount/preview', async (req, reply) => {\n const parsed = discountPreviewRequestSchema.safeParse(req.body);\n if (!parsed.success) {\n return reply.code(400).send({ error: 'INVALID_DISCOUNT_REQUEST' });\n }\n reply.header('Cache-Control', 'no-store');\n const result = await previewDiscount(db, parsed.data, settings.currency);\n if (!result.ok) {\n return reply.code(422).send(\n discountRejectionSchema.parse({\n error: 'DISCOUNT_INVALID',\n reason: result.reason,\n }),\n );\n }\n return discountPreviewSchema.parse(result.preview);\n });\n\n app.get<{ Params: { sessionId: string } }>(\n '/orders/session/:sessionId',\n async (req, reply) => {\n try {\n return orderSummarySchema.parse(\n await getOrderSummary(db, req.params.sessionId),\n );\n } catch (err) {\n if (err instanceof OrderNotFoundError) {\n return reply.code(404).send({ error: err.code });\n }\n throw err;\n }\n },\n );\n\n await app.register(webhookRoutes, {\n db,\n payment: settings.payment,\n hooks: settings.hooks,\n prefix: '/orders/webhooks',\n });\n};\n","import type {\n DiscountPreview,\n DiscountPreviewRequest,\n DiscountRejectionReason,\n} from '@develemit/commerce-contracts';\nimport { findPurchasable } from '../catalog/service';\nimport type { CommerceDb } from '../db/types';\nimport { evaluateDiscount } from './evaluate';\nimport { countActiveRedemptions, findDiscountByCode } from './repo';\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport type PreviewResult =\n | { ok: true; preview: DiscountPreview }\n | { ok: false; reason: DiscountRejectionReason };\n\nasync function availableSubtotal(\n db: CommerceDb,\n items: DiscountPreviewRequest['items'],\n currency: string,\n): Promise<number> {\n const quantities = new Map<string, number>();\n for (const { productId, quantity } of items) {\n quantities.set(productId, (quantities.get(productId) ?? 0) + quantity);\n }\n const found = await findPurchasable(\n db,\n [...quantities.keys()].filter((id) => UUID.test(id)),\n );\n return found\n .filter((p) => p.currency === currency)\n .reduce((sum, p) => sum + p.priceCents * quantities.get(p.id)!, 0);\n}\n\nexport async function previewDiscount(\n db: CommerceDb,\n request: DiscountPreviewRequest,\n currency: string,\n now: Date = new Date(),\n): Promise<PreviewResult> {\n const code = request.code.trim().toUpperCase();\n const discount = await findDiscountByCode(db, code);\n const subtotalCents = await availableSubtotal(db, request.items, currency);\n const result = evaluateDiscount(discount, {\n subtotalCents,\n currency,\n now,\n activeRedemptions: discount\n ? await countActiveRedemptions(db, discount.id)\n : 0,\n });\n if (!result.ok) {\n return {\n ok: false,\n reason: result.reason === 'inactive' ? 'not_found' : result.reason,\n };\n }\n return {\n ok: true,\n preview: {\n code,\n discountCents: result.discountCents,\n subtotalCents,\n currency,\n },\n };\n}\n","import { randomUUID } from 'node:crypto';\nimport type { CheckoutRequest } from '@develemit/commerce-contracts';\nimport { findPurchasable } from '../catalog/service';\nimport type { CommerceDb } from '../db/types';\nimport {\n evaluateForCheckout,\n releaseRedemptions,\n reserveRedemption,\n type AppliedDiscount,\n} from '../discounts/redemptions';\nimport {\n InsufficientStockError,\n release,\n reserve,\n type StockRequest,\n} from '../inventory/service';\nimport { listActiveRates } from '../shipping/repo';\nimport { SESSION_ID_PLACEHOLDER, type CheckoutLine } from '../payment/gateway';\nimport { insertOrder, setCheckoutSession, setOrderStatus } from './repo';\nimport {\n CheckoutConflictError,\n PaymentGatewayError,\n type CheckoutSettings,\n} from './types';\n\n// Stripe rejects expires_at under 30 minutes from its own clock, so the\n// session (and the hold that matches it) gets a minute of slack.\nexport const HOLD_DURATION_MS = 31 * 60 * 1000;\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nfunction mergeItems(items: CheckoutRequest['items']): StockRequest[] {\n const merged = new Map<string, number>();\n for (const { productId, quantity } of items) {\n merged.set(productId, (merged.get(productId) ?? 0) + quantity);\n }\n return [...merged].map(([productId, quantity]) => ({ productId, quantity }));\n}\n\nasync function priceLines(\n db: CommerceDb,\n requests: StockRequest[],\n currency: string,\n): Promise<CheckoutLine[]> {\n const ids = requests.map((r) => r.productId);\n const found = await findPurchasable(\n db,\n ids.filter((id) => UUID.test(id)),\n );\n const byId = new Map(found.map((p) => [p.id, p]));\n const unavailable = ids.filter((id) => byId.get(id)?.currency !== currency);\n if (unavailable.length > 0) throw new CheckoutConflictError(unavailable);\n return requests.map(({ productId, quantity }) => {\n const product = byId.get(productId)!;\n return {\n productId,\n name: product.name,\n unitAmountCents: product.priceCents,\n quantity,\n ...(product.taxCode && { taxCode: product.taxCode }),\n };\n });\n}\n\ninterface OrderDraft {\n orderId: string;\n lines: CheckoutLine[];\n currency: string;\n expiresAt: Date;\n discountCode?: string;\n}\n\nasync function reserveOrder(\n db: CommerceDb,\n { orderId, lines, currency, expiresAt, discountCode }: OrderDraft,\n): Promise<AppliedDiscount | undefined> {\n const subtotalCents = lines.reduce(\n (sum, l) => sum + l.unitAmountCents * l.quantity,\n 0,\n );\n try {\n return await db.transaction(async (tx) => {\n await reserve(tx, { reference: orderId, items: lines, expiresAt });\n const discount =\n discountCode === undefined\n ? undefined\n : await evaluateForCheckout(tx, {\n code: discountCode,\n subtotalCents,\n currency,\n });\n await insertOrder(\n tx,\n {\n id: orderId,\n currency,\n subtotalCents,\n discountCents: discount?.discountCents ?? 0,\n discountCode: discount?.code ?? null,\n },\n lines.map((l) => ({\n productId: l.productId,\n name: l.name,\n priceCents: l.unitAmountCents,\n quantity: l.quantity,\n })),\n );\n if (discount) await reserveRedemption(tx, orderId, discount);\n return discount;\n });\n } catch (err) {\n if (err instanceof InsufficientStockError) {\n throw new CheckoutConflictError(err.productIds);\n }\n throw err;\n }\n}\n\nexport async function createCheckout(\n db: CommerceDb,\n settings: CheckoutSettings,\n request: CheckoutRequest,\n): Promise<{ url: string }> {\n const lines = await priceLines(\n db,\n mergeItems(request.items),\n settings.currency,\n );\n const shippingRates = await listActiveRates(db, settings.currency);\n const orderId = randomUUID();\n const expiresAt = new Date(Date.now() + HOLD_DURATION_MS);\n const discount = await reserveOrder(db, {\n orderId,\n lines,\n currency: settings.currency,\n expiresAt,\n discountCode: request.discountCode,\n });\n\n let session;\n try {\n session = await settings.payment.createCheckoutSession({\n reference: orderId,\n currency: settings.currency,\n lines,\n successUrl: settings.urls.success(SESSION_ID_PLACEHOLDER),\n cancelUrl: settings.urls.cancel,\n shippingCountries: settings.shippingCountries,\n shippingRates,\n expiresAt,\n ...(discount &&\n discount.discountCents > 0 && {\n discount: {\n code: discount.code,\n amountCents: discount.discountCents,\n },\n }),\n });\n } catch (err) {\n await release(db, orderId);\n await releaseRedemptions(db, [orderId]);\n await setOrderStatus(db, orderId, 'cancelled');\n throw new PaymentGatewayError(err);\n }\n await setCheckoutSession(db, orderId, session.id);\n return { url: session.url };\n}\n","import type { FastifyPluginAsync } from 'fastify';\nimport type { CommerceDb } from '../db/types';\nimport { WebhookSignatureError, type PaymentGateway } from '../payment/gateway';\nimport { handlePaymentEvent } from './order-service';\nimport type { OrderHooks } from './types';\n\nexport interface WebhookRoutesOptions {\n db: CommerceDb;\n payment: PaymentGateway;\n hooks?: OrderHooks;\n}\n\n// Signature checks need the exact bytes Stripe sent, so this encapsulated\n// context swaps the JSON parser for a raw buffer.\nexport const webhookRoutes: FastifyPluginAsync<WebhookRoutesOptions> = async (\n app,\n { db, payment, hooks },\n) => {\n app.removeAllContentTypeParsers();\n app.addContentTypeParser('*', { parseAs: 'buffer' }, (_req, body, done) =>\n done(null, body),\n );\n\n app.post('/stripe', async (req, reply) => {\n const header = req.headers['stripe-signature'];\n const signature = typeof header === 'string' ? header : undefined;\n const rawBody = Buffer.isBuffer(req.body) ? req.body : Buffer.alloc(0);\n let event;\n try {\n event = await payment.verifyWebhook(rawBody, signature);\n } catch (err) {\n if (err instanceof WebhookSignatureError) {\n return reply.code(400).send({ error: err.code });\n }\n throw err;\n }\n const outcome = await handlePaymentEvent(db, event, hooks, req.log);\n return { received: true, outcome };\n });\n};\n","import { createHmac, randomUUID, timingSafeEqual } from 'node:crypto';\nimport {\n SESSION_ID_PLACEHOLDER,\n RefundRejectedError,\n WebhookSignatureError,\n type ChosenShipping,\n type CreateCheckoutSessionInput,\n type GatewayRefundEvent,\n type PaymentEvent,\n type PaymentGateway,\n type RefundInput,\n type ShippingAddress,\n} from './gateway';\n\nexport interface SignedWebhook {\n rawBody: Buffer;\n signature: string;\n}\n\nexport interface FakeGateway extends PaymentGateway {\n readonly kind: 'fake';\n useSessionUrl(build: (sessionId: string) => string): void;\n readonly sessions: (CreateCheckoutSessionInput & { id: string })[];\n readonly refunds: (RefundInput & { gatewayRefundId: string })[];\n readonly refundCalls: RefundInput[];\n failNextSession(error?: Error): void;\n failNextRefund(error?: Error): void;\n failNextRefundUncertain(error?: Error): void;\n completed(\n sessionId: string,\n details?: {\n email?: string;\n shippingAddress?: ShippingAddress;\n shipping?: ChosenShipping;\n taxCents?: number;\n paymentPending?: boolean;\n },\n ): SignedWebhook;\n asyncPaid(sessionId: string): SignedWebhook;\n asyncFailed(sessionId: string): SignedWebhook;\n expired(sessionId: string): SignedWebhook;\n refunded(sessionId: string, refunds: GatewayRefundEvent[]): SignedWebhook;\n sign(event: PaymentEvent): SignedWebhook;\n taxFor(sessionId: string, shippingCents?: number): number;\n}\n\nexport interface FakeTaxOptions {\n rate: number;\n}\n\nexport interface FakeGatewayOptions {\n secret?: string;\n tax?: FakeTaxOptions;\n}\n\nexport function createFakeGateway({\n secret = 'fake-webhook-secret',\n tax,\n}: FakeGatewayOptions = {}): FakeGateway {\n const sessions: FakeGateway['sessions'] = [];\n const refunds: FakeGateway['refunds'] = [];\n const refundCalls: RefundInput[] = [];\n let pendingFailure: Error | undefined;\n let sessionUrl: ((sessionId: string) => string) | undefined;\n let pendingRefundFailure: Error | undefined;\n\n const hmac = (body: Buffer) =>\n createHmac('sha256', secret).update(body).digest('hex');\n\n function sign(event: PaymentEvent): SignedWebhook {\n const rawBody = Buffer.from(JSON.stringify(event));\n return { rawBody, signature: hmac(rawBody) };\n }\n\n return {\n kind: 'fake',\n useSessionUrl(build) {\n sessionUrl = build;\n },\n sessions,\n refunds,\n refundCalls,\n async createCheckoutSession(input) {\n if (pendingFailure) {\n const err = pendingFailure;\n pendingFailure = undefined;\n throw err;\n }\n const id = `fake_cs_${randomUUID()}`;\n sessions.push({ ...input, id });\n return {\n id,\n url:\n sessionUrl?.(id) ??\n input.successUrl.replace(SESSION_ID_PLACEHOLDER, id),\n };\n },\n async verifyWebhook(rawBody, signature) {\n const expected = Buffer.from(hmac(rawBody));\n const given = Buffer.from(signature ?? '');\n if (\n given.length !== expected.length ||\n !timingSafeEqual(given, expected)\n ) {\n throw new WebhookSignatureError();\n }\n return JSON.parse(rawBody.toString('utf8')) as PaymentEvent;\n },\n async refund(input) {\n refundCalls.push(input);\n if (pendingRefundFailure) {\n const err = pendingRefundFailure;\n pendingRefundFailure = undefined;\n throw err;\n }\n const prior = refunds.find(\n (r) => r.idempotencyKey === input.idempotencyKey,\n );\n if (prior) return { gatewayRefundId: prior.gatewayRefundId };\n const gatewayRefundId = `fake_re_${refunds.length + 1}_${input.sessionId}`;\n refunds.push({ ...input, gatewayRefundId });\n return { gatewayRefundId };\n },\n failNextSession(error = new Error('Fake gateway failure')) {\n pendingFailure = error;\n },\n failNextRefund(error = new RefundRejectedError('Fake refund failure')) {\n pendingRefundFailure = error;\n },\n failNextRefundUncertain(error = new Error('Fake refund timeout')) {\n pendingRefundFailure = error;\n },\n completed(sessionId, details = {}) {\n return sign({\n id: `fake_evt_${randomUUID()}`,\n type: 'checkout.completed',\n sessionId,\n ...details,\n });\n },\n asyncPaid(sessionId) {\n return sign({\n id: `fake_evt_${randomUUID()}`,\n type: 'checkout.async_paid',\n sessionId,\n });\n },\n asyncFailed(sessionId) {\n return sign({\n id: `fake_evt_${randomUUID()}`,\n type: 'checkout.async_failed',\n sessionId,\n });\n },\n expired(sessionId) {\n return sign({\n id: `fake_evt_${randomUUID()}`,\n type: 'checkout.expired',\n sessionId,\n });\n },\n refunded(sessionId, refunds) {\n return sign({\n id: `fake_evt_${randomUUID()}`,\n type: 'payment.refunded',\n sessionId,\n refunds,\n });\n },\n sign,\n taxFor(sessionId, shippingCents = 0) {\n const session = sessions.find((s) => s.id === sessionId);\n if (!tax || !session) return 0;\n const merchandise = session.lines.reduce(\n (sum, l) => sum + l.unitAmountCents * l.quantity,\n 0,\n );\n const taxable =\n merchandise - (session.discount?.amountCents ?? 0) + shippingCents;\n return Math.round(taxable * tax.rate);\n },\n };\n}\n\nexport function isFakeGateway(gateway: PaymentGateway): gateway is FakeGateway {\n return (gateway as Partial<FakeGateway>).kind === 'fake';\n}\n","import type { FastifyPluginAsync } from 'fastify';\nimport { adminPlugin, type AdminOptions } from './admin/routes';\nimport { catalogPlugin } from './catalog/routes';\nimport type { CatalogCacheOptions } from './catalog/cache-hooks';\nimport type { CategoryLabels } from './catalog/types';\nimport type { CommerceDb } from './db/types';\nimport { adminOrdersPlugin } from './orders/admin-routes';\nimport { adminDiscountsPlugin } from './discounts/admin-routes';\nimport {\n assertDevCheckoutAllowed,\n devCheckoutRoutes,\n} from './orders/dev-checkout';\nimport { ordersPlugin } from './orders/routes';\nimport type { CheckoutUrls, OrderHooks } from './orders/types';\nimport type { DevBuyerSetting } from './orders/dev-buyer';\nimport { isFakeGateway } from './payment/fake-gateway';\nimport type { PaymentGateway } from './payment/gateway';\n\ninterface CheckoutOptions {\n payment: PaymentGateway;\n urls: CheckoutUrls;\n shippingCountries?: string[];\n hooks?: OrderHooks;\n devCheckout?: boolean | DevCheckoutSetting;\n}\n\nexport interface DevCheckoutSetting {\n apiUrl?: string;\n buyer?: DevBuyerSetting;\n}\n\ninterface CatalogOnlyOptions {\n payment?: undefined;\n urls?: undefined;\n shippingCountries?: undefined;\n hooks?: undefined;\n devCheckout?: undefined;\n}\n\nexport type CommercePluginOptions = {\n db: CommerceDb;\n currency: string;\n admin?: AdminOptions;\n categoryLabels?: CategoryLabels;\n catalogCache?: CatalogCacheOptions;\n} & (CheckoutOptions | CatalogOnlyOptions);\n\nexport const commercePlugin: FastifyPluginAsync<CommercePluginOptions> = async (\n app,\n {\n db,\n currency,\n payment,\n urls,\n shippingCountries = ['US'],\n hooks,\n admin,\n categoryLabels,\n catalogCache,\n devCheckout,\n },\n) => {\n app.get('/health', async () => ({ status: 'ok' }));\n await app.register(catalogPlugin, {\n db,\n categoryLabels,\n catalogCache,\n prefix: '/catalog',\n });\n if (admin) {\n await app.register(adminPlugin, {\n db,\n currency,\n ...admin,\n prefix: '/admin',\n });\n }\n if (!payment || !urls) return;\n await payment.preflight?.();\n await app.register(ordersPlugin, {\n db,\n currency,\n payment,\n urls,\n shippingCountries,\n hooks,\n });\n if (devCheckout) {\n assertDevCheckoutAllowed();\n if (isFakeGateway(payment)) {\n const apiUrl = typeof devCheckout === 'object' ? devCheckout.apiUrl : '';\n payment.useSessionUrl(\n (id) => `${apiUrl ?? ''}${app.prefix}/checkout/dev/${id}/complete`,\n );\n const buyer =\n typeof devCheckout === 'object' ? devCheckout.buyer : undefined;\n await app.register(devCheckoutRoutes, {\n db,\n payment,\n urls,\n hooks,\n buyer,\n });\n }\n }\n if (admin) {\n await app.register(adminOrdersPlugin, {\n db,\n authorize: admin.authorize,\n payment,\n hooks,\n prefix: '/admin/orders',\n });\n await app.register(adminDiscountsPlugin, {\n db,\n authorize: admin.authorize,\n prefix: '/admin/discounts',\n });\n }\n};\n","import type Stripe from 'stripe';\n\n// stripe is an optional peer, loaded on first use so hosts that bring their\n// own gateway never need it installed.\nexport async function loadStripe(secretKey: string): Promise<Stripe> {\n try {\n const { default: StripeClient } = await import('stripe');\n return new StripeClient(secretKey);\n } catch (cause) {\n throw new Error(\n '@develemit/commerce-server: STRIPE_SECRET_KEY is set but the optional peer \"stripe\" is not installed. Run: pnpm add stripe',\n { cause },\n );\n }\n}\n","import type Stripe from 'stripe';\nimport {\n RefundRejectedError,\n WebhookSignatureError,\n type CreateCheckoutSessionInput,\n type ChosenShipping,\n type GatewayRefundEvent,\n type PaymentEvent,\n type PaymentGateway,\n type ShippingAddress,\n} from './gateway';\nimport { loadStripe } from './stripe-loader';\n\nexport interface StripeGatewayOptions {\n secretKey: string;\n webhookSecret: string;\n client?: Stripe;\n tax?: StripeTaxOptions;\n}\n\nexport interface StripeTaxOptions {\n enabled: true;\n behavior?: 'exclusive' | 'inclusive';\n shippingTaxCode?: string;\n}\n\nconst REFUND_KEY_METADATA = 'commerceRefundKey';\n\nfunction rejectedRefund(err: unknown): unknown {\n if (err instanceof RefundRejectedError) return err;\n const status = (err as { statusCode?: unknown } | null)?.statusCode;\n if (typeof status === 'number' && status >= 400 && status < 500) {\n return new RefundRejectedError((err as Error).message, { cause: err });\n }\n return err;\n}\n\ntype StripeAddress = Stripe.Address | null | undefined;\n\nfunction toShippingAddress(\n name: string | null | undefined,\n address: StripeAddress,\n): ShippingAddress | undefined {\n if (!address) return undefined;\n const fields: ShippingAddress = {\n name: name ?? undefined,\n line1: address.line1 ?? undefined,\n line2: address.line2 ?? undefined,\n city: address.city ?? undefined,\n state: address.state ?? undefined,\n postalCode: address.postal_code ?? undefined,\n country: address.country ?? undefined,\n };\n return Object.fromEntries(\n Object.entries(fields).filter(([, v]) => v !== undefined),\n );\n}\n\nfunction toShippingOption(\n rate: CreateCheckoutSessionInput['shippingRates'][number],\n currency: string,\n tax?: StripeTaxOptions,\n): Stripe.Checkout.SessionCreateParams.ShippingOption {\n const days = (value: number) => ({ unit: 'business_day' as const, value });\n return {\n shipping_rate_data: {\n type: 'fixed_amount',\n display_name: rate.label,\n fixed_amount: { amount: rate.amountCents, currency },\n ...(tax && {\n tax_behavior: tax.behavior ?? 'exclusive',\n ...(tax.shippingTaxCode && { tax_code: tax.shippingTaxCode }),\n }),\n ...(rate.minDays !== undefined &&\n rate.maxDays !== undefined && {\n delivery_estimate: {\n minimum: days(rate.minDays),\n maximum: days(rate.maxDays),\n },\n }),\n },\n };\n}\n\nfunction toChosenShipping(\n session: Stripe.Checkout.Session,\n): ChosenShipping | undefined {\n const cost = session.shipping_cost;\n const rate = cost?.shipping_rate;\n if (!cost || !rate || typeof rate === 'string') return undefined;\n return {\n label: rate.display_name ?? 'Shipping',\n amountCents: cost.amount_subtotal,\n };\n}\n\nexport function toPaymentEvent(\n event: Stripe.Event,\n expanded?: Stripe.Checkout.Session,\n): PaymentEvent {\n if (event.type === 'checkout.session.completed') {\n const session = event.data.object;\n const shipping = session.collected_information?.shipping_details;\n return {\n id: event.id,\n type: 'checkout.completed',\n sessionId: session.id,\n email: session.customer_details?.email ?? undefined,\n shippingAddress: toShippingAddress(shipping?.name, shipping?.address),\n shipping: expanded && toChosenShipping(expanded),\n ...(session.total_details && {\n taxCents: session.total_details.amount_tax,\n }),\n paymentPending: session.payment_status === 'unpaid',\n };\n }\n if (event.type === 'checkout.session.async_payment_succeeded') {\n return {\n id: event.id,\n type: 'checkout.async_paid',\n sessionId: event.data.object.id,\n };\n }\n if (event.type === 'checkout.session.async_payment_failed') {\n return {\n id: event.id,\n type: 'checkout.async_failed',\n sessionId: event.data.object.id,\n };\n }\n if (event.type === 'checkout.session.expired') {\n return {\n id: event.id,\n type: 'checkout.expired',\n sessionId: event.data.object.id,\n };\n }\n return { id: event.id, type: 'ignored', sourceType: event.type };\n}\n\nfunction toRefundEvent(refund: Stripe.Refund): GatewayRefundEvent {\n return {\n gatewayRefundId: refund.id,\n amountCents: refund.amount,\n idempotencyKey: refund.metadata?.[REFUND_KEY_METADATA] || undefined,\n };\n}\n\n// Newer Stripe API versions leave refunds off the charge, so list them.\nasync function refundedEvent(\n stripe: Stripe,\n event: Stripe.ChargeRefundedEvent,\n): Promise<PaymentEvent> {\n const charge = event.data.object;\n const intent = charge.payment_intent;\n const ignored: PaymentEvent = {\n id: event.id,\n type: 'ignored',\n sourceType: event.type,\n };\n if (!intent) return ignored;\n const { data: sessions } = await stripe.checkout.sessions.list({\n payment_intent: typeof intent === 'string' ? intent : intent.id,\n limit: 1,\n });\n if (sessions.length === 0) return ignored;\n const refunds =\n charge.refunds?.data ??\n (await stripe.refunds.list({ charge: charge.id, limit: 100 })).data;\n return {\n id: event.id,\n type: 'payment.refunded',\n sessionId: sessions[0].id,\n refunds: refunds\n .filter((refund) => refund.status !== 'failed')\n .map(toRefundEvent),\n };\n}\n\n// A one-off coupon for the exact amount the kit computed, so Stripe displays\n// and applies the discount without owning any of its rules. Stripe applies\n// amount_off to line items only, leaving shipping undiscounted.\nfunction createCoupon(\n stripe: Stripe,\n { discount, currency, expiresAt }: CreateCheckoutSessionInput,\n): Promise<Stripe.Coupon> {\n return stripe.coupons.create({\n amount_off: discount!.amountCents,\n currency: currency.toLowerCase(),\n duration: 'once',\n max_redemptions: 1,\n redeem_by: Math.floor(expiresAt.getTime() / 1000),\n name: discount!.code,\n });\n}\n\nexport function createStripeGateway({\n secretKey,\n webhookSecret,\n client,\n tax,\n}: StripeGatewayOptions): PaymentGateway {\n let stripe: Promise<Stripe> | undefined = client\n ? Promise.resolve(client)\n : undefined;\n const getStripe = () => (stripe ??= loadStripe(secretKey));\n\n return {\n async preflight() {\n await getStripe();\n },\n async createCheckoutSession(input) {\n const stripeClient = await getStripe();\n const coupon =\n input.discount && (await createCoupon(stripeClient, input));\n const session = await stripeClient.checkout.sessions.create({\n mode: 'payment',\n client_reference_id: input.reference,\n metadata: { reference: input.reference },\n line_items: input.lines.map((line) => ({\n quantity: line.quantity,\n price_data: {\n currency: input.currency.toLowerCase(),\n unit_amount: line.unitAmountCents,\n ...(tax && { tax_behavior: tax.behavior ?? 'exclusive' }),\n product_data: {\n name: line.name,\n metadata: { productId: line.productId },\n ...(tax && line.taxCode && { tax_code: line.taxCode }),\n },\n },\n })),\n ...(tax && { automatic_tax: { enabled: true } }),\n ...(coupon && { discounts: [{ coupon: coupon.id }] }),\n success_url: input.successUrl,\n cancel_url: input.cancelUrl,\n expires_at: Math.floor(input.expiresAt.getTime() / 1000),\n ...(input.shippingRates.length > 0 && {\n shipping_options: input.shippingRates.map((rate) =>\n toShippingOption(rate, input.currency.toLowerCase(), tax),\n ),\n }),\n shipping_address_collection: {\n allowed_countries:\n input.shippingCountries as Stripe.Checkout.SessionCreateParams.ShippingAddressCollection.AllowedCountry[],\n },\n });\n if (!session.url) throw new Error('Stripe returned no checkout URL');\n return { id: session.id, url: session.url };\n },\n async verifyWebhook(rawBody, signature) {\n const stripeClient = await getStripe();\n let event: Stripe.Event;\n try {\n event = stripeClient.webhooks.constructEvent(\n rawBody,\n signature ?? '',\n webhookSecret,\n );\n } catch (err) {\n throw new WebhookSignatureError((err as Error).message);\n }\n if (event.type === 'charge.refunded') {\n return refundedEvent(stripeClient, event);\n }\n if (event.type !== 'checkout.session.completed') {\n return toPaymentEvent(event);\n }\n const session = await stripeClient.checkout.sessions.retrieve(\n event.data.object.id,\n { expand: ['shipping_cost.shipping_rate'] },\n );\n return toPaymentEvent(event, session);\n },\n async refund({ sessionId, amountCents, idempotencyKey }) {\n try {\n const stripeClient = await getStripe();\n const session =\n await stripeClient.checkout.sessions.retrieve(sessionId);\n const intent = session.payment_intent;\n if (!intent) {\n throw new RefundRejectedError(`Session ${sessionId} has no payment`);\n }\n const refund = await stripeClient.refunds.create(\n {\n payment_intent: typeof intent === 'string' ? intent : intent.id,\n amount: amountCents,\n metadata: { [REFUND_KEY_METADATA]: idempotencyKey },\n },\n { idempotencyKey },\n );\n return { gatewayRefundId: refund.id };\n } catch (err) {\n throw rejectedRefund(err);\n }\n },\n };\n}\n","import type { CommerceDb } from './db/types';\nimport { releaseRedemptions } from './discounts/redemptions';\nimport { releaseExpiredReferences } from './inventory/service';\n\nexport interface HoldSweeperOptions {\n intervalMs?: number;\n onError?: (err: unknown) => void;\n}\n\nexport interface HoldSweeper {\n stop(): void;\n}\n\n// An order whose holds lapsed gives its discount slot back with them.\nexport async function sweepExpiredHolds(db: CommerceDb): Promise<void> {\n await db.transaction(async (tx) => {\n await releaseRedemptions(tx, await releaseExpiredReferences(tx));\n });\n}\n\nexport function startHoldSweeper(\n db: CommerceDb,\n { intervalMs = 60_000, onError }: HoldSweeperOptions = {},\n): HoldSweeper {\n const timer = setInterval(() => {\n sweepExpiredHolds(db).catch((err) => onError?.(err));\n }, intervalMs);\n timer.unref();\n return { stop: () => clearInterval(timer) };\n}\n","import {\n formatOrderRef,\n type OrderDetail,\n} from '@develemit/commerce-contracts';\n\nexport interface EmailLine {\n label: string;\n amount: string;\n}\n\nexport interface OrderEmailLabels {\n subtotal: string;\n discount: (code: string | null) => string;\n shipping: (label: string | null) => string;\n tax: string;\n total: string;\n}\n\nexport interface OrderEmailLinesOptions {\n locale?: string;\n labels?: Partial<OrderEmailLabels>;\n}\n\nexport interface OrderEmailLinesText {\n items: string;\n totals: string;\n address: string;\n}\n\nexport interface OrderEmailLinesHtml {\n items: string;\n totals: string;\n address: string;\n}\n\nexport interface OrderEmailLines {\n reference: string;\n items: EmailLine[];\n subtotal: EmailLine;\n discount?: EmailLine;\n shipping: EmailLine;\n tax?: EmailLine;\n total: EmailLine;\n address?: string[];\n text: OrderEmailLinesText;\n html: OrderEmailLinesHtml;\n}\n\nconst defaultLabels: OrderEmailLabels = {\n subtotal: 'Subtotal',\n discount: (code) => (code ? `Discount (${code})` : 'Discount'),\n shipping: (label) => (label ? `Shipping (${label})` : 'Shipping'),\n tax: 'Tax',\n total: 'Total',\n};\n\nconst escapes: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n};\n\nexport function escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (c) => escapes[c]);\n}\n\nfunction addressLines(order: OrderDetail): string[] | undefined {\n const a = order.shippingAddress;\n if (!a) return undefined;\n const lines = [\n a.name,\n a.line1,\n a.line2,\n [a.city, a.state, a.postalCode].filter(Boolean).join(' '),\n a.country,\n ].filter((l): l is string => Boolean(l));\n return lines.length ? lines : undefined;\n}\n\nfunction textBlock(lines: EmailLine[]): string {\n return lines.map((l) => `${l.label} ${l.amount}`).join('\\n');\n}\n\nfunction htmlRows(lines: EmailLine[]): string {\n return lines\n .map(\n (l) =>\n `<tr><td>${escapeHtml(l.label)}</td><td align=\"right\">${escapeHtml(l.amount)}</td></tr>`,\n )\n .join('');\n}\n\nexport function orderEmailLines(\n order: OrderDetail,\n options: OrderEmailLinesOptions = {},\n): OrderEmailLines {\n const locale = options.locale ?? 'en-US';\n const labels = { ...defaultLabels, ...options.labels };\n const money = (cents: number) =>\n new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: order.currency,\n }).format(cents / 100);\n\n const items = order.items.map((i) => ({\n label: `${i.name} × ${i.quantity}`,\n amount: money(i.priceCents * i.quantity),\n }));\n const subtotal = {\n label: labels.subtotal,\n amount: money(order.subtotalCents),\n };\n const discount =\n order.discountCents > 0\n ? {\n label: labels.discount(order.discountCode),\n amount: `-${money(order.discountCents)}`,\n }\n : undefined;\n const shipping = {\n label: labels.shipping(order.shippingLabel),\n amount: money(order.shippingCents),\n };\n const tax =\n order.taxCents > 0\n ? { label: labels.tax, amount: money(order.taxCents) }\n : undefined;\n const total = { label: labels.total, amount: money(order.totalCents) };\n const address = addressLines(order);\n\n const totals = [\n subtotal,\n ...(discount ? [discount] : []),\n shipping,\n ...(tax ? [tax] : []),\n total,\n ];\n return {\n reference: formatOrderRef(order.id),\n items,\n subtotal,\n discount,\n shipping,\n tax,\n total,\n address,\n text: {\n items: textBlock(items),\n totals: textBlock(totals),\n address: address?.join('\\n') ?? '',\n },\n html: {\n items: htmlRows(items),\n totals: htmlRows(totals),\n address: address?.map((l) => `<p>${escapeHtml(l)}</p>`).join('') ?? '',\n },\n };\n}\n","import path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { migrate } from 'drizzle-orm/node-postgres/migrator';\nimport type { NodePgDatabase } from 'drizzle-orm/node-postgres';\n\nconst here = path.dirname(fileURLToPath(import.meta.url));\n\n// Bundled into dist/index.js, but run from src/db/ under vitest: the two\n// sit at different depths from the package root.\nfunction resolveMigrationsFolder(): string {\n const fromSrc = path.resolve(here, '../../migrations');\n const fromDist = path.resolve(here, '../migrations');\n return here.endsWith(path.join('src', 'db')) ? fromSrc : fromDist;\n}\n\nexport async function runCommerceMigrations(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n db: NodePgDatabase<any>,\n): Promise<void> {\n await migrate(db, {\n migrationsFolder: resolveMigrationsFolder(),\n migrationsTable: '__commerce_migrations',\n });\n}\n","import { eq, sql } from 'drizzle-orm';\nimport type { CommerceDb } from './db/types';\nimport { productImages, products } from './db/schema';\n\nexport interface SeedProduct {\n slug: string;\n name: string;\n description?: string;\n materials?: string;\n dimensions?: string;\n priceCents: number;\n currency: string;\n category: string;\n quantity?: number;\n status?: 'draft' | 'active' | 'archived';\n featured?: boolean;\n images?: { url: string; alt?: string }[];\n}\n\nexport async function seedProducts(\n db: CommerceDb,\n items: SeedProduct[],\n): Promise<void> {\n await db.transaction(async (tx) => {\n for (const { images = [], ...item } of items) {\n const values = { status: 'active' as const, ...item };\n const [row] = await tx\n .insert(products)\n .values(values)\n .onConflictDoUpdate({\n target: products.slug,\n set: { ...values, updatedAt: sql`now()` },\n })\n .returning({ id: products.id });\n await tx.delete(productImages).where(eq(productImages.productId, row.id));\n if (images.length > 0) {\n await tx.insert(productImages).values(\n images.map((img, position) => ({\n productId: row.id,\n url: img.url,\n alt: img.alt ?? '',\n position,\n })),\n );\n }\n }\n });\n}\n","import type { CommerceDb } from '../db/types';\nimport { shippingRates } from '../db/schema';\n\nexport interface SeedShippingRate {\n label: string;\n amountCents: number;\n currency: string;\n minDays?: number;\n maxDays?: number;\n active?: boolean;\n sort?: number;\n}\n\nexport async function seedShippingRates(\n db: CommerceDb,\n rates: SeedShippingRate[],\n): Promise<void> {\n await db.transaction(async (tx) => {\n for (const rate of rates) {\n await tx\n .insert(shippingRates)\n .values(rate)\n .onConflictDoUpdate({\n target: shippingRates.label,\n set: {\n amountCents: rate.amountCents,\n currency: rate.currency,\n minDays: rate.minDays ?? null,\n maxDays: rate.maxDays ?? null,\n active: rate.active ?? true,\n sort: rate.sort ?? 0,\n },\n });\n }\n });\n}\n","import type { FileStorage } from './types';\n\nexport interface MemoryStorage extends FileStorage {\n files: Map<string, { body: Buffer; contentType: string }>;\n}\n\nexport function createMemoryStorage(baseUrl = 'memory://'): MemoryStorage {\n const files: MemoryStorage['files'] = new Map();\n return {\n files,\n async put(key, stream, contentType) {\n const chunks: Buffer[] = [];\n for await (const chunk of stream) chunks.push(Buffer.from(chunk));\n files.set(key, { body: Buffer.concat(chunks), contentType });\n return { key, url: `${baseUrl}${key}` };\n },\n async delete(key) {\n files.delete(key);\n },\n };\n}\n","import { createReadStream, createWriteStream } from 'node:fs';\nimport { mkdir, realpath, rm, stat } from 'node:fs/promises';\nimport path from 'node:path';\nimport type { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport type { FileStorage } from './types';\n\nexport interface DiskStorageOptions {\n dir: string;\n publicUrl: string;\n}\n\nexport interface StoredFileStream {\n stream: Readable;\n contentType: string | undefined;\n size: number;\n}\n\nconst contentTypes: Record<string, string> = {\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.png': 'image/png',\n '.webp': 'image/webp',\n '.gif': 'image/gif',\n '.avif': 'image/avif',\n};\n\nfunction hasTraversalSegment(key: string): boolean {\n return key.split(/[\\\\/]/).some((segment) => segment === '..');\n}\n\nfunction decodesToTraversal(key: string): boolean {\n try {\n return hasTraversalSegment(decodeURIComponent(key));\n } catch {\n return true;\n }\n}\n\nfunction isInside(root: string, target: string): boolean {\n return target.startsWith(root + path.sep);\n}\n\nfunction resolveKey(dir: string, key: string): string | null {\n if (!key || key.includes('\\0') || path.isAbsolute(key)) return null;\n if (hasTraversalSegment(key) || decodesToTraversal(key)) return null;\n const root = path.resolve(dir);\n const target = path.resolve(root, key);\n return isInside(root, target) ? target : null;\n}\n\nasync function realInside(\n dir: string,\n target: string,\n allowRoot = false,\n): Promise<string | null> {\n const [root, real] = await Promise.all([\n realpath(dir),\n realpath(target),\n ]).catch(() => [null, null]);\n if (!root || !real) return null;\n return isInside(root, real) || (allowRoot && real === root) ? real : null;\n}\n\nexport function createDiskStorage(options: DiskStorageOptions): FileStorage {\n const { dir, publicUrl } = options;\n const base = publicUrl.replace(/\\/+$/, '');\n return {\n async put(key, stream) {\n const target = resolveKey(dir, key);\n if (!target)\n throw new Error(`Invalid storage key: ${JSON.stringify(key)}`);\n await mkdir(path.dirname(target), { recursive: true });\n if (\n !(await realInside(dir, path.dirname(target), true).catch(() => null))\n ) {\n throw new Error(`Invalid storage key: ${JSON.stringify(key)}`);\n }\n await pipeline(stream, createWriteStream(target));\n return { key, url: `${base}/${key}` };\n },\n async delete(key) {\n const target = resolveKey(dir, key);\n if (!target)\n throw new Error(`Invalid storage key: ${JSON.stringify(key)}`);\n if (\n !(await realInside(dir, path.dirname(target), true).catch(() => null))\n )\n return;\n await rm(target, { force: true });\n },\n };\n}\n\nexport async function openStoredFile(\n dir: string,\n key: string,\n): Promise<StoredFileStream | null> {\n const target = resolveKey(dir, key);\n if (!target) return null;\n const real = await realInside(dir, target);\n if (!real) return null;\n const info = await stat(real).catch(() => null);\n if (!info?.isFile()) return null;\n const contentType = contentTypes[path.extname(real).toLowerCase()];\n return { stream: createReadStream(real), contentType, size: info.size };\n}\n","import type { CheckoutUrls } from '../orders/types';\nimport { SESSION_ID_PLACEHOLDER, type PaymentGateway } from './gateway';\nimport { createFakeGateway, type FakeTaxOptions } from './fake-gateway';\nimport { createStripeGateway, type StripeTaxOptions } from './stripe-gateway';\n\nexport interface PaymentEnv {\n NODE_ENV?: string;\n PAYMENT_GATEWAY?: string;\n STRIPE_SECRET_KEY?: string;\n STRIPE_WEBHOOK_SECRET?: string;\n}\n\nexport interface ResolvePaymentOptions {\n tax?: StripeTaxOptions;\n fakeTax?: FakeTaxOptions;\n}\n\nexport function resolvePayment(\n env: PaymentEnv = process.env,\n { tax, fakeTax }: ResolvePaymentOptions = {},\n): PaymentGateway {\n const secretKey = env.STRIPE_SECRET_KEY || undefined;\n const webhookSecret = env.STRIPE_WEBHOOK_SECRET || undefined;\n if (!secretKey !== !webhookSecret) {\n throw new Error(\n 'Set STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET together, or neither',\n );\n }\n if (env.PAYMENT_GATEWAY === 'stripe' && !secretKey) {\n throw new Error('PAYMENT_GATEWAY=stripe needs the Stripe keys');\n }\n if (env.PAYMENT_GATEWAY === 'fake' || !secretKey) {\n if (env.NODE_ENV === 'production') {\n throw new Error(\n 'The fake payment gateway is not allowed in production; set the Stripe keys',\n );\n }\n return createFakeGateway({ tax: fakeTax });\n }\n return createStripeGateway({\n secretKey,\n webhookSecret: webhookSecret!,\n tax,\n });\n}\n\nexport interface CheckoutUrlPaths {\n successPath?: string;\n cancelPath?: string;\n}\n\nexport function checkoutUrls(\n webUrl: string,\n {\n successPath = '/checkout/success',\n cancelPath = '/shop',\n }: CheckoutUrlPaths = {},\n): CheckoutUrls {\n const base = webUrl.replace(/\\/+$/, '');\n return {\n success: (sessionId) => {\n const id =\n sessionId === SESSION_ID_PLACEHOLDER\n ? sessionId\n : encodeURIComponent(sessionId);\n return `${base}${successPath}?session_id=${id}`;\n },\n cancel: `${base}${cancelPath}`,\n };\n}\n"],"mappings":"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","names":["adminProductInputSchema","adminProductPatchSchema","text","and","asc","eq","sql","asc","sql","eq","and","at","randomUUID","adminProductInputSchema","adminProductPatchSchema","and","asc","eq","gt","inArray","lt","sql","inArray","asc","sql","and","eq","gt","lt","and","asc","eq","gt","inArray","or","sql","and","eq","inArray","asc","conditions","sql","or","gt","withImages","decodeProductCursor","at","and","desc","eq","ilike","lt","or","sql","escapeLike","eq","sql","or","ilike","lt","and","desc","and","asc","eq","sql","and","eq","isNotNull","or","sql","or","isNotNull","sql","eq","idempotencyKey","and","sql","eq","asc","and","and","desc","eq","gt","ilike","inArray","isNull","lt","or","sql","eq","discountId","sql","and","inArray","escapeLike","or","isNull","gt","ilike","lt","desc","eq","sql","eq","sql","idempotencyKey","recordRefund","eq","sql","eq","sql","isZodError","UUID","BAD_REQUEST","isZodError","UUID","randomUUID","UUID","shippingRates","randomUUID","randomUUID","randomUUID","refunds","ignored","idempotencyKey","eq","sql","sql","eq","path"]}
|