@aglyn/plugins-commerce 1.0.0-beta.264 → 1.0.0-beta.266

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.
Files changed (29) hide show
  1. package/package.json +13 -13
  2. package/src/lib/components/console/product-editor-dialog.component.js +10 -1
  3. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  4. package/src/lib/components/console/products-hub-card.component.js +11 -10
  5. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  6. package/src/lib/server/billing-webhook.js +3 -1
  7. package/src/lib/server/billing-webhook.js.map +1 -1
  8. package/src/lib/server/pos-payment.js +9 -5
  9. package/src/lib/server/pos-payment.js.map +1 -1
  10. package/src/lib/server/pos-terminal.d.ts +12 -1
  11. package/src/lib/server/pos-terminal.js +82 -3
  12. package/src/lib/server/pos-terminal.js.map +1 -1
  13. package/src/lib/server/printers.js +33 -5
  14. package/src/lib/server/printers.js.map +1 -1
  15. package/src/lib/server/product-drafts.js +3 -2
  16. package/src/lib/server/product-drafts.js.map +1 -1
  17. package/src/lib/server/product-writer.js +3 -1
  18. package/src/lib/server/product-writer.js.map +1 -1
  19. package/src/lib/server/products-write.js +3 -2
  20. package/src/lib/server/products-write.js.map +1 -1
  21. package/src/lib/server/returns.js +3 -1
  22. package/src/lib/server/returns.js.map +1 -1
  23. package/src/lib/tenant-emails.js +264 -84
  24. package/src/lib/tenant-emails.js.map +1 -1
  25. package/src/lib/testing/memory-firestore.d.ts +5 -3
  26. package/src/lib/testing/memory-firestore.js +6 -3
  27. package/src/lib/testing/memory-firestore.js.map +1 -1
  28. package/src/lib/transfer/products.server.js +3 -2
  29. package/src/lib/transfer/products.server.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-terminal.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport { merchantAccountIsReady } from '@aglyn/tenant-data-admin/server/payment-provider'\nimport {\n cardAuthenticationParams,\n checkoutSessionCardAuthenticationParams,\n} from '@aglyn/tenant-data-admin/server/stripe-card-authentication'\nimport * as CommerceModel from '../model'\nimport type { PosRegisterSettings } from '../plugin-config'\nimport {\n POS_CURRENCY,\n posCardApplicationFeeCents,\n posStripe,\n posStripeErrorMessage,\n posStripeTestMode,\n posTerminalAvailable,\n} from './pos-stripe'\nimport { applyPosPayment, readPosSale, type PosLiftedOrder, type PosPaymentOutcome } from './pos-sale'\n\n/*==========================================\n * STRIPE TERMINAL AND THE REGISTER'S OTHER CARD TENDERS (AGL-3607).\n *\n * ## Where the readers live\n *\n * Every register charge is a DESTINATION charge on the platform account, so\n * Stripe's rule for that model applies: Locations and Readers are created on\n * the PLATFORM with the platform key, and identify their merchant by\n * metadata. One Location per site (`hosts/{hostId}/terminal/config`), and one\n * `hosts/{hostId}/terminalReaders/{readerId}` document per reader, written\n * only here. The document is what makes a reader THIS site's: every call that\n * names a reader reads it under the caller's own site first, so one store can\n * never drive, rename or remove another's reader, and the reader's own\n * `metadata.hostId` is checked again before a payment is sent to it.\n *\n * ## Why the reader charges with MANUAL capture\n *\n * On-reader tipping changes the amount after the PaymentIntent exists: the\n * reader adds the tip on confirmation and reports it in\n * `amount_details.tip.amount`. `application_fee_amount` set at creation was\n * sized on the pre-tip amount, and the platform's take must exclude the tip\n * while Stripe's processing cost (debited from the platform) is on the whole\n * charge. So the reader only AUTHORIZES; {@link refreshPosCardPayment} reads\n * the tip, re-prices the fee as\n *\n * take share (of the sale, never of the tip) + card processing (whole charge)\n *\n * and captures with that exact `application_fee_amount`. Stripe's own Connect\n * guidance for Terminal is the same: inspect, fix the application fee, then\n * capture. An authorization expires after two days, and a register capture\n * happens seconds after the tap.\n *=========================================*/\n\nconst NOT_A_SALE = { ok: false as const, status: 409, error: 'This sale is no longer open' }\n\n/** `hosts/{hostId}/terminalReaders/{readerId}`. */\nexport interface PosTerminalReader {\n label: string\n registerId?: string\n stripeLocationId: string\n deviceType?: string\n serialNumber?: string\n status?: 'online' | 'offline' | string\n livemode: boolean\n createdAtMs: number\n createdBy?: string\n lastSeenAtMs?: number\n}\n\n/**\n * The payment without its failure reason. Removed, never set to `undefined`:\n * Firestore refuses an undefined field inside the `payments` array.\n */\nfunction withoutFailure(payment: CommerceModel.OrderPayment): CommerceModel.OrderPayment {\n const next = { ...payment }\n delete next.failureMessage\n return next\n}\n\n/** The merchant's connected account, when it can take card payments now. */\nexport async function posMerchantAccount(\n hostId: string,\n org: Record<string, any> | null,\n): Promise<string | null> {\n const ownerUid = String(org?.['ownerUid'] ?? '')\n if (!ownerUid) return null\n const profile = await firebaseAdmin.app().firestore().collection('profiles').doc(ownerUid).get()\n const accountId = profile.get('stripeAccountId')\n if (\n !merchantAccountIsReady(\n {\n accountId,\n chargesEnabled: profile.get('stripeChargesEnabled'),\n accountLivemode: profile.get('stripeAccountLivemode'),\n },\n { subject: `POS card payment host ${hostId}` },\n )\n ) {\n return null\n }\n return String(accountId)\n}\n\nfunction terminalConfigRef(hostId: string) {\n return firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .collection('terminal')\n .doc('config')\n}\n\nfunction readerRef(hostId: string, readerId: string) {\n return firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .collection('terminalReaders')\n .doc(readerId)\n}\n\n/** A reader id as Stripe mints them; anything else never reaches Stripe. */\nexport function isTerminalReaderId(value: unknown): value is string {\n return typeof value === 'string' && /^tmr_[A-Za-z0-9]{6,64}$/.test(value)\n}\n\n/** The reader, when it is THIS site's; null for anyone else's or none. */\nexport async function ownedTerminalReader(\n hostId: string,\n readerId: unknown,\n): Promise<(PosTerminalReader & { id: string }) | null> {\n if (!isTerminalReaderId(readerId)) return null\n const snapshot = await readerRef(hostId, readerId).get()\n if (!snapshot.exists) return null\n return { ...(snapshot.data() as PosTerminalReader), id: readerId }\n}\n\n/** A Terminal Location's address, as the console sends it. */\nexport interface PosTerminalAddress {\n line1: string\n line2?: string\n city: string\n state?: string\n postalCode: string\n country: string\n}\n\nexport function posTerminalAddressProblem(address: Partial<PosTerminalAddress> | undefined): string | null {\n if (!address?.line1 || !address?.city || !address?.postalCode || !address?.country) {\n return 'Enter the street, city, postal code and country where the reader is used.'\n }\n if (!/^[A-Z]{2}$/.test(String(address.country))) return 'Choose a two-letter country code, like US.'\n return null\n}\n\n/**\n * The site's Terminal Location, created on first use (on the PLATFORM, with\n * the merchant named in metadata). Its address is the store's, because Stripe\n * uses it to configure the reader for the right country.\n */\nexport async function ensurePosTerminalLocation(options: {\n hostId: string\n orgId: string\n displayName: string\n address?: PosTerminalAddress\n}): Promise<{ ok: true; locationId: string } | { ok: false; status: number; error: string }> {\n const ref = terminalConfigRef(options.hostId)\n const existing = await ref.get()\n const known = String(existing.get('stripeLocationId') ?? '')\n if (known) return { ok: true, locationId: known }\n const problem = posTerminalAddressProblem(options.address)\n if (problem) return { ok: false, status: 400, error: problem }\n const address = options.address as PosTerminalAddress\n const created = await posStripe('POST', 'terminal/locations', {\n idempotencyKey: `pos-location:${options.hostId}`,\n params: {\n display_name: options.displayName.slice(0, 100) || 'Store',\n 'address[line1]': address.line1,\n 'address[line2]': address.line2 || undefined,\n 'address[city]': address.city,\n 'address[state]': address.state || undefined,\n 'address[postal_code]': address.postalCode,\n 'address[country]': address.country,\n 'metadata[orgId]': options.orgId,\n 'metadata[hostId]': options.hostId,\n },\n })\n if (!created.ok || !created.body?.id) {\n return {\n ok: false,\n status: 502,\n error: posStripeErrorMessage(created.body, 'The reader location could not be set up.'),\n }\n }\n await ref.set(\n {\n stripeLocationId: String(created.body.id),\n livemode: Boolean(created.body.livemode),\n createdAtMs: Date.now(),\n },\n { merge: true },\n )\n return { ok: true, locationId: String(created.body.id) }\n}\n\n/** The tipping a reader shows: Stripe offers three buttons, so three presets. */\nexport function posReaderTippingKey(settings: PosRegisterSettings): string {\n return settings.tippingEnabled ? settings.tipPercentages.slice(0, 3).join(',') : 'off'\n}\n\n/**\n * Keeps the site's reader tipping in step with the merchant's settings: one\n * Terminal Configuration per site, assigned to its Location, rewritten only\n * when the presets changed. Never fatal: a reader that keeps the previous\n * presets still takes the payment.\n */\nexport async function syncPosReaderTipping(\n hostId: string,\n settings: PosRegisterSettings,\n): Promise<void> {\n if (!settings.tippingEnabled) return\n const ref = terminalConfigRef(hostId)\n const config = await ref.get()\n const locationId = String(config.get('stripeLocationId') ?? '')\n if (!locationId) return\n const key = posReaderTippingKey(settings)\n if (config.get('tippingKey') === key) return\n const percentages = settings.tipPercentages.slice(0, 3)\n const configurationId = String(config.get('stripeConfigurationId') ?? '')\n const params = {\n [`tipping[${POS_CURRENCY}][percentages]`]: percentages.map(String),\n }\n const saved = await posStripe(\n 'POST',\n configurationId ? `terminal/configurations/${configurationId}` : 'terminal/configurations',\n {\n params,\n ...(configurationId ? {} : { idempotencyKey: `pos-configuration:${hostId}:${key}` }),\n },\n )\n if (!saved.ok || !saved.body?.id) {\n console.error('[pos-terminal] tipping configuration failed', hostId, saved.body?.error)\n return\n }\n if (!configurationId) {\n const assigned = await posStripe('POST', `terminal/locations/${locationId}`, {\n params: { configuration_overrides: String(saved.body.id) },\n })\n if (!assigned.ok) {\n console.error('[pos-terminal] configuration assignment failed', hostId, assigned.body?.error)\n return\n }\n }\n await ref.set(\n { stripeConfigurationId: String(saved.body.id), tippingKey: key },\n { merge: true },\n )\n}\n\n/** Reads a reader from Stripe and keeps its status on the site's document. */\nexport async function refreshPosReaderStatus(\n hostId: string,\n readerId: string,\n): Promise<{ status: string; action: any } | null> {\n const fetched = await posStripe('GET', `terminal/readers/${readerId}`)\n if (!fetched.ok) return null\n const status = String(fetched.body?.status ?? 'offline')\n await readerRef(hostId, readerId)\n .set({ status, lastSeenAtMs: Date.now() }, { merge: true })\n .catch(() => undefined)\n return { status, action: fetched.body?.action ?? null }\n}\n\n/*==========================================\n * TAKING A CARD PAYMENT.\n *=========================================*/\n\ninterface CardStart {\n hostId: string\n orderId: string\n paymentId: string\n amountCents: number\n /** A tip already chosen (on the customer display or by the cashier). */\n tipCents: number\n cashierId: string\n org: Record<string, any> | null\n}\n\n/** Reserves a pending card payment in the ledger, or finds the one this attempt made. */\nasync function reserveCardPayment(\n start: CardStart,\n method: CommerceModel.OrderPaymentMethod,\n extra: Partial<CommerceModel.OrderPayment>,\n): Promise<PosPaymentOutcome> {\n return await applyPosPayment({\n hostId: start.hostId,\n orderId: start.orderId,\n paymentId: start.paymentId,\n decide: ({ order, payments, existing }) => {\n if (existing) {\n // The same attempt again: hand back what it started. A failed card\n // is re-armed by `retry`, not by pressing the tender a second time.\n return { kind: 'keep' }\n }\n if (order.status !== 'pending') return { kind: 'refuse', ...NOT_A_SALE }\n const total = Number(order.totals?.totalCents ?? 0)\n const tenderable = CommerceModel.posTenderableCents(total, payments)\n if (!(start.amountCents > 0)) {\n return { kind: 'refuse', status: 400, error: 'Enter an amount to charge.' }\n }\n if (start.amountCents > tenderable) {\n return {\n kind: 'refuse',\n status: 409,\n error:\n tenderable > 0\n ? `Only $${(tenderable / 100).toFixed(2)} is left to pay on this sale.`\n : 'Nothing is left to pay on this sale.',\n }\n }\n const tipProblem = CommerceModel.posTipProblem(start.tipCents, start.amountCents)\n if (tipProblem) return { kind: 'refuse', status: 400, error: tipProblem }\n const takeFeeCents = CommerceModel.posTakeShareCents({\n takeFeeCents: Number(order.posTakeFeeCents ?? 0),\n totalCents: total,\n amountCents: start.amountCents,\n })\n return {\n kind: 'put',\n payment: {\n id: start.paymentId,\n method,\n amountCents: start.amountCents,\n ...(start.tipCents > 0 ? { tipCents: start.tipCents } : {}),\n status: 'pending',\n atMs: Date.now(),\n takeFeeCents,\n cashierId: start.cashierId,\n ...extra,\n },\n }\n },\n })\n}\n\n/** Writes Stripe's answer onto a pending card payment. */\nasync function patchCardPayment(\n hostId: string,\n orderId: string,\n paymentId: string,\n patch: (payment: CommerceModel.OrderPayment) => CommerceModel.OrderPayment | null,\n event?: { event: string; detail?: string },\n): Promise<PosPaymentOutcome> {\n return await applyPosPayment({\n hostId,\n orderId,\n paymentId,\n decide: ({ existing }) => {\n if (!existing) return { kind: 'refuse', status: 404, error: 'Unknown payment' }\n const next = patch(existing)\n if (!next) return { kind: 'keep' }\n return { kind: 'put', payment: next, ...(event ? { event } : {}) }\n },\n })\n}\n\nfunction paymentIntentParams(\n start: CardStart,\n payment: CommerceModel.OrderPayment,\n destination: string,\n extra: Record<string, string | number | boolean | undefined | readonly string[]>,\n) {\n const chargeCents = start.amountCents + start.tipCents\n const fee = posCardApplicationFeeCents({\n takeShareCents: Number(payment.takeFeeCents ?? 0),\n chargeCents,\n })\n return {\n amount: chargeCents,\n currency: POS_CURRENCY,\n 'transfer_data[destination]': destination,\n ...(fee > 0 ? { application_fee_amount: fee } : {}),\n 'metadata[kind]': 'pos',\n 'metadata[hostId]': start.hostId,\n 'metadata[orderId]': start.orderId,\n 'metadata[paymentId]': start.paymentId,\n 'metadata[tipCents]': String(start.tipCents),\n ...extra,\n }\n}\n\n/**\n * Creates (or finds) the card-present PaymentIntent for one payment WITHOUT\n * choosing how the card is collected: reserves the amount in the ledger and\n * creates a manual-capture destination charge to the merchant (settled on\n * Aglyn's platform account, with no `on_behalf_of`, as ToS §10.7 states),\n * keyed by the payment so a retry returns the same intent.\n *\n * Two collectors use it. {@link startCardPresentPayment} pushes the intent\n * to a smart reader the site owns; a native companion app (AGL-3618)\n * collects it with the Stripe Terminal SDK (Tap to Pay, Bluetooth readers)\n * using the returned `clientSecret`. However the card is collected, the\n * payment is recorded by {@link refreshPosCardPayment}, keyed on the intent's\n * metadata, from the register's poll or the webhook alike.\n */\nexport async function createCardPresentIntent(\n start: CardStart & { readerId?: string },\n): Promise<PosPaymentOutcome & { clientSecret?: string; paymentIntentId?: string }> {\n // Live card-present payments wait for Terminal to be switched on for the\n // platform (`STRIPE_TERMINAL_LIVE_ENABLED`); the hidden controls are not\n // the only gate.\n if (!posTerminalAvailable()) {\n return { ok: false, status: 409, error: 'Card readers are not available for this store yet.' }\n }\n const destination = await posMerchantAccount(start.hostId, start.org)\n if (!destination) return { ok: false, status: 409, error: 'Card payments not set up' }\n const reserved = await reserveCardPayment(\n start,\n 'card_present',\n start.readerId ? { readerId: start.readerId } : {},\n )\n if (!reserved.ok || !reserved.payment) return reserved\n const payment = reserved.payment\n if (payment.status !== 'pending') return reserved\n // Keyed by the payment: a second call for the same payment replays the\n // intent Stripe already made rather than opening another.\n const intent = await posStripe('POST', 'payment_intents', {\n idempotencyKey: `pos-pi:${payment.id}`,\n params: paymentIntentParams(start, payment, destination, {\n payment_method_types:\n POS_CURRENCY === 'usd' ? ['card_present'] : ['card_present', 'interac_present'],\n capture_method: 'manual',\n ...(start.readerId ? { 'metadata[readerId]': start.readerId } : {}),\n description: 'In-store purchase',\n }),\n })\n if (!intent.ok || !intent.body?.id) {\n return await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n status: 'failed',\n failureMessage: posStripeErrorMessage(intent.body, 'The card payment could not be started.'),\n }))\n }\n const paymentIntentId = String(intent.body.id)\n const recorded = payment.paymentIntentId\n ? reserved\n : await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n paymentIntentId,\n livemode: Boolean(intent.body.livemode),\n }))\n return {\n ...recorded,\n paymentIntentId,\n ...(intent.body.client_secret ? { clientSecret: String(intent.body.client_secret) } : {}),\n }\n}\n\n/**\n * A smart-reader payment: the card-present intent from\n * {@link createCardPresentIntent}, pushed to a reader THIS site owns. The\n * register then polls {@link refreshPosCardPayment} until the reader answers;\n * webhooks land on the same function.\n */\nexport async function startCardPresentPayment(\n start: CardStart & { readerId: string; settings: PosRegisterSettings },\n): Promise<PosPaymentOutcome> {\n const reader = await ownedTerminalReader(start.hostId, start.readerId)\n if (!reader) return { ok: false, status: 404, error: 'That card reader is not on this site.' }\n await syncPosReaderTipping(start.hostId, start.settings).catch((error) =>\n console.error('[pos-terminal] tipping sync failed', error),\n )\n const created = await createCardPresentIntent({ ...start, readerId: reader.id })\n if (!created.ok || !created.payment || !created.paymentIntentId) return created\n if (created.payment.status !== 'pending') return created\n return await sendToReader({\n hostId: start.hostId,\n orderId: start.orderId,\n paymentId: created.payment.id,\n readerId: reader.id,\n paymentIntentId: created.paymentIntentId,\n // A tip the customer already chose on the display is in the amount;\n // asking again on the reader would ask twice.\n skipTipping: !start.settings.tippingEnabled || start.tipCents > 0,\n attempt: 0,\n })\n}\n\n/** The site's Terminal Location id, or null before a reader was ever set up. */\nexport async function posTerminalLocationId(hostId: string): Promise<string | null> {\n const config = await terminalConfigRef(hostId).get()\n return String(config.get('stripeLocationId') ?? '') || null\n}\n\n/**\n * A Terminal SDK connection token scoped to the site's own Location, for a\n * native companion app's SDK (AGL-3618). Scoping it to the Location is what\n * keeps one store's app from discovering another store's readers: Stripe\n * only lets a location-scoped token connect to readers in that Location.\n * Null when the site has no Location yet, never an unscoped token.\n */\nexport async function createPosTerminalConnectionToken(\n hostId: string,\n): Promise<{ secret: string; locationId: string } | null> {\n const locationId = await posTerminalLocationId(hostId)\n if (!locationId) return null\n const token = await posStripe('POST', 'terminal/connection_tokens', {\n params: { location: locationId },\n })\n if (!token.ok || !token.body?.secret) return null\n return { secret: String(token.body.secret), locationId }\n}\n\nasync function sendToReader(input: {\n hostId: string\n orderId: string\n paymentId: string\n readerId: string\n paymentIntentId: string\n skipTipping: boolean\n attempt: number\n}): Promise<PosPaymentOutcome> {\n const processed = await posStripe(\n 'POST',\n `terminal/readers/${input.readerId}/process_payment_intent`,\n {\n idempotencyKey: `pos-process:${input.paymentId}:${input.attempt}`,\n params: {\n payment_intent: input.paymentIntentId,\n 'process_config[enable_customer_cancellation]': true,\n ...(input.skipTipping ? { 'process_config[skip_tipping]': true } : {}),\n },\n },\n )\n if (!processed.ok) {\n return await patchCardPayment(input.hostId, input.orderId, input.paymentId, (current) => ({\n ...current,\n status: 'failed',\n failureMessage: posStripeErrorMessage(processed.body, 'The card reader could not start the payment.'),\n }))\n }\n return await patchCardPayment(input.hostId, input.orderId, input.paymentId, (current) => {\n if (!current.failureMessage) return null\n return withoutFailure(current)\n })\n}\n\n/**\n * Re-arms a declined or failed reader payment on the SAME PaymentIntent, as\n * Stripe asks (a new one per attempt risks a double charge).\n */\nexport async function retryCardPresentPayment(input: {\n hostId: string\n orderId: string\n paymentId: string\n settings: PosRegisterSettings\n}): Promise<PosPaymentOutcome> {\n const order = await readPosSale(input.hostId, input.orderId)\n if (!order || order.status !== 'pending') return NOT_A_SALE\n const payment = CommerceModel.orderPayments(order).find((entry) => entry.id === input.paymentId)\n if (!payment || payment.method !== 'card_present' || !payment.paymentIntentId || !payment.readerId) {\n return { ok: false, status: 404, error: 'Unknown payment' }\n }\n if (payment.status === 'succeeded') return { ok: true, order, payment, completed: false, changed: false }\n const reader = await ownedTerminalReader(input.hostId, payment.readerId)\n if (!reader) return { ok: false, status: 404, error: 'That card reader is not on this site.' }\n // Back to pending first, so the amount is reserved again before the reader\n // is asked; a sale paid meanwhile by another tender refuses here.\n const rearmed = await applyPosPayment({\n hostId: input.hostId,\n orderId: input.orderId,\n paymentId: input.paymentId,\n decide: ({ order: fresh, payments, existing }) => {\n if (!existing) return { kind: 'refuse', status: 404, error: 'Unknown payment' }\n if (existing.status === 'pending') return { kind: 'keep' }\n if (existing.status !== 'failed' && existing.status !== 'canceled') {\n return { kind: 'refuse', status: 409, error: 'This payment cannot be retried.' }\n }\n const tenderable = CommerceModel.posTenderableCents(\n Number(fresh.totals?.totalCents ?? 0),\n payments.filter((entry) => entry.id !== existing.id),\n )\n if (existing.amountCents > tenderable) {\n return { kind: 'refuse', status: 409, error: 'The balance changed. Start a new card payment.' }\n }\n return { kind: 'put', payment: { ...withoutFailure(existing), status: 'pending' } }\n },\n })\n if (!rearmed.ok) return rearmed\n const attempt = Date.now()\n return await sendToReader({\n hostId: input.hostId,\n orderId: input.orderId,\n paymentId: input.paymentId,\n readerId: reader.id,\n paymentIntentId: payment.paymentIntentId,\n skipTipping: !input.settings.tippingEnabled || Number(payment.tipCents ?? 0) > 0,\n attempt,\n })\n}\n\n/**\n * A typed card (card not present): a PaymentIntent the console confirms with\n * the Payment Element. No MOTO: the card is keyed into Stripe's own field and\n * goes through Stripe's normal online checks, 3-D Secure included.\n */\nexport async function startCardKeyedPayment(\n start: CardStart,\n): Promise<PosPaymentOutcome & { clientSecret?: string }> {\n const destination = await posMerchantAccount(start.hostId, start.org)\n if (!destination) return { ok: false, status: 409, error: 'Card payments not set up' }\n const reserved = await reserveCardPayment(start, 'card_keyed', {})\n if (!reserved.ok || !reserved.payment) return reserved\n const payment = reserved.payment\n if (payment.status !== 'pending') return reserved\n const intent = await posStripe('POST', 'payment_intents', {\n idempotencyKey: `pos-pi:${payment.id}`,\n params: paymentIntentParams(start, payment, destination, {\n payment_method_types: ['card'],\n description: 'In-store purchase (typed card)',\n // 3-D Secure from the one seam every card payment shares (AGL-3360).\n ...cardAuthenticationParams('one-time'),\n }),\n })\n if (!intent.ok || !intent.body?.id || !intent.body?.client_secret) {\n return await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n status: 'failed',\n failureMessage: posStripeErrorMessage(intent.body, 'The card payment could not be started.'),\n }))\n }\n const updated = payment.paymentIntentId\n ? reserved\n : await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n paymentIntentId: String(intent.body.id),\n livemode: Boolean(intent.body.livemode),\n }))\n return { ...updated, clientSecret: String(intent.body.client_secret) }\n}\n\n/**\n * The customer pays on their own phone from a QR code: a Stripe payment page\n * for this payment's amount (and tip, as its own line). Settled by\n * `checkout.session.completed` or by the register's poll.\n */\nexport async function startCardLinkPayment(\n start: CardStart & { returnUrl: string },\n): Promise<PosPaymentOutcome> {\n const destination = await posMerchantAccount(start.hostId, start.org)\n if (!destination) return { ok: false, status: 409, error: 'Card payments not set up' }\n const reserved = await reserveCardPayment(start, 'card_link', {})\n if (!reserved.ok || !reserved.payment) return reserved\n const payment = reserved.payment\n if (payment.checkoutSessionId || payment.status !== 'pending') return reserved\n const fee = posCardApplicationFeeCents({\n takeShareCents: Number(payment.takeFeeCents ?? 0),\n chargeCents: start.amountCents + start.tipCents,\n })\n const session = await posStripe('POST', 'checkout/sessions', {\n idempotencyKey: `pos-link:${payment.id}`,\n params: {\n mode: 'payment',\n ...checkoutSessionCardAuthenticationParams('payment'),\n 'payment_method_types': ['card'],\n 'line_items[0][quantity]': 1,\n 'line_items[0][price_data][currency]': POS_CURRENCY,\n 'line_items[0][price_data][unit_amount]': start.amountCents,\n 'line_items[0][price_data][product_data][name]': 'In-store purchase',\n ...(start.tipCents > 0\n ? {\n 'line_items[1][quantity]': 1,\n 'line_items[1][price_data][currency]': POS_CURRENCY,\n 'line_items[1][price_data][unit_amount]': start.tipCents,\n 'line_items[1][price_data][product_data][name]': 'Tip',\n }\n : {}),\n 'payment_intent_data[transfer_data][destination]': destination,\n ...(fee > 0 ? { 'payment_intent_data[application_fee_amount]': fee } : {}),\n 'payment_intent_data[metadata][kind]': 'pos',\n 'payment_intent_data[metadata][hostId]': start.hostId,\n 'payment_intent_data[metadata][orderId]': start.orderId,\n 'payment_intent_data[metadata][paymentId]': payment.id,\n 'payment_intent_data[metadata][tipCents]': String(start.tipCents),\n success_url: `${start.returnUrl}?paid=1`,\n cancel_url: `${start.returnUrl}?paid=0`,\n 'metadata[type]': 'pos-payment',\n 'metadata[hostId]': start.hostId,\n 'metadata[orderId]': start.orderId,\n 'metadata[paymentId]': payment.id,\n },\n })\n if (!session.ok || !session.body?.url) {\n return await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n status: 'failed',\n failureMessage: posStripeErrorMessage(session.body, 'The payment page could not be created.'),\n }))\n }\n return await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n checkoutSessionId: String(session.body.id),\n checkoutUrl: String(session.body.url),\n livemode: Boolean(session.body.livemode),\n }))\n}\n\n/*==========================================\n * SETTLING A CARD PAYMENT.\n *=========================================*/\n\n/** The card's brand and last four, off the charge Stripe made. */\nfunction cardDetailsOf(intent: any): { cardBrand?: string; last4?: string } {\n const charge = typeof intent?.latest_charge === 'object' ? intent.latest_charge : null\n const details =\n charge?.payment_method_details?.card_present ??\n charge?.payment_method_details?.interac_present ??\n charge?.payment_method_details?.card ??\n null\n return {\n ...(details?.brand ? { cardBrand: String(details.brand) } : {}),\n ...(details?.last4 ? { last4: String(details.last4) } : {}),\n }\n}\n\n/** The tip on an intent: what the display chose plus what the reader added. */\nexport function posIntentTipCents(intent: any): number {\n const preset = Math.max(0, Math.round(Number(intent?.metadata?.tipCents ?? 0)) || 0)\n const onReader = Math.max(0, Math.round(Number(intent?.amount_details?.tip?.amount ?? 0)) || 0)\n return preset + onReader\n}\n\n/**\n * The fee a register card charge is captured with: the payment's share of the\n * take (fixed at reservation, the sale's share and never the tip's) plus the\n * card processing cost on the whole amount, tip included.\n */\nexport function posCaptureFeeCents(payment: CommerceModel.OrderPayment, intent: any): number {\n return posCardApplicationFeeCents({\n takeShareCents: Number(payment.takeFeeCents ?? 0),\n chargeCents: Math.max(0, Math.round(Number(intent?.amount ?? 0))),\n })\n}\n\n/**\n * Brings one card payment up to date with Stripe, from a webhook or the\n * register's poll alike: captures an authorized reader payment with its final\n * fee, records a success, a decline or a cancellation. Idempotent: the\n * capture is keyed by the intent, and the ledger change is a no-op when the\n * payment already says what Stripe says.\n */\nexport async function refreshPosCardPayment(input: {\n hostId: string\n orderId: string\n paymentId: string\n}): Promise<PosPaymentOutcome> {\n const order = await readPosSale(input.hostId, input.orderId)\n if (!order) return { ok: false, status: 404, error: 'Unknown sale' }\n const payment = CommerceModel.orderPayments(order).find((entry) => entry.id === input.paymentId)\n if (!payment) return { ok: false, status: 404, error: 'Unknown payment' }\n if (!CommerceModel.isCardPaymentMethod(payment.method)) {\n return { ok: true, order, payment, completed: false, changed: false }\n }\n let paymentIntentId = payment.paymentIntentId ?? ''\n if (!paymentIntentId && payment.checkoutSessionId) {\n const session = await posStripe('GET', `checkout/sessions/${payment.checkoutSessionId}`)\n if (session.ok && session.body?.status === 'expired' && payment.status === 'pending') {\n return await patchCardPayment(input.hostId, input.orderId, payment.id, (current) =>\n current.status === 'pending' ? { ...current, status: 'canceled' } : null,\n )\n }\n paymentIntentId = String(session.body?.payment_intent ?? '')\n }\n if (!paymentIntentId) return { ok: true, order, payment, completed: false, changed: false }\n let intent = (\n await posStripe('GET', `payment_intents/${paymentIntentId}`, {\n params: { 'expand[]': 'latest_charge' },\n })\n ).body\n if (!intent?.id) return { ok: true, order, payment, completed: false, changed: false }\n if (intent.metadata?.hostId !== input.hostId || intent.metadata?.paymentId !== payment.id) {\n console.error('[pos-terminal] intent does not belong to this payment', paymentIntentId)\n return { ok: false, status: 409, error: 'This payment does not match its card charge.' }\n }\n if (intent.status === 'requires_capture') {\n if (order.status !== 'pending' && payment.status !== 'pending') {\n // Nothing to capture into: the sale is gone. Release the hold.\n await posStripe('POST', `payment_intents/${intent.id}/cancel`, {\n idempotencyKey: `pos-release:${intent.id}`,\n })\n return { ok: true, order, payment, completed: false, changed: false }\n }\n const fee = posCaptureFeeCents(payment, intent)\n const captured = await posStripe('POST', `payment_intents/${intent.id}/capture`, {\n idempotencyKey: `pos-capture:${intent.id}`,\n params: {\n ...(fee > 0 ? { application_fee_amount: fee } : {}),\n 'expand[]': 'latest_charge',\n },\n })\n if (captured.ok && captured.body?.id) intent = captured.body\n else {\n console.error('[pos-terminal] capture failed', intent.id, captured.body?.error)\n return { ok: true, order, payment, completed: false, changed: false }\n }\n }\n if (intent.status === 'succeeded') {\n const tipCents = posIntentTipCents(intent)\n const amountCents = Math.max(0, Math.round(Number(intent.amount ?? 0))) - tipCents\n return await patchCardPayment(\n input.hostId,\n input.orderId,\n payment.id,\n (current) => {\n if (current.status === 'succeeded' && current.paymentIntentId === intent.id) return null\n return {\n ...withoutFailure(current),\n status: 'succeeded',\n settledAtMs: Date.now(),\n paymentIntentId: String(intent.id),\n // What the charge actually put toward the sale. Equal to the\n // reserved amount; restated from Stripe so the ledger records the\n // money that moved rather than the money that was asked for.\n amountCents: amountCents > 0 ? amountCents : current.amountCents,\n ...(tipCents > 0 ? { tipCents } : {}),\n feeCents: Math.max(0, Math.round(Number(intent.application_fee_amount ?? 0))),\n livemode: Boolean(intent.livemode),\n ...cardDetailsOf(intent),\n }\n },\n )\n }\n if (intent.status === 'canceled') {\n return await patchCardPayment(input.hostId, input.orderId, payment.id, (current) =>\n current.status === 'pending' || current.status === 'failed'\n ? { ...current, status: 'canceled' }\n : null,\n )\n }\n if (intent.status === 'requires_payment_method' && intent.last_payment_error) {\n const message = String(intent.last_payment_error?.message ?? 'The card was declined.')\n return await patchCardPayment(input.hostId, input.orderId, payment.id, (current) =>\n current.status === 'pending' ? { ...current, status: 'failed', failureMessage: message } : null,\n )\n }\n // Still waiting on the customer. A reader action that already failed (a\n // card removed, a cancel on the reader) says so on the reader, not the\n // intent, so ask the reader too.\n if (payment.method === 'card_present' && payment.readerId && payment.status === 'pending') {\n const reader = await refreshPosReaderStatus(input.hostId, payment.readerId)\n const action = reader?.action\n if (\n action?.type === 'process_payment_intent' &&\n action?.process_payment_intent?.payment_intent === intent.id &&\n action?.status === 'failed'\n ) {\n const message = String(action.failure_message ?? 'The reader could not take the card.')\n return await patchCardPayment(input.hostId, input.orderId, payment.id, (current) =>\n current.status === 'pending' ? { ...current, status: 'failed', failureMessage: message } : null,\n )\n }\n }\n const fresh = await readPosSale(input.hostId, input.orderId)\n return {\n ok: true,\n order: fresh ?? order,\n payment:\n CommerceModel.orderPayments(fresh ?? order).find((entry) => entry.id === payment.id) ?? payment,\n completed: false,\n changed: false,\n }\n}\n\n/**\n * Stops a card payment that has not been paid: clears the reader, cancels the\n * intent, expires the payment page. A charge that already went through is\n * left alone and recorded as paid, never cancelled under the customer.\n */\nexport async function cancelPosCardPayment(input: {\n hostId: string\n orderId: string\n paymentId: string\n}): Promise<PosPaymentOutcome> {\n const refreshed = await refreshPosCardPayment(input)\n if (!refreshed.ok || !refreshed.payment) return refreshed\n const payment = refreshed.payment\n if (payment.status === 'succeeded' || payment.status === 'canceled') return refreshed\n if (payment.method === 'card_present' && payment.readerId) {\n const cleared = await posStripe('POST', `terminal/readers/${payment.readerId}/cancel_action`)\n if (!cleared.ok && cleared.body?.error?.code === 'terminal_reader_busy') {\n return {\n ok: false,\n status: 409,\n error: 'The customer is mid-payment on the reader. Wait for it to finish.',\n }\n }\n }\n if (payment.paymentIntentId) {\n await posStripe('POST', `payment_intents/${payment.paymentIntentId}/cancel`, {\n idempotencyKey: `pos-cancel:${payment.paymentIntentId}`,\n })\n }\n if (payment.checkoutSessionId) {\n await posStripe('POST', `checkout/sessions/${payment.checkoutSessionId}/expire`, {\n idempotencyKey: `pos-expire:${payment.checkoutSessionId}`,\n })\n }\n // Re-read: a payment that completed in the gap is recorded as paid.\n const settled = await refreshPosCardPayment(input)\n if (settled.ok && settled.payment?.status === 'succeeded') return settled\n return await patchCardPayment(input.hostId, input.orderId, payment.id, (current) =>\n current.status === 'pending' || current.status === 'failed'\n ? { ...current, status: 'canceled' }\n : null,\n )\n}\n\n/**\n * TEST MODE ONLY: Stripe's simulated reader is \"tapped\" with a test card.\n * Refused outright on a live key, whatever the request says.\n */\nexport async function simulatePosReaderTap(input: {\n hostId: string\n orderId: string\n paymentId: string\n /** `4000000000000002` declines; anything else Stripe's default approves. */\n cardNumber?: string\n}): Promise<PosPaymentOutcome> {\n if (!posStripeTestMode()) return { ok: false, status: 403, error: 'Only in test mode' }\n const order = await readPosSale(input.hostId, input.orderId)\n if (!order) return { ok: false, status: 404, error: 'Unknown sale' }\n const payment = CommerceModel.orderPayments(order).find((entry) => entry.id === input.paymentId)\n if (!payment?.readerId || payment.method !== 'card_present') {\n return { ok: false, status: 404, error: 'Unknown payment' }\n }\n const reader = await ownedTerminalReader(input.hostId, payment.readerId)\n if (!reader) return { ok: false, status: 404, error: 'That card reader is not on this site.' }\n const presented = await posStripe(\n 'POST',\n `test_helpers/terminal/readers/${reader.id}/present_payment_method`,\n {\n params: {\n type: 'card_present',\n ...(input.cardNumber && /^[0-9]{12,19}$/.test(input.cardNumber)\n ? { 'card_present[number]': input.cardNumber }\n : {}),\n },\n },\n )\n if (!presented.ok) {\n return {\n ok: false,\n status: 502,\n error: posStripeErrorMessage(presented.body, 'The simulated tap failed.'),\n }\n }\n return await refreshPosCardPayment(input)\n}\n\n/*==========================================\n * WEBHOOKS (the small branch `billing-webhook.ts` calls).\n *=========================================*/\n\n/** Where a Stripe object says a register payment lives, or null. */\nfunction posPaymentRef(\n metadata: Record<string, unknown> | undefined,\n): { hostId: string; orderId: string; paymentId: string } | null {\n const hostId = String(metadata?.['hostId'] ?? '')\n const orderId = String(metadata?.['orderId'] ?? '')\n const paymentId = String(metadata?.['paymentId'] ?? '')\n if (!hostId || !orderId || !paymentId) return null\n return { hostId, orderId, paymentId }\n}\n\n/**\n * The register's Stripe events: a reader finished or failed an action, a\n * register PaymentIntent was authorized, paid, declined or cancelled, or a\n * register QR page was paid or expired. Each resolves to ONE payment and is\n * answered by re-reading Stripe through {@link refreshPosCardPayment}, never\n * by trusting the event's own copy (whose API version predates the tip\n * fields). Returns the site when the event was the register's, so the\n * dispatcher can count it as claimed; null for every other event.\n */\nexport async function handlePosStripeEvent(input: {\n type: string\n object: any\n}): Promise<{ hostId: string } | null> {\n const { type, object } = input\n let ref: { hostId: string; orderId: string; paymentId: string } | null = null\n if (type === 'terminal.reader.action_succeeded' || type === 'terminal.reader.action_failed') {\n const intentId = String(object?.action?.process_payment_intent?.payment_intent ?? '')\n if (object?.action?.type !== 'process_payment_intent' || !intentId) return null\n const intent = await posStripe('GET', `payment_intents/${intentId}`)\n if (!intent.ok || intent.body?.metadata?.kind !== 'pos') return null\n ref = posPaymentRef(intent.body.metadata)\n } else if (\n type === 'payment_intent.amount_capturable_updated' ||\n type === 'payment_intent.succeeded' ||\n type === 'payment_intent.payment_failed' ||\n type === 'payment_intent.canceled'\n ) {\n if (object?.metadata?.kind !== 'pos') return null\n ref = posPaymentRef(object.metadata)\n } else if (\n (type === 'checkout.session.completed' || type === 'checkout.session.expired') &&\n object?.metadata?.type === 'pos-payment'\n ) {\n ref = posPaymentRef(object.metadata)\n } else {\n return null\n }\n if (!ref) return null\n // A reader can be re-registered by nobody else, but an event is only a\n // claim: the intent's own metadata must name a reader this site owns.\n await refreshPosCardPayment(ref).catch((error) => {\n console.error('[pos-terminal] webhook settle failed', ref, error)\n // Rethrown so the delivery is retried: a lost settlement is a sale the\n // register cannot complete without its poll.\n throw error\n })\n return { hostId: ref.hostId }\n}\n\nexport type { PosLiftedOrder }\n"],"names":["firebaseAdmin","merchantAccountIsReady","cardAuthenticationParams","checkoutSessionCardAuthenticationParams","CommerceModel","POS_CURRENCY","posCardApplicationFeeCents","posStripe","posStripeErrorMessage","posStripeTestMode","posTerminalAvailable","applyPosPayment","readPosSale","NOT_A_SALE","ok","status","error","withoutFailure","payment","next","failureMessage","posMerchantAccount","hostId","org","ownerUid","String","profile","app","firestore","collection","doc","get","accountId","chargesEnabled","accountLivemode","subject","terminalConfigRef","readerRef","readerId","isTerminalReaderId","value","test","ownedTerminalReader","snapshot","exists","data","id","posTerminalAddressProblem","address","line1","city","postalCode","country","ensurePosTerminalLocation","options","existing","created","ref","known","locationId","problem","idempotencyKey","params","display_name","displayName","slice","line2","undefined","state","orgId","body","set","stripeLocationId","livemode","Boolean","createdAtMs","Date","now","merge","posReaderTippingKey","settings","tippingEnabled","tipPercentages","join","syncPosReaderTipping","config","saved","key","percentages","configurationId","map","console","assigned","configuration_overrides","stripeConfigurationId","tippingKey","refreshPosReaderStatus","fetched","lastSeenAtMs","catch","action","reserveCardPayment","start","method","extra","orderId","paymentId","decide","order","payments","kind","total","Number","totals","totalCents","tenderable","posTenderableCents","amountCents","toFixed","tipProblem","posTipProblem","tipCents","takeFeeCents","posTakeShareCents","posTakeFeeCents","atMs","cashierId","patchCardPayment","patch","event","paymentIntentParams","destination","chargeCents","fee","takeShareCents","amount","currency","application_fee_amount","createCardPresentIntent","intent","reserved","payment_method_types","capture_method","description","current","paymentIntentId","recorded","client_secret","clientSecret","startCardPresentPayment","reader","sendToReader","skipTipping","attempt","posTerminalLocationId","createPosTerminalConnectionToken","token","location","secret","input","processed","payment_intent","retryCardPresentPayment","orderPayments","find","entry","completed","changed","rearmed","fresh","filter","startCardKeyedPayment","updated","startCardLinkPayment","session","checkoutSessionId","mode","success_url","returnUrl","cancel_url","url","checkoutUrl","cardDetailsOf","charge","latest_charge","details","payment_method_details","card_present","interac_present","card","brand","cardBrand","last4","posIntentTipCents","preset","Math","max","round","metadata","onReader","amount_details","tip","posCaptureFeeCents","refreshPosCardPayment","isCardPaymentMethod","captured","settledAtMs","feeCents","last_payment_error","message","type","process_payment_intent","failure_message","cancelPosCardPayment","settled","refreshed","cleared","code","simulatePosReaderTap","presented","cardNumber","posPaymentRef","handlePosStripeEvent","object","intentId"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-terminal.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { createHash } from 'node:crypto'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport { merchantAccountIsReady } from '@aglyn/tenant-data-admin/server/payment-provider'\nimport {\n cardAuthenticationParams,\n checkoutSessionCardAuthenticationParams,\n} from '@aglyn/tenant-data-admin/server/stripe-card-authentication'\nimport * as CommerceModel from '../model'\nimport type { PosRegisterSettings } from '../plugin-config'\nimport {\n POS_CURRENCY,\n posCardApplicationFeeCents,\n posStripe,\n posStripeErrorMessage,\n posStripeTestMode,\n posTerminalAvailable,\n} from './pos-stripe'\nimport { applyPosPayment, readPosSale, type PosLiftedOrder, type PosPaymentOutcome } from './pos-sale'\n\n/*==========================================\n * STRIPE TERMINAL AND THE REGISTER'S OTHER CARD TENDERS (AGL-3607).\n *\n * ## Where the readers live\n *\n * Every register charge is a DESTINATION charge on the platform account, so\n * Stripe's rule for that model applies: Locations and Readers are created on\n * the PLATFORM with the platform key, and identify their merchant by\n * metadata. One Location per site (`hosts/{hostId}/terminal/config`), and one\n * `hosts/{hostId}/terminalReaders/{readerId}` document per reader, written\n * only here. The document is what makes a reader THIS site's: every call that\n * names a reader reads it under the caller's own site first, so one store can\n * never drive, rename or remove another's reader, and the reader's own\n * `metadata.hostId` is checked again before a payment is sent to it.\n *\n * ## Why the reader charges with MANUAL capture\n *\n * On-reader tipping changes the amount after the PaymentIntent exists: the\n * reader adds the tip on confirmation and reports it in\n * `amount_details.tip.amount`. `application_fee_amount` set at creation was\n * sized on the pre-tip amount, and the platform's take must exclude the tip\n * while Stripe's processing cost (debited from the platform) is on the whole\n * charge. So the reader only AUTHORIZES; {@link refreshPosCardPayment} reads\n * the tip, re-prices the fee as\n *\n * take share (of the sale, never of the tip) + card processing (whole charge)\n *\n * and captures with that exact `application_fee_amount`. Stripe's own Connect\n * guidance for Terminal is the same: inspect, fix the application fee, then\n * capture. An authorization expires after two days, and a register capture\n * happens seconds after the tap.\n *=========================================*/\n\nconst NOT_A_SALE = { ok: false as const, status: 409, error: 'This sale is no longer open' }\n\n/** `hosts/{hostId}/terminalReaders/{readerId}`. */\nexport interface PosTerminalReader {\n label: string\n registerId?: string\n stripeLocationId: string\n deviceType?: string\n serialNumber?: string\n status?: 'online' | 'offline' | string\n livemode: boolean\n createdAtMs: number\n createdBy?: string\n lastSeenAtMs?: number\n}\n\n/**\n * The payment without its failure reason. Removed, never set to `undefined`:\n * Firestore refuses an undefined field inside the `payments` array.\n */\nfunction withoutFailure(payment: CommerceModel.OrderPayment): CommerceModel.OrderPayment {\n const next = { ...payment }\n delete next.failureMessage\n return next\n}\n\n/** The merchant's connected account, when it can take card payments now. */\nexport async function posMerchantAccount(\n hostId: string,\n org: Record<string, any> | null,\n): Promise<string | null> {\n const ownerUid = String(org?.['ownerUid'] ?? '')\n if (!ownerUid) return null\n const profile = await firebaseAdmin.app().firestore().collection('profiles').doc(ownerUid).get()\n const accountId = profile.get('stripeAccountId')\n if (\n !merchantAccountIsReady(\n {\n accountId,\n chargesEnabled: profile.get('stripeChargesEnabled'),\n accountLivemode: profile.get('stripeAccountLivemode'),\n },\n { subject: `POS card payment host ${hostId}` },\n )\n ) {\n return null\n }\n return String(accountId)\n}\n\nfunction terminalConfigRef(hostId: string) {\n return firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .collection('terminal')\n .doc('config')\n}\n\nfunction readerRef(hostId: string, readerId: string) {\n return firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .collection('terminalReaders')\n .doc(readerId)\n}\n\n/** A reader id as Stripe mints them; anything else never reaches Stripe. */\nexport function isTerminalReaderId(value: unknown): value is string {\n return typeof value === 'string' && /^tmr_[A-Za-z0-9]{6,64}$/.test(value)\n}\n\n/** The reader, when it is THIS site's; null for anyone else's or none. */\nexport async function ownedTerminalReader(\n hostId: string,\n readerId: unknown,\n): Promise<(PosTerminalReader & { id: string }) | null> {\n if (!isTerminalReaderId(readerId)) return null\n const snapshot = await readerRef(hostId, readerId).get()\n if (!snapshot.exists) return null\n return { ...(snapshot.data() as PosTerminalReader), id: readerId }\n}\n\n/** A Terminal Location's address, as the console sends it. */\nexport interface PosTerminalAddress {\n line1: string\n line2?: string\n city: string\n state?: string\n postalCode: string\n country: string\n}\n\nexport function posTerminalAddressProblem(address: Partial<PosTerminalAddress> | undefined): string | null {\n if (!address?.line1 || !address?.city || !address?.postalCode || !address?.country) {\n return 'Enter the street, city, postal code and country where the reader is used.'\n }\n if (!/^[A-Z]{2}$/.test(String(address.country))) return 'Choose a two-letter country code, like US.'\n return null\n}\n\n/**\n * The idempotency key of a site's Location request. Two presses with the same\n * address share it, so a double tap makes one Location; a corrected address\n * gets its own, since Stripe refuses a key reused with other parameters for a\n * day (a merchant fixing a typo would otherwise read that refusal).\n */\nexport function posTerminalLocationKey(hostId: string, displayName: string, address: PosTerminalAddress): string {\n const fingerprint = createHash('sha256')\n .update(\n JSON.stringify([\n displayName.slice(0, 100),\n address.line1,\n address.line2 ?? '',\n address.city,\n address.state ?? '',\n address.postalCode,\n address.country,\n ]),\n )\n .digest('hex')\n .slice(0, 16)\n return `pos-location:${hostId}:${fingerprint}`\n}\n\n/**\n * The site's Terminal Location, created on first use (on the PLATFORM, with\n * the merchant named in metadata). Its address is the store's, because Stripe\n * uses it to configure the reader for the right country.\n */\nexport async function ensurePosTerminalLocation(options: {\n hostId: string\n orgId: string\n displayName: string\n address?: PosTerminalAddress\n}): Promise<{ ok: true; locationId: string } | { ok: false; status: number; error: string }> {\n const ref = terminalConfigRef(options.hostId)\n const existing = await ref.get()\n const known = String(existing.get('stripeLocationId') ?? '')\n if (known) return { ok: true, locationId: known }\n const problem = posTerminalAddressProblem(options.address)\n if (problem) return { ok: false, status: 400, error: problem }\n const address = options.address as PosTerminalAddress\n const created = await posStripe('POST', 'terminal/locations', {\n idempotencyKey: posTerminalLocationKey(options.hostId, options.displayName, address),\n params: {\n display_name: options.displayName.slice(0, 100) || 'Store',\n 'address[line1]': address.line1,\n 'address[line2]': address.line2 || undefined,\n 'address[city]': address.city,\n 'address[state]': address.state || undefined,\n 'address[postal_code]': address.postalCode,\n 'address[country]': address.country,\n 'metadata[orgId]': options.orgId,\n 'metadata[hostId]': options.hostId,\n },\n })\n if (!created.ok || !created.body?.id) {\n return {\n ok: false,\n status: 502,\n error: posStripeErrorMessage(created.body, 'The reader location could not be set up.'),\n }\n }\n await ref.set(\n {\n stripeLocationId: String(created.body.id),\n livemode: Boolean(created.body.livemode),\n createdAtMs: Date.now(),\n },\n { merge: true },\n )\n return { ok: true, locationId: String(created.body.id) }\n}\n\n/** The tipping a reader shows: Stripe offers three buttons, so three presets. */\nexport function posReaderTippingKey(settings: PosRegisterSettings): string {\n return settings.tippingEnabled ? settings.tipPercentages.slice(0, 3).join(',') : 'off'\n}\n\n/**\n * Keeps the site's reader tipping in step with the merchant's settings: one\n * Terminal Configuration per site, assigned to its Location, rewritten only\n * when the presets changed. Never fatal: a reader that keeps the previous\n * presets still takes the payment.\n */\nexport async function syncPosReaderTipping(\n hostId: string,\n settings: PosRegisterSettings,\n): Promise<void> {\n if (!settings.tippingEnabled) return\n const ref = terminalConfigRef(hostId)\n const config = await ref.get()\n const locationId = String(config.get('stripeLocationId') ?? '')\n if (!locationId) return\n const key = posReaderTippingKey(settings)\n if (config.get('tippingKey') === key) return\n const percentages = settings.tipPercentages.slice(0, 3)\n const configurationId = String(config.get('stripeConfigurationId') ?? '')\n const params = {\n [`tipping[${POS_CURRENCY}][percentages]`]: percentages.map(String),\n }\n const saved = await posStripe(\n 'POST',\n configurationId ? `terminal/configurations/${configurationId}` : 'terminal/configurations',\n {\n params,\n ...(configurationId ? {} : { idempotencyKey: `pos-configuration:${hostId}:${key}` }),\n },\n )\n if (!saved.ok || !saved.body?.id) {\n console.error('[pos-terminal] tipping configuration failed', hostId, saved.body?.error)\n return\n }\n if (!configurationId) {\n const assigned = await posStripe('POST', `terminal/locations/${locationId}`, {\n params: { configuration_overrides: String(saved.body.id) },\n })\n if (!assigned.ok) {\n console.error('[pos-terminal] configuration assignment failed', hostId, assigned.body?.error)\n return\n }\n }\n await ref.set(\n { stripeConfigurationId: String(saved.body.id), tippingKey: key },\n { merge: true },\n )\n}\n\n/** Reads a reader from Stripe and keeps its status on the site's document. */\nexport async function refreshPosReaderStatus(\n hostId: string,\n readerId: string,\n): Promise<{ status: string; action: any } | null> {\n const fetched = await posStripe('GET', `terminal/readers/${readerId}`)\n if (!fetched.ok) return null\n const status = String(fetched.body?.status ?? 'offline')\n await readerRef(hostId, readerId)\n .set({ status, lastSeenAtMs: Date.now() }, { merge: true })\n .catch(() => undefined)\n return { status, action: fetched.body?.action ?? null }\n}\n\n/*==========================================\n * TAKING A CARD PAYMENT.\n *=========================================*/\n\ninterface CardStart {\n hostId: string\n orderId: string\n paymentId: string\n amountCents: number\n /** A tip already chosen (on the customer display or by the cashier). */\n tipCents: number\n cashierId: string\n org: Record<string, any> | null\n}\n\n/** Reserves a pending card payment in the ledger, or finds the one this attempt made. */\nasync function reserveCardPayment(\n start: CardStart,\n method: CommerceModel.OrderPaymentMethod,\n extra: Partial<CommerceModel.OrderPayment>,\n): Promise<PosPaymentOutcome> {\n return await applyPosPayment({\n hostId: start.hostId,\n orderId: start.orderId,\n paymentId: start.paymentId,\n decide: ({ order, payments, existing }) => {\n if (existing) {\n // The same attempt again: hand back what it started. A failed card\n // is re-armed by `retry`, not by pressing the tender a second time.\n return { kind: 'keep' }\n }\n if (order.status !== 'pending') return { kind: 'refuse', ...NOT_A_SALE }\n const total = Number(order.totals?.totalCents ?? 0)\n const tenderable = CommerceModel.posTenderableCents(total, payments)\n if (!(start.amountCents > 0)) {\n return { kind: 'refuse', status: 400, error: 'Enter an amount to charge.' }\n }\n if (start.amountCents > tenderable) {\n return {\n kind: 'refuse',\n status: 409,\n error:\n tenderable > 0\n ? `Only $${(tenderable / 100).toFixed(2)} is left to pay on this sale.`\n : 'Nothing is left to pay on this sale.',\n }\n }\n const tipProblem = CommerceModel.posTipProblem(start.tipCents, start.amountCents)\n if (tipProblem) return { kind: 'refuse', status: 400, error: tipProblem }\n const takeFeeCents = CommerceModel.posTakeShareCents({\n takeFeeCents: Number(order.posTakeFeeCents ?? 0),\n totalCents: total,\n amountCents: start.amountCents,\n })\n return {\n kind: 'put',\n payment: {\n id: start.paymentId,\n method,\n amountCents: start.amountCents,\n ...(start.tipCents > 0 ? { tipCents: start.tipCents } : {}),\n status: 'pending',\n atMs: Date.now(),\n takeFeeCents,\n cashierId: start.cashierId,\n ...extra,\n },\n }\n },\n })\n}\n\n/** Writes Stripe's answer onto a pending card payment. */\nasync function patchCardPayment(\n hostId: string,\n orderId: string,\n paymentId: string,\n patch: (payment: CommerceModel.OrderPayment) => CommerceModel.OrderPayment | null,\n event?: { event: string; detail?: string },\n): Promise<PosPaymentOutcome> {\n return await applyPosPayment({\n hostId,\n orderId,\n paymentId,\n decide: ({ existing }) => {\n if (!existing) return { kind: 'refuse', status: 404, error: 'Unknown payment' }\n const next = patch(existing)\n if (!next) return { kind: 'keep' }\n return { kind: 'put', payment: next, ...(event ? { event } : {}) }\n },\n })\n}\n\nfunction paymentIntentParams(\n start: CardStart,\n payment: CommerceModel.OrderPayment,\n destination: string,\n extra: Record<string, string | number | boolean | undefined | readonly string[]>,\n) {\n const chargeCents = start.amountCents + start.tipCents\n const fee = posCardApplicationFeeCents({\n takeShareCents: Number(payment.takeFeeCents ?? 0),\n chargeCents,\n })\n return {\n amount: chargeCents,\n currency: POS_CURRENCY,\n 'transfer_data[destination]': destination,\n ...(fee > 0 ? { application_fee_amount: fee } : {}),\n 'metadata[kind]': 'pos',\n 'metadata[hostId]': start.hostId,\n 'metadata[orderId]': start.orderId,\n 'metadata[paymentId]': start.paymentId,\n 'metadata[tipCents]': String(start.tipCents),\n ...extra,\n }\n}\n\n/**\n * Creates (or finds) the card-present PaymentIntent for one payment WITHOUT\n * choosing how the card is collected: reserves the amount in the ledger and\n * creates a manual-capture destination charge to the merchant (settled on\n * Aglyn's platform account, with no `on_behalf_of`, as ToS §10.7 states),\n * keyed by the payment so a retry returns the same intent.\n *\n * Two collectors use it. {@link startCardPresentPayment} pushes the intent\n * to a smart reader the site owns; a native companion app (AGL-3618)\n * collects it with the Stripe Terminal SDK (Tap to Pay, Bluetooth readers)\n * using the returned `clientSecret`. However the card is collected, the\n * payment is recorded by {@link refreshPosCardPayment}, keyed on the intent's\n * metadata, from the register's poll or the webhook alike.\n */\nexport async function createCardPresentIntent(\n start: CardStart & { readerId?: string },\n): Promise<PosPaymentOutcome & { clientSecret?: string; paymentIntentId?: string }> {\n // Live card-present payments wait for Terminal to be switched on for the\n // platform (`STRIPE_TERMINAL_LIVE_ENABLED`); the hidden controls are not\n // the only gate.\n if (!posTerminalAvailable()) {\n return { ok: false, status: 409, error: 'Card readers are not available for this store yet.' }\n }\n const destination = await posMerchantAccount(start.hostId, start.org)\n if (!destination) return { ok: false, status: 409, error: 'Card payments not set up' }\n const reserved = await reserveCardPayment(\n start,\n 'card_present',\n start.readerId ? { readerId: start.readerId } : {},\n )\n if (!reserved.ok || !reserved.payment) return reserved\n const payment = reserved.payment\n if (payment.status !== 'pending') return reserved\n // Keyed by the payment: a second call for the same payment replays the\n // intent Stripe already made rather than opening another.\n const intent = await posStripe('POST', 'payment_intents', {\n idempotencyKey: `pos-pi:${payment.id}`,\n params: paymentIntentParams(start, payment, destination, {\n payment_method_types:\n POS_CURRENCY === 'usd' ? ['card_present'] : ['card_present', 'interac_present'],\n capture_method: 'manual',\n ...(start.readerId ? { 'metadata[readerId]': start.readerId } : {}),\n description: 'In-store purchase',\n }),\n })\n if (!intent.ok || !intent.body?.id) {\n return await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n status: 'failed',\n failureMessage: posStripeErrorMessage(intent.body, 'The card payment could not be started.'),\n }))\n }\n const paymentIntentId = String(intent.body.id)\n const recorded = payment.paymentIntentId\n ? reserved\n : await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n paymentIntentId,\n livemode: Boolean(intent.body.livemode),\n }))\n return {\n ...recorded,\n paymentIntentId,\n ...(intent.body.client_secret ? { clientSecret: String(intent.body.client_secret) } : {}),\n }\n}\n\n/**\n * A smart-reader payment: the card-present intent from\n * {@link createCardPresentIntent}, pushed to a reader THIS site owns. The\n * register then polls {@link refreshPosCardPayment} until the reader answers;\n * webhooks land on the same function.\n */\nexport async function startCardPresentPayment(\n start: CardStart & { readerId: string; settings: PosRegisterSettings },\n): Promise<PosPaymentOutcome> {\n const reader = await ownedTerminalReader(start.hostId, start.readerId)\n if (!reader) return { ok: false, status: 404, error: 'That card reader is not on this site.' }\n await syncPosReaderTipping(start.hostId, start.settings).catch((error) =>\n console.error('[pos-terminal] tipping sync failed', error),\n )\n const created = await createCardPresentIntent({ ...start, readerId: reader.id })\n if (!created.ok || !created.payment || !created.paymentIntentId) return created\n if (created.payment.status !== 'pending') return created\n return await sendToReader({\n hostId: start.hostId,\n orderId: start.orderId,\n paymentId: created.payment.id,\n readerId: reader.id,\n paymentIntentId: created.paymentIntentId,\n // A tip the customer already chose on the display is in the amount;\n // asking again on the reader would ask twice.\n skipTipping: !start.settings.tippingEnabled || start.tipCents > 0,\n attempt: 0,\n })\n}\n\n/** The site's Terminal Location id, or null before a reader was ever set up. */\nexport async function posTerminalLocationId(hostId: string): Promise<string | null> {\n const config = await terminalConfigRef(hostId).get()\n return String(config.get('stripeLocationId') ?? '') || null\n}\n\n/**\n * A Terminal SDK connection token scoped to the site's own Location, for a\n * native companion app's SDK (AGL-3618). Scoping it to the Location is what\n * keeps one store's app from discovering another store's readers: Stripe\n * only lets a location-scoped token connect to readers in that Location.\n * Null when the site has no Location yet, never an unscoped token.\n */\nexport async function createPosTerminalConnectionToken(\n hostId: string,\n): Promise<{ secret: string; locationId: string } | null> {\n const locationId = await posTerminalLocationId(hostId)\n if (!locationId) return null\n const token = await posStripe('POST', 'terminal/connection_tokens', {\n params: { location: locationId },\n })\n if (!token.ok || !token.body?.secret) return null\n return { secret: String(token.body.secret), locationId }\n}\n\nasync function sendToReader(input: {\n hostId: string\n orderId: string\n paymentId: string\n readerId: string\n paymentIntentId: string\n skipTipping: boolean\n attempt: number\n}): Promise<PosPaymentOutcome> {\n const processed = await posStripe(\n 'POST',\n `terminal/readers/${input.readerId}/process_payment_intent`,\n {\n idempotencyKey: `pos-process:${input.paymentId}:${input.attempt}`,\n params: {\n payment_intent: input.paymentIntentId,\n 'process_config[enable_customer_cancellation]': true,\n ...(input.skipTipping ? { 'process_config[skip_tipping]': true } : {}),\n },\n },\n )\n if (!processed.ok) {\n return await patchCardPayment(input.hostId, input.orderId, input.paymentId, (current) => ({\n ...current,\n status: 'failed',\n failureMessage: posStripeErrorMessage(processed.body, 'The card reader could not start the payment.'),\n }))\n }\n return await patchCardPayment(input.hostId, input.orderId, input.paymentId, (current) => {\n if (!current.failureMessage) return null\n return withoutFailure(current)\n })\n}\n\n/**\n * Re-arms a declined or failed reader payment on the SAME PaymentIntent, as\n * Stripe asks (a new one per attempt risks a double charge).\n */\nexport async function retryCardPresentPayment(input: {\n hostId: string\n orderId: string\n paymentId: string\n settings: PosRegisterSettings\n}): Promise<PosPaymentOutcome> {\n const order = await readPosSale(input.hostId, input.orderId)\n if (!order || order.status !== 'pending') return NOT_A_SALE\n const payment = CommerceModel.orderPayments(order).find((entry) => entry.id === input.paymentId)\n if (!payment || payment.method !== 'card_present' || !payment.paymentIntentId || !payment.readerId) {\n return { ok: false, status: 404, error: 'Unknown payment' }\n }\n if (payment.status === 'succeeded') return { ok: true, order, payment, completed: false, changed: false }\n const reader = await ownedTerminalReader(input.hostId, payment.readerId)\n if (!reader) return { ok: false, status: 404, error: 'That card reader is not on this site.' }\n // Back to pending first, so the amount is reserved again before the reader\n // is asked; a sale paid meanwhile by another tender refuses here.\n const rearmed = await applyPosPayment({\n hostId: input.hostId,\n orderId: input.orderId,\n paymentId: input.paymentId,\n decide: ({ order: fresh, payments, existing }) => {\n if (!existing) return { kind: 'refuse', status: 404, error: 'Unknown payment' }\n if (existing.status === 'pending') return { kind: 'keep' }\n if (existing.status !== 'failed' && existing.status !== 'canceled') {\n return { kind: 'refuse', status: 409, error: 'This payment cannot be retried.' }\n }\n const tenderable = CommerceModel.posTenderableCents(\n Number(fresh.totals?.totalCents ?? 0),\n payments.filter((entry) => entry.id !== existing.id),\n )\n if (existing.amountCents > tenderable) {\n return { kind: 'refuse', status: 409, error: 'The balance changed. Start a new card payment.' }\n }\n return { kind: 'put', payment: { ...withoutFailure(existing), status: 'pending' } }\n },\n })\n if (!rearmed.ok) return rearmed\n const attempt = Date.now()\n return await sendToReader({\n hostId: input.hostId,\n orderId: input.orderId,\n paymentId: input.paymentId,\n readerId: reader.id,\n paymentIntentId: payment.paymentIntentId,\n skipTipping: !input.settings.tippingEnabled || Number(payment.tipCents ?? 0) > 0,\n attempt,\n })\n}\n\n/**\n * A typed card (card not present): a PaymentIntent the console confirms with\n * the Payment Element. No MOTO: the card is keyed into Stripe's own field and\n * goes through Stripe's normal online checks, 3-D Secure included.\n */\nexport async function startCardKeyedPayment(\n start: CardStart & { paymentMethodId?: string },\n): Promise<PosPaymentOutcome & { clientSecret?: string }> {\n const paymentMethodId = start.paymentMethodId ?? ''\n if (paymentMethodId && !isPaymentMethodId(paymentMethodId)) {\n return { ok: false, status: 400, error: 'The card could not be read. Enter it again.' }\n }\n const destination = await posMerchantAccount(start.hostId, start.org)\n if (!destination) return { ok: false, status: 409, error: 'Card payments not set up' }\n const reserved = await reserveCardPayment(start, 'card_keyed', {})\n if (!reserved.ok || !reserved.payment) return reserved\n const payment = reserved.payment\n if (payment.status !== 'pending') return reserved\n const intent = await posStripe('POST', 'payment_intents', {\n idempotencyKey: `pos-pi:${payment.id}`,\n params: paymentIntentParams(start, payment, destination, {\n payment_method_types: ['card'],\n description: 'In-store purchase (typed card)',\n // 3-D Secure from the one seam every card payment shares (AGL-3360).\n ...cardAuthenticationParams('one-time'),\n }),\n })\n if (!intent.ok || !intent.body?.id || !intent.body?.client_secret) {\n return await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n status: 'failed',\n failureMessage: posStripeErrorMessage(intent.body, 'The card payment could not be started.'),\n }))\n }\n const updated = payment.paymentIntentId\n ? reserved\n : await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n paymentIntentId: String(intent.body.id),\n livemode: Boolean(intent.body.livemode),\n }))\n if (!paymentMethodId) return { ...updated, clientSecret: String(intent.body.client_secret) }\n return await confirmKeyedCard(start, payment.id, String(intent.body.id), paymentMethodId)\n}\n\n/** A PaymentMethod id as Stripe mints them; anything else never reaches Stripe. */\nfunction isPaymentMethodId(value: string): boolean {\n return /^pm_[A-Za-z0-9]{8,64}$/.test(value)\n}\n\n/** What a typed card that needs the cardholder's bank to approve it says. */\nexport const KEYED_CARD_NEEDS_AUTHENTICATION =\n 'This card needs the cardholder to approve the payment with their bank, which a typed card cannot do. Use Card (QR) so they can approve it on their phone.'\n\n/**\n * A card typed into the register's own card fields (AGL-3848): the device\n * sent the number to Stripe alone, which answered with a PaymentMethod, and\n * hands this route only its id. The server confirms the intent with it, with\n * the platform's secret key, and records the payment from Stripe's answer as\n * every card payment is. A card whose bank asks for the cardholder's approval\n * (3-D Secure) cannot be approved at the register, so that intent is released\n * and the payment fails with words that point at Card (QR).\n */\nasync function confirmKeyedCard(\n start: CardStart,\n paymentId: string,\n intentId: string,\n paymentMethodId: string,\n): Promise<PosPaymentOutcome> {\n const input = { hostId: start.hostId, orderId: start.orderId, paymentId }\n const confirmed = await posStripe('POST', `payment_intents/${intentId}/confirm`, {\n idempotencyKey: `pos-keyed-confirm:${intentId}:${paymentMethodId}`,\n params: { payment_method: paymentMethodId },\n })\n const status = String(confirmed.body?.status ?? confirmed.body?.error?.payment_intent?.status ?? '')\n if (status === 'requires_action') {\n await posStripe('POST', `payment_intents/${intentId}/cancel`, {\n idempotencyKey: `pos-cancel:${intentId}`,\n })\n return await patchCardPayment(start.hostId, start.orderId, paymentId, (current) =>\n current.status === 'pending'\n ? { ...current, status: 'failed', failureMessage: KEYED_CARD_NEEDS_AUTHENTICATION }\n : null,\n )\n }\n if (!confirmed.ok && confirmed.body?.error && !confirmed.body.error.payment_intent) {\n // Refused before the card was tried (a stale method, a bad request): say so, leave nothing pending.\n const message = posStripeErrorMessage(confirmed.body, 'The card was not accepted.')\n await posStripe('POST', `payment_intents/${intentId}/cancel`, {\n idempotencyKey: `pos-cancel:${intentId}`,\n })\n return await patchCardPayment(start.hostId, start.orderId, paymentId, (current) =>\n current.status === 'pending' ? { ...current, status: 'failed', failureMessage: message } : null,\n )\n }\n // Approved, declined or still processing: Stripe's own read of the intent decides.\n return await refreshPosCardPayment(input)\n}\n\n/**\n * The customer pays on their own phone from a QR code: a Stripe payment page\n * for this payment's amount (and tip, as its own line). Settled by\n * `checkout.session.completed` or by the register's poll.\n */\nexport async function startCardLinkPayment(\n start: CardStart & { returnUrl: string },\n): Promise<PosPaymentOutcome> {\n const destination = await posMerchantAccount(start.hostId, start.org)\n if (!destination) return { ok: false, status: 409, error: 'Card payments not set up' }\n const reserved = await reserveCardPayment(start, 'card_link', {})\n if (!reserved.ok || !reserved.payment) return reserved\n const payment = reserved.payment\n if (payment.checkoutSessionId || payment.status !== 'pending') return reserved\n const fee = posCardApplicationFeeCents({\n takeShareCents: Number(payment.takeFeeCents ?? 0),\n chargeCents: start.amountCents + start.tipCents,\n })\n const session = await posStripe('POST', 'checkout/sessions', {\n idempotencyKey: `pos-link:${payment.id}`,\n params: {\n mode: 'payment',\n ...checkoutSessionCardAuthenticationParams('payment'),\n 'payment_method_types': ['card'],\n 'line_items[0][quantity]': 1,\n 'line_items[0][price_data][currency]': POS_CURRENCY,\n 'line_items[0][price_data][unit_amount]': start.amountCents,\n 'line_items[0][price_data][product_data][name]': 'In-store purchase',\n ...(start.tipCents > 0\n ? {\n 'line_items[1][quantity]': 1,\n 'line_items[1][price_data][currency]': POS_CURRENCY,\n 'line_items[1][price_data][unit_amount]': start.tipCents,\n 'line_items[1][price_data][product_data][name]': 'Tip',\n }\n : {}),\n 'payment_intent_data[transfer_data][destination]': destination,\n ...(fee > 0 ? { 'payment_intent_data[application_fee_amount]': fee } : {}),\n 'payment_intent_data[metadata][kind]': 'pos',\n 'payment_intent_data[metadata][hostId]': start.hostId,\n 'payment_intent_data[metadata][orderId]': start.orderId,\n 'payment_intent_data[metadata][paymentId]': payment.id,\n 'payment_intent_data[metadata][tipCents]': String(start.tipCents),\n success_url: `${start.returnUrl}?paid=1`,\n cancel_url: `${start.returnUrl}?paid=0`,\n 'metadata[type]': 'pos-payment',\n 'metadata[hostId]': start.hostId,\n 'metadata[orderId]': start.orderId,\n 'metadata[paymentId]': payment.id,\n },\n })\n if (!session.ok || !session.body?.url) {\n return await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n status: 'failed',\n failureMessage: posStripeErrorMessage(session.body, 'The payment page could not be created.'),\n }))\n }\n return await patchCardPayment(start.hostId, start.orderId, payment.id, (current) => ({\n ...current,\n checkoutSessionId: String(session.body.id),\n checkoutUrl: String(session.body.url),\n livemode: Boolean(session.body.livemode),\n }))\n}\n\n/*==========================================\n * SETTLING A CARD PAYMENT.\n *=========================================*/\n\n/** The card's brand and last four, off the charge Stripe made. */\nfunction cardDetailsOf(intent: any): { cardBrand?: string; last4?: string } {\n const charge = typeof intent?.latest_charge === 'object' ? intent.latest_charge : null\n const details =\n charge?.payment_method_details?.card_present ??\n charge?.payment_method_details?.interac_present ??\n charge?.payment_method_details?.card ??\n null\n return {\n ...(details?.brand ? { cardBrand: String(details.brand) } : {}),\n ...(details?.last4 ? { last4: String(details.last4) } : {}),\n }\n}\n\n/** The tip on an intent: what the display chose plus what the reader added. */\nexport function posIntentTipCents(intent: any): number {\n const preset = Math.max(0, Math.round(Number(intent?.metadata?.tipCents ?? 0)) || 0)\n const onReader = Math.max(0, Math.round(Number(intent?.amount_details?.tip?.amount ?? 0)) || 0)\n return preset + onReader\n}\n\n/**\n * The fee a register card charge is captured with: the payment's share of the\n * take (fixed at reservation, the sale's share and never the tip's) plus the\n * card processing cost on the whole amount, tip included.\n */\nexport function posCaptureFeeCents(payment: CommerceModel.OrderPayment, intent: any): number {\n return posCardApplicationFeeCents({\n takeShareCents: Number(payment.takeFeeCents ?? 0),\n chargeCents: Math.max(0, Math.round(Number(intent?.amount ?? 0))),\n })\n}\n\n/**\n * Brings one card payment up to date with Stripe, from a webhook or the\n * register's poll alike: captures an authorized reader payment with its final\n * fee, records a success, a decline or a cancellation. Idempotent: the\n * capture is keyed by the intent, and the ledger change is a no-op when the\n * payment already says what Stripe says.\n */\nexport async function refreshPosCardPayment(input: {\n hostId: string\n orderId: string\n paymentId: string\n}): Promise<PosPaymentOutcome> {\n const order = await readPosSale(input.hostId, input.orderId)\n if (!order) return { ok: false, status: 404, error: 'Unknown sale' }\n const payment = CommerceModel.orderPayments(order).find((entry) => entry.id === input.paymentId)\n if (!payment) return { ok: false, status: 404, error: 'Unknown payment' }\n if (!CommerceModel.isCardPaymentMethod(payment.method)) {\n return { ok: true, order, payment, completed: false, changed: false }\n }\n let paymentIntentId = payment.paymentIntentId ?? ''\n if (!paymentIntentId && payment.checkoutSessionId) {\n const session = await posStripe('GET', `checkout/sessions/${payment.checkoutSessionId}`)\n if (session.ok && session.body?.status === 'expired' && payment.status === 'pending') {\n return await patchCardPayment(input.hostId, input.orderId, payment.id, (current) =>\n current.status === 'pending' ? { ...current, status: 'canceled' } : null,\n )\n }\n paymentIntentId = String(session.body?.payment_intent ?? '')\n }\n if (!paymentIntentId) return { ok: true, order, payment, completed: false, changed: false }\n let intent = (\n await posStripe('GET', `payment_intents/${paymentIntentId}`, {\n params: { 'expand[]': 'latest_charge' },\n })\n ).body\n if (!intent?.id) return { ok: true, order, payment, completed: false, changed: false }\n if (intent.metadata?.hostId !== input.hostId || intent.metadata?.paymentId !== payment.id) {\n console.error('[pos-terminal] intent does not belong to this payment', paymentIntentId)\n return { ok: false, status: 409, error: 'This payment does not match its card charge.' }\n }\n if (intent.status === 'requires_capture') {\n if (order.status !== 'pending' && payment.status !== 'pending') {\n // Nothing to capture into: the sale is gone. Release the hold.\n await posStripe('POST', `payment_intents/${intent.id}/cancel`, {\n idempotencyKey: `pos-release:${intent.id}`,\n })\n return { ok: true, order, payment, completed: false, changed: false }\n }\n const fee = posCaptureFeeCents(payment, intent)\n const captured = await posStripe('POST', `payment_intents/${intent.id}/capture`, {\n idempotencyKey: `pos-capture:${intent.id}`,\n params: {\n ...(fee > 0 ? { application_fee_amount: fee } : {}),\n 'expand[]': 'latest_charge',\n },\n })\n if (captured.ok && captured.body?.id) intent = captured.body\n else {\n console.error('[pos-terminal] capture failed', intent.id, captured.body?.error)\n return { ok: true, order, payment, completed: false, changed: false }\n }\n }\n if (intent.status === 'succeeded') {\n const tipCents = posIntentTipCents(intent)\n const amountCents = Math.max(0, Math.round(Number(intent.amount ?? 0))) - tipCents\n return await patchCardPayment(\n input.hostId,\n input.orderId,\n payment.id,\n (current) => {\n if (current.status === 'succeeded' && current.paymentIntentId === intent.id) return null\n return {\n ...withoutFailure(current),\n status: 'succeeded',\n settledAtMs: Date.now(),\n paymentIntentId: String(intent.id),\n // What the charge actually put toward the sale. Equal to the\n // reserved amount; restated from Stripe so the ledger records the\n // money that moved rather than the money that was asked for.\n amountCents: amountCents > 0 ? amountCents : current.amountCents,\n ...(tipCents > 0 ? { tipCents } : {}),\n feeCents: Math.max(0, Math.round(Number(intent.application_fee_amount ?? 0))),\n livemode: Boolean(intent.livemode),\n ...cardDetailsOf(intent),\n }\n },\n )\n }\n if (intent.status === 'canceled') {\n return await patchCardPayment(input.hostId, input.orderId, payment.id, (current) =>\n current.status === 'pending' || current.status === 'failed'\n ? { ...current, status: 'canceled' }\n : null,\n )\n }\n if (intent.status === 'requires_payment_method' && intent.last_payment_error) {\n const message = String(intent.last_payment_error?.message ?? 'The card was declined.')\n return await patchCardPayment(input.hostId, input.orderId, payment.id, (current) =>\n current.status === 'pending' ? { ...current, status: 'failed', failureMessage: message } : null,\n )\n }\n // Still waiting on the customer. A reader action that already failed (a\n // card removed, a cancel on the reader) says so on the reader, not the\n // intent, so ask the reader too.\n if (payment.method === 'card_present' && payment.readerId && payment.status === 'pending') {\n const reader = await refreshPosReaderStatus(input.hostId, payment.readerId)\n const action = reader?.action\n if (\n action?.type === 'process_payment_intent' &&\n action?.process_payment_intent?.payment_intent === intent.id &&\n action?.status === 'failed'\n ) {\n const message = String(action.failure_message ?? 'The reader could not take the card.')\n return await patchCardPayment(input.hostId, input.orderId, payment.id, (current) =>\n current.status === 'pending' ? { ...current, status: 'failed', failureMessage: message } : null,\n )\n }\n }\n const fresh = await readPosSale(input.hostId, input.orderId)\n return {\n ok: true,\n order: fresh ?? order,\n payment:\n CommerceModel.orderPayments(fresh ?? order).find((entry) => entry.id === payment.id) ?? payment,\n completed: false,\n changed: false,\n }\n}\n\n/**\n * Stops a card payment that has not been paid: clears the reader, cancels the\n * intent, expires the payment page. A charge that already went through is\n * left alone and recorded as paid, never cancelled under the customer.\n */\nexport async function cancelPosCardPayment(input: {\n hostId: string\n orderId: string\n paymentId: string\n}): Promise<PosPaymentOutcome> {\n const refreshed = await refreshPosCardPayment(input)\n if (!refreshed.ok || !refreshed.payment) return refreshed\n const payment = refreshed.payment\n if (payment.status === 'succeeded' || payment.status === 'canceled') return refreshed\n if (payment.method === 'card_present' && payment.readerId) {\n const cleared = await posStripe('POST', `terminal/readers/${payment.readerId}/cancel_action`)\n if (!cleared.ok && cleared.body?.error?.code === 'terminal_reader_busy') {\n return {\n ok: false,\n status: 409,\n error: 'The customer is mid-payment on the reader. Wait for it to finish.',\n }\n }\n }\n if (payment.paymentIntentId) {\n await posStripe('POST', `payment_intents/${payment.paymentIntentId}/cancel`, {\n idempotencyKey: `pos-cancel:${payment.paymentIntentId}`,\n })\n }\n if (payment.checkoutSessionId) {\n await posStripe('POST', `checkout/sessions/${payment.checkoutSessionId}/expire`, {\n idempotencyKey: `pos-expire:${payment.checkoutSessionId}`,\n })\n }\n // Re-read: a payment that completed in the gap is recorded as paid.\n const settled = await refreshPosCardPayment(input)\n if (settled.ok && settled.payment?.status === 'succeeded') return settled\n return await patchCardPayment(input.hostId, input.orderId, payment.id, (current) =>\n current.status === 'pending' || current.status === 'failed'\n ? { ...current, status: 'canceled' }\n : null,\n )\n}\n\n/**\n * TEST MODE ONLY: Stripe's simulated reader is \"tapped\" with a test card.\n * Refused outright on a live key, whatever the request says.\n */\nexport async function simulatePosReaderTap(input: {\n hostId: string\n orderId: string\n paymentId: string\n /** `4000000000000002` declines; anything else Stripe's default approves. */\n cardNumber?: string\n}): Promise<PosPaymentOutcome> {\n if (!posStripeTestMode()) return { ok: false, status: 403, error: 'Only in test mode' }\n const order = await readPosSale(input.hostId, input.orderId)\n if (!order) return { ok: false, status: 404, error: 'Unknown sale' }\n const payment = CommerceModel.orderPayments(order).find((entry) => entry.id === input.paymentId)\n if (!payment?.readerId || payment.method !== 'card_present') {\n return { ok: false, status: 404, error: 'Unknown payment' }\n }\n const reader = await ownedTerminalReader(input.hostId, payment.readerId)\n if (!reader) return { ok: false, status: 404, error: 'That card reader is not on this site.' }\n const presented = await posStripe(\n 'POST',\n `test_helpers/terminal/readers/${reader.id}/present_payment_method`,\n {\n params: {\n type: 'card_present',\n ...(input.cardNumber && /^[0-9]{12,19}$/.test(input.cardNumber)\n ? { 'card_present[number]': input.cardNumber }\n : {}),\n },\n },\n )\n if (!presented.ok) {\n return {\n ok: false,\n status: 502,\n error: posStripeErrorMessage(presented.body, 'The simulated tap failed.'),\n }\n }\n return await refreshPosCardPayment(input)\n}\n\n/*==========================================\n * WEBHOOKS (the small branch `billing-webhook.ts` calls).\n *=========================================*/\n\n/** Where a Stripe object says a register payment lives, or null. */\nfunction posPaymentRef(\n metadata: Record<string, unknown> | undefined,\n): { hostId: string; orderId: string; paymentId: string } | null {\n const hostId = String(metadata?.['hostId'] ?? '')\n const orderId = String(metadata?.['orderId'] ?? '')\n const paymentId = String(metadata?.['paymentId'] ?? '')\n if (!hostId || !orderId || !paymentId) return null\n return { hostId, orderId, paymentId }\n}\n\n/**\n * The register's Stripe events: a reader finished or failed an action, a\n * register PaymentIntent was authorized, paid, declined or cancelled, or a\n * register QR page was paid or expired. Each resolves to ONE payment and is\n * answered by re-reading Stripe through {@link refreshPosCardPayment}, never\n * by trusting the event's own copy (whose API version predates the tip\n * fields). Returns the site when the event was the register's, so the\n * dispatcher can count it as claimed; null for every other event.\n */\nexport async function handlePosStripeEvent(input: {\n type: string\n object: any\n}): Promise<{ hostId: string } | null> {\n const { type, object } = input\n let ref: { hostId: string; orderId: string; paymentId: string } | null = null\n if (type === 'terminal.reader.action_succeeded' || type === 'terminal.reader.action_failed') {\n const intentId = String(object?.action?.process_payment_intent?.payment_intent ?? '')\n if (object?.action?.type !== 'process_payment_intent' || !intentId) return null\n const intent = await posStripe('GET', `payment_intents/${intentId}`)\n if (!intent.ok || intent.body?.metadata?.kind !== 'pos') return null\n ref = posPaymentRef(intent.body.metadata)\n } else if (\n type === 'payment_intent.amount_capturable_updated' ||\n type === 'payment_intent.succeeded' ||\n type === 'payment_intent.payment_failed' ||\n type === 'payment_intent.canceled'\n ) {\n if (object?.metadata?.kind !== 'pos') return null\n ref = posPaymentRef(object.metadata)\n } else if (\n (type === 'checkout.session.completed' || type === 'checkout.session.expired') &&\n object?.metadata?.type === 'pos-payment'\n ) {\n ref = posPaymentRef(object.metadata)\n } else {\n return null\n }\n if (!ref) return null\n // A reader can be re-registered by nobody else, but an event is only a\n // claim: the intent's own metadata must name a reader this site owns.\n await refreshPosCardPayment(ref).catch((error) => {\n console.error('[pos-terminal] webhook settle failed', ref, error)\n // Rethrown so the delivery is retried: a lost settlement is a sale the\n // register cannot complete without its poll.\n throw error\n })\n return { hostId: ref.hostId }\n}\n\nexport type { PosLiftedOrder }\n"],"names":["createHash","firebaseAdmin","merchantAccountIsReady","cardAuthenticationParams","checkoutSessionCardAuthenticationParams","CommerceModel","POS_CURRENCY","posCardApplicationFeeCents","posStripe","posStripeErrorMessage","posStripeTestMode","posTerminalAvailable","applyPosPayment","readPosSale","NOT_A_SALE","ok","status","error","withoutFailure","payment","next","failureMessage","posMerchantAccount","hostId","org","ownerUid","String","profile","app","firestore","collection","doc","get","accountId","chargesEnabled","accountLivemode","subject","terminalConfigRef","readerRef","readerId","isTerminalReaderId","value","test","ownedTerminalReader","snapshot","exists","data","id","posTerminalAddressProblem","address","line1","city","postalCode","country","posTerminalLocationKey","displayName","fingerprint","update","JSON","stringify","slice","line2","state","digest","ensurePosTerminalLocation","options","existing","created","ref","known","locationId","problem","idempotencyKey","params","display_name","undefined","orgId","body","set","stripeLocationId","livemode","Boolean","createdAtMs","Date","now","merge","posReaderTippingKey","settings","tippingEnabled","tipPercentages","join","syncPosReaderTipping","config","saved","key","percentages","configurationId","map","console","assigned","configuration_overrides","stripeConfigurationId","tippingKey","refreshPosReaderStatus","fetched","lastSeenAtMs","catch","action","reserveCardPayment","start","method","extra","orderId","paymentId","decide","order","payments","kind","total","Number","totals","totalCents","tenderable","posTenderableCents","amountCents","toFixed","tipProblem","posTipProblem","tipCents","takeFeeCents","posTakeShareCents","posTakeFeeCents","atMs","cashierId","patchCardPayment","patch","event","paymentIntentParams","destination","chargeCents","fee","takeShareCents","amount","currency","application_fee_amount","createCardPresentIntent","intent","reserved","payment_method_types","capture_method","description","current","paymentIntentId","recorded","client_secret","clientSecret","startCardPresentPayment","reader","sendToReader","skipTipping","attempt","posTerminalLocationId","createPosTerminalConnectionToken","token","location","secret","input","processed","payment_intent","retryCardPresentPayment","orderPayments","find","entry","completed","changed","rearmed","fresh","filter","startCardKeyedPayment","paymentMethodId","isPaymentMethodId","updated","confirmKeyedCard","KEYED_CARD_NEEDS_AUTHENTICATION","intentId","confirmed","payment_method","message","refreshPosCardPayment","startCardLinkPayment","session","checkoutSessionId","mode","success_url","returnUrl","cancel_url","url","checkoutUrl","cardDetailsOf","charge","latest_charge","details","payment_method_details","card_present","interac_present","card","brand","cardBrand","last4","posIntentTipCents","preset","Math","max","round","metadata","onReader","amount_details","tip","posCaptureFeeCents","isCardPaymentMethod","captured","settledAtMs","feeCents","last_payment_error","type","process_payment_intent","failure_message","cancelPosCardPayment","settled","refreshed","cleared","code","simulatePosReaderTap","presented","cardNumber","posPaymentRef","handlePosStripeEvent","object"],"mappings":"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"}
@@ -150,6 +150,20 @@ const text = (value, max)=>String(value != null ? value : '').trim().slice(0, ma
150
150
  patch
151
151
  };
152
152
  }
153
+ /**
154
+ * A printer's URL is signed with the deployment's token secret. Where that is
155
+ * not configured nothing could ever poll, so a create or a regenerate is
156
+ * refused BEFORE it writes: a signing failure leaves no printer the
157
+ * manager cannot connect, and no version bump that strands a working one.
158
+ */ const NO_SIGNING = 'Receipt printers are not available on this deployment.';
159
+ function signedCredentials(hostId, printerId, printer) {
160
+ try {
161
+ return printerCredentials(hostId, printerId, printer);
162
+ } catch (error) {
163
+ console.error('[printers] the printer URL could not be signed', error);
164
+ return null;
165
+ }
166
+ }
153
167
  /** What the card shows a manager to paste into the printer's web configuration. */ export function printerCredentials(hostId, printerId, printer) {
154
168
  return {
155
169
  printerId,
@@ -360,8 +374,13 @@ export const printersHandler = async (req, res)=>{
360
374
  createdAtMs: Date.now(),
361
375
  createdBy: uid
362
376
  });
377
+ // Signed first: a deployment that cannot sign the URL adds no printer.
378
+ const credentials = signedCredentials(hostId, ref.id, printer);
379
+ if (!credentials) return res.status(503).json({
380
+ error: NO_SIGNING
381
+ });
363
382
  await ref.set(printer);
364
- return res.status(200).json(printerCredentials(hostId, ref.id, printer));
383
+ return res.status(200).json(credentials);
365
384
  }
366
385
  case 'update':
367
386
  {
@@ -419,7 +438,11 @@ export const printersHandler = async (req, res)=>{
419
438
  if (!loaded) return res.status(404).json({
420
439
  error: 'Unknown printer'
421
440
  });
422
- return res.status(200).json(printerCredentials(hostId, printerId, loaded.printer));
441
+ const credentials = signedCredentials(hostId, printerId, loaded.printer);
442
+ if (!credentials) return res.status(503).json({
443
+ error: NO_SIGNING
444
+ });
445
+ return res.status(200).json(credentials);
423
446
  }
424
447
  case 'regenerate':
425
448
  {
@@ -428,15 +451,20 @@ export const printersHandler = async (req, res)=>{
428
451
  error: 'Unknown printer'
429
452
  });
430
453
  const secretVersion = Math.max(1, Number(loaded.printer.secretVersion) || 1) + 1;
454
+ // Signed first: a failed signing must not bump the version and strand the printer's working URL.
455
+ const credentials = signedCredentials(hostId, printerId, _extends({}, loaded.printer, {
456
+ secretVersion
457
+ }));
458
+ if (!credentials) return res.status(503).json({
459
+ error: NO_SIGNING
460
+ });
431
461
  await loaded.ref.set({
432
462
  secretVersion,
433
463
  updatedAtMs: Date.now()
434
464
  }, {
435
465
  merge: true
436
466
  });
437
- return res.status(200).json(printerCredentials(hostId, printerId, _extends({}, loaded.printer, {
438
- secretVersion
439
- })));
467
+ return res.status(200).json(credentials);
440
468
  }
441
469
  case 'test':
442
470
  case 'drawer':