@aglyn/plugins-commerce 1.0.0-beta.230 → 1.0.0-beta.231
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -13
- package/src/index.d.ts +0 -1
- package/src/index.js +0 -1
- package/src/index.js.map +1 -1
- package/src/lib/barcode/barcode-decode.d.ts +71 -0
- package/src/lib/barcode/barcode-decode.js +498 -0
- package/src/lib/barcode/barcode-decode.js.map +1 -0
- package/src/lib/barcode/barcode-encode.d.ts +44 -0
- package/src/lib/barcode/barcode-encode.js +138 -0
- package/src/lib/barcode/barcode-encode.js.map +1 -0
- package/src/lib/barcode/barcode-scanner.component.d.ts +23 -0
- package/src/lib/barcode/barcode-scanner.component.js +260 -0
- package/src/lib/barcode/barcode-scanner.component.js.map +1 -0
- package/src/lib/barcode/scan-button.component.d.ts +10 -0
- package/src/lib/barcode/scan-button.component.js +62 -0
- package/src/lib/barcode/scan-button.component.js.map +1 -0
- package/src/lib/barcode/scanner-wedge.d.ts +35 -0
- package/src/lib/barcode/scanner-wedge.js +108 -0
- package/src/lib/barcode/scanner-wedge.js.map +1 -0
- package/src/lib/components/account.js +10 -1
- package/src/lib/components/account.js.map +1 -1
- package/src/lib/components/cart-extras.d.ts +45 -0
- package/src/lib/components/cart-extras.js +127 -0
- package/src/lib/components/cart-extras.js.map +1 -0
- package/src/lib/components/cart.js +82 -15
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/checkout-return-notice.d.ts +26 -0
- package/src/lib/components/checkout-return-notice.js +48 -0
- package/src/lib/components/checkout-return-notice.js.map +1 -0
- package/src/lib/components/commerce-console-page.js +124 -0
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/commerce-console-sections.d.ts +1 -1
- package/src/lib/components/commerce-console-sections.js +4 -0
- package/src/lib/components/commerce-console-sections.js.map +1 -1
- package/src/lib/components/console/bulk-fulfill.d.ts +44 -0
- package/src/lib/components/console/bulk-fulfill.js +84 -0
- package/src/lib/components/console/bulk-fulfill.js.map +1 -0
- package/src/lib/components/console/customer-notifications-card.component.d.ts +19 -0
- package/src/lib/components/console/customer-notifications-card.component.js +149 -0
- package/src/lib/components/console/customer-notifications-card.component.js.map +1 -0
- package/src/lib/components/console/host-orders-card.component.js +84 -5
- package/src/lib/components/console/host-orders-card.component.js.map +1 -1
- package/src/lib/components/console/locations-card.component.js +164 -57
- package/src/lib/components/console/locations-card.component.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +209 -61
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/order-fulfillment-panel.component.d.ts +48 -0
- package/src/lib/components/console/order-fulfillment-panel.component.js +542 -0
- package/src/lib/components/console/order-fulfillment-panel.component.js.map +1 -0
- package/src/lib/components/console/order-invoice.component.d.ts +33 -0
- package/src/lib/components/console/order-invoice.component.js +81 -0
- package/src/lib/components/console/order-invoice.component.js.map +1 -0
- package/src/lib/components/console/order-receipt-resend.component.d.ts +21 -0
- package/src/lib/components/console/order-receipt-resend.component.js +203 -0
- package/src/lib/components/console/order-receipt-resend.component.js.map +1 -0
- package/src/lib/components/console/order-returns.component.d.ts +36 -0
- package/src/lib/components/console/order-returns.component.js +142 -0
- package/src/lib/components/console/order-returns.component.js.map +1 -0
- package/src/lib/components/console/order-shipping-actions.component.d.ts +54 -0
- package/src/lib/components/console/order-shipping-actions.component.js +149 -0
- package/src/lib/components/console/order-shipping-actions.component.js.map +1 -0
- package/src/lib/components/console/order-webhooks-card.component.d.ts +50 -0
- package/src/lib/components/console/order-webhooks-card.component.js +623 -0
- package/src/lib/components/console/order-webhooks-card.component.js.map +1 -0
- package/src/lib/components/console/order-zones.d.ts +155 -0
- package/src/lib/components/console/order-zones.js +40 -0
- package/src/lib/components/console/order-zones.js.map +1 -0
- package/src/lib/components/console/payment-methods-card.component.d.ts +67 -0
- package/src/lib/components/console/payment-methods-card.component.js +395 -0
- package/src/lib/components/console/payment-methods-card.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-api.d.ts +124 -0
- package/src/lib/components/console/pos/pos-api.js +101 -0
- package/src/lib/components/console/pos/pos-api.js.map +1 -0
- package/src/lib/components/console/pos/pos-cart-panel.component.d.ts +36 -0
- package/src/lib/components/console/pos/pos-cart-panel.component.js +160 -0
- package/src/lib/components/console/pos/pos-cart-panel.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-item-dialog.component.d.ts +39 -0
- package/src/lib/components/console/pos/pos-item-dialog.component.js +352 -0
- package/src/lib/components/console/pos/pos-item-dialog.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-product-grid.component.d.ts +47 -0
- package/src/lib/components/console/pos/pos-product-grid.component.js +272 -0
- package/src/lib/components/console/pos/pos-product-grid.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-receipt-panel.component.d.ts +27 -0
- package/src/lib/components/console/pos/pos-receipt-panel.component.js +282 -0
- package/src/lib/components/console/pos/pos-receipt-panel.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-tender-dialogs.component.d.ts +53 -0
- package/src/lib/components/console/pos/pos-tender-dialogs.component.js +445 -0
- package/src/lib/components/console/pos/pos-tender-dialogs.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-tender-panel.component.d.ts +31 -0
- package/src/lib/components/console/pos/pos-tender-panel.component.js +640 -0
- package/src/lib/components/console/pos/pos-tender-panel.component.js.map +1 -0
- package/src/lib/components/console/pos/use-pos-display.d.ts +43 -0
- package/src/lib/components/console/pos/use-pos-display.js +161 -0
- package/src/lib/components/console/pos/use-pos-display.js.map +1 -0
- package/src/lib/components/console/pos-devices-card.component.d.ts +14 -0
- package/src/lib/components/console/pos-devices-card.component.js +546 -0
- package/src/lib/components/console/pos-devices-card.component.js.map +1 -0
- package/src/lib/components/console/pos-display/brand-screen.d.ts +33 -0
- package/src/lib/components/console/pos-display/brand-screen.js +85 -0
- package/src/lib/components/console/pos-display/brand-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/cart-screen.d.ts +29 -0
- package/src/lib/components/console/pos-display/cart-screen.js +166 -0
- package/src/lib/components/console/pos-display/cart-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/pairing-screen.d.ts +24 -0
- package/src/lib/components/console/pos-display/pairing-screen.js +127 -0
- package/src/lib/components/console/pos-display/pairing-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/pos-display-api.d.ts +90 -0
- package/src/lib/components/console/pos-display/pos-display-api.js +140 -0
- package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -0
- package/src/lib/components/console/pos-display/receipt-screen.d.ts +35 -0
- package/src/lib/components/console/pos-display/receipt-screen.js +200 -0
- package/src/lib/components/console/pos-display/receipt-screen.js.map +1 -0
- package/src/lib/components/console/pos-display/tip-screen.d.ts +36 -0
- package/src/lib/components/console/pos-display/tip-screen.js +211 -0
- package/src/lib/components/console/pos-display/tip-screen.js.map +1 -0
- package/src/lib/components/console/pos-display-page.component.d.ts +16 -0
- package/src/lib/components/console/pos-display-page.component.js +407 -0
- package/src/lib/components/console/pos-display-page.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.d.ts +40 -0
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js +289 -0
- package/src/lib/components/console/pos-ops/pos-customer-lookup.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-last-receipt.component.d.ts +34 -0
- package/src/lib/components/console/pos-ops/pos-last-receipt.component.js +61 -0
- package/src/lib/components/console/pos-ops/pos-last-receipt.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +38 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +129 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-ops-api.d.ts +37 -0
- package/src/lib/components/console/pos-ops/pos-ops-api.js +60 -0
- package/src/lib/components/console/pos-ops/pos-ops-api.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-pin-pad.component.d.ts +48 -0
- package/src/lib/components/console/pos-ops/pos-pin-pad.component.js +220 -0
- package/src/lib/components/console/pos-ops/pos-pin-pad.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-receipt-actions.component.d.ts +63 -0
- package/src/lib/components/console/pos-ops/pos-receipt-actions.component.js +186 -0
- package/src/lib/components/console/pos-ops/pos-receipt-actions.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-receipt.d.ts +39 -0
- package/src/lib/components/console/pos-ops/pos-receipt.js +182 -0
- package/src/lib/components/console/pos-ops/pos-receipt.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-return-dialog.component.d.ts +40 -0
- package/src/lib/components/console/pos-ops/pos-return-dialog.component.js +465 -0
- package/src/lib/components/console/pos-ops/pos-return-dialog.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-history-card.component.d.ts +29 -0
- package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js +253 -0
- package/src/lib/components/console/pos-ops/pos-shift-history-card.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.d.ts +34 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.js +494 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-report.component.d.ts +37 -0
- package/src/lib/components/console/pos-ops/pos-shift-report.component.js +102 -0
- package/src/lib/components/console/pos-ops/pos-shift-report.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.d.ts +30 -0
- package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.js +277 -0
- package/src/lib/components/console/pos-ops/pos-staff-pins-card.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/register-ops.d.ts +28 -0
- package/src/lib/components/console/pos-ops/register-ops.js +28 -0
- package/src/lib/components/console/pos-ops/register-ops.js.map +1 -0
- package/src/lib/components/console/pos-ops/use-pos-cashier.d.ts +46 -0
- package/src/lib/components/console/pos-ops/use-pos-cashier.js +128 -0
- package/src/lib/components/console/pos-ops/use-pos-cashier.js.map +1 -0
- package/src/lib/components/console/pos-ops/use-pos-sale-receipt.d.ts +36 -0
- package/src/lib/components/console/pos-ops/use-pos-sale-receipt.js +64 -0
- package/src/lib/components/console/pos-ops/use-pos-sale-receipt.js.map +1 -0
- package/src/lib/components/console/pos-page.component.d.ts +13 -5
- package/src/lib/components/console/pos-page.component.js +729 -657
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/product-editor-dialog.component.js +51 -7
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/product-labels-dialog.component.d.ts +13 -0
- package/src/lib/components/console/product-labels-dialog.component.js +197 -0
- package/src/lib/components/console/product-labels-dialog.component.js.map +1 -0
- package/src/lib/components/console/product-register-fields.component.d.ts +19 -0
- package/src/lib/components/console/product-register-fields.component.js +241 -0
- package/src/lib/components/console/product-register-fields.component.js.map +1 -0
- package/src/lib/components/console/product-zones.d.ts +44 -0
- package/src/lib/components/console/product-zones.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +12 -0
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/register-hardware-card.component.d.ts +33 -0
- package/src/lib/components/console/register-hardware-card.component.js +826 -0
- package/src/lib/components/console/register-hardware-card.component.js.map +1 -0
- package/src/lib/components/console/registers-card.component.js +24 -5
- package/src/lib/components/console/registers-card.component.js.map +1 -1
- package/src/lib/components/console/return-actions.d.ts +38 -0
- package/src/lib/components/console/return-actions.js +76 -0
- package/src/lib/components/console/return-actions.js.map +1 -0
- package/src/lib/components/console/return-detail-dialog.component.d.ts +34 -0
- package/src/lib/components/console/return-detail-dialog.component.js +718 -0
- package/src/lib/components/console/return-detail-dialog.component.js.map +1 -0
- package/src/lib/components/console/return-settings-card.component.d.ts +34 -0
- package/src/lib/components/console/return-settings-card.component.js +195 -0
- package/src/lib/components/console/return-settings-card.component.js.map +1 -0
- package/src/lib/components/console/return-zones.d.ts +87 -0
- package/src/lib/components/console/return-zones.js +31 -0
- package/src/lib/components/console/return-zones.js.map +1 -0
- package/src/lib/components/console/returns-card.component.d.ts +45 -0
- package/src/lib/components/console/returns-card.component.js +159 -0
- package/src/lib/components/console/returns-card.component.js.map +1 -0
- package/src/lib/components/console/shipping-settings-card.component.js +161 -5
- package/src/lib/components/console/shipping-settings-card.component.js.map +1 -1
- package/src/lib/components/console/shippingeasy-card.component.d.ts +59 -0
- package/src/lib/components/console/shippingeasy-card.component.js +378 -0
- package/src/lib/components/console/shippingeasy-card.component.js.map +1 -0
- package/src/lib/components/console/shipstation-card.component.d.ts +35 -0
- package/src/lib/components/console/shipstation-card.component.js +345 -0
- package/src/lib/components/console/shipstation-card.component.js.map +1 -0
- package/src/lib/components/console/start-return-dialog.component.d.ts +40 -0
- package/src/lib/components/console/start-return-dialog.component.js +243 -0
- package/src/lib/components/console/start-return-dialog.component.js.map +1 -0
- package/src/lib/components/console/stock-movements-card.component.js +2 -1
- package/src/lib/components/console/stock-movements-card.component.js.map +1 -1
- package/src/lib/components/console/store-settings-card.component.js +2 -66
- package/src/lib/components/console/store-settings-card.component.js.map +1 -1
- package/src/lib/components/console/store-zones.d.ts +42 -0
- package/src/lib/components/console/store-zones.js +39 -0
- package/src/lib/components/console/store-zones.js.map +1 -0
- package/src/lib/components/order-status.d.ts +36 -0
- package/src/lib/components/order-status.js +502 -0
- package/src/lib/components/order-status.js.map +1 -0
- package/src/lib/components/product-detail.js +41 -2
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/return-request.d.ts +32 -0
- package/src/lib/components/return-request.js +576 -0
- package/src/lib/components/return-request.js.map +1 -0
- package/src/lib/components/storefront-payment-appearance.d.ts +32 -0
- package/src/lib/components/storefront-payment-appearance.js +114 -0
- package/src/lib/components/storefront-payment-appearance.js.map +1 -0
- package/src/lib/components/storefront-payment-element.d.ts +57 -1
- package/src/lib/components/storefront-payment-element.js +332 -33
- package/src/lib/components/storefront-payment-element.js.map +1 -1
- package/src/lib/constants/order-status.d.ts +22 -0
- package/src/lib/constants/order-status.js +22 -0
- package/src/lib/constants/order-status.js.map +1 -0
- package/src/lib/constants/return-request.d.ts +27 -0
- package/src/lib/constants/return-request.js +32 -0
- package/src/lib/constants/return-request.js.map +1 -0
- package/src/lib/constants/stock-movement-query.js +2 -1
- package/src/lib/constants/stock-movement-query.js.map +1 -1
- package/src/lib/declarations.console-server.js +9 -0
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/declarations.server.d.ts +2 -1
- package/src/lib/declarations.server.js +47 -1
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/model/buyer-notifications.d.ts +59 -0
- package/src/lib/model/buyer-notifications.js +97 -0
- package/src/lib/model/buyer-notifications.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +127 -1
- package/src/lib/model/commerce-orders.js +24 -15
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-payment-methods.d.ts +146 -0
- package/src/lib/model/commerce-payment-methods.js +247 -0
- package/src/lib/model/commerce-payment-methods.js.map +1 -0
- package/src/lib/model/commerce-pos-ops.d.ts +437 -0
- package/src/lib/model/commerce-pos-ops.js +721 -0
- package/src/lib/model/commerce-pos-ops.js.map +1 -0
- package/src/lib/model/commerce-pos.d.ts +263 -0
- package/src/lib/model/commerce-pos.js +375 -0
- package/src/lib/model/commerce-pos.js.map +1 -0
- package/src/lib/model/commerce-printers.d.ts +211 -0
- package/src/lib/model/commerce-printers.js +181 -0
- package/src/lib/model/commerce-printers.js.map +1 -0
- package/src/lib/model/commerce-receipt.d.ts +97 -0
- package/src/lib/model/commerce-receipt.js +180 -0
- package/src/lib/model/commerce-receipt.js.map +1 -0
- package/src/lib/model/commerce-returns.d.ts +207 -0
- package/src/lib/model/commerce-returns.js +300 -0
- package/src/lib/model/commerce-returns.js.map +1 -0
- package/src/lib/model/commerce-shipping.d.ts +44 -1
- package/src/lib/model/commerce-shipping.js +53 -9
- package/src/lib/model/commerce-shipping.js.map +1 -1
- package/src/lib/model/commerce-tax-engine.d.ts +90 -0
- package/src/lib/model/commerce-tax-engine.js +129 -0
- package/src/lib/model/commerce-tax-engine.js.map +1 -0
- package/src/lib/model/commerce.d.ts +88 -2
- package/src/lib/model/commerce.js +89 -1
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/index.d.ts +15 -0
- package/src/lib/model/index.js +15 -0
- package/src/lib/model/index.js.map +1 -1
- package/src/lib/model/order-events.d.ts +154 -0
- package/src/lib/model/order-events.js +153 -0
- package/src/lib/model/order-events.js.map +1 -0
- package/src/lib/model/order-fulfillment.d.ts +117 -0
- package/src/lib/model/order-fulfillment.js +217 -0
- package/src/lib/model/order-fulfillment.js.map +1 -0
- package/src/lib/model/order-history.d.ts +58 -0
- package/src/lib/model/order-history.js +22 -0
- package/src/lib/model/order-history.js.map +1 -0
- package/src/lib/model/order-list-fields.d.ts +9 -1
- package/src/lib/model/order-list-fields.js +7 -1
- package/src/lib/model/order-list-fields.js.map +1 -1
- package/src/lib/model/order-shipping-export.d.ts +91 -0
- package/src/lib/model/order-shipping-export.js +192 -0
- package/src/lib/model/order-shipping-export.js.map +1 -0
- package/src/lib/model/order-status-view.d.ts +103 -0
- package/src/lib/model/order-status-view.js +156 -0
- package/src/lib/model/order-status-view.js.map +1 -0
- package/src/lib/model/order-webhooks.d.ts +142 -0
- package/src/lib/model/order-webhooks.js +91 -0
- package/src/lib/model/order-webhooks.js.map +1 -0
- package/src/lib/model/product-channel.d.ts +56 -0
- package/src/lib/model/product-channel.js +86 -0
- package/src/lib/model/product-channel.js.map +1 -0
- package/src/lib/model/product-modifiers.d.ts +80 -0
- package/src/lib/model/product-modifiers.js +140 -0
- package/src/lib/model/product-modifiers.js.map +1 -0
- package/src/lib/model/shippingeasy.d.ts +94 -0
- package/src/lib/model/shippingeasy.js +267 -0
- package/src/lib/model/shippingeasy.js.map +1 -0
- package/src/lib/model/shipstation.d.ts +125 -0
- package/src/lib/model/shipstation.js +399 -0
- package/src/lib/model/shipstation.js.map +1 -0
- package/src/lib/model/smart-collection-reads.d.ts +32 -0
- package/src/lib/model/smart-collection-reads.js +63 -0
- package/src/lib/model/smart-collection-reads.js.map +1 -0
- package/src/lib/model/tracking-url.d.ts +74 -0
- package/src/lib/model/tracking-url.js +159 -0
- package/src/lib/model/tracking-url.js.map +1 -0
- package/src/lib/plugin-config.d.ts +13 -0
- package/src/lib/plugin-config.js +77 -5
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +23 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/pos-ops-config.d.ts +39 -0
- package/src/lib/pos-ops-config.js +88 -0
- package/src/lib/pos-ops-config.js.map +1 -0
- package/src/lib/printing/print-document.d.ts +106 -0
- package/src/lib/printing/print-document.js +464 -0
- package/src/lib/printing/print-document.js.map +1 -0
- package/src/lib/printing/product-labels.d.ts +58 -0
- package/src/lib/printing/product-labels.js +116 -0
- package/src/lib/printing/product-labels.js.map +1 -0
- package/src/lib/printing/render-epson.d.ts +57 -0
- package/src/lib/printing/render-epson.js +135 -0
- package/src/lib/printing/render-epson.js.map +1 -0
- package/src/lib/printing/render-star.d.ts +45 -0
- package/src/lib/printing/render-star.js +100 -0
- package/src/lib/printing/render-star.js.map +1 -0
- package/src/lib/printing/render-text.d.ts +25 -0
- package/src/lib/printing/render-text.js +53 -0
- package/src/lib/printing/render-text.js.map +1 -0
- package/src/lib/server/api-v1/openapi.js +27 -4
- package/src/lib/server/api-v1/openapi.js.map +1 -1
- package/src/lib/server/api-v1/order-view.d.ts +67 -0
- package/src/lib/server/api-v1/order-view.js +120 -0
- package/src/lib/server/api-v1/order-view.js.map +1 -0
- package/src/lib/server/api-v1/orders-and-products.js +58 -65
- package/src/lib/server/api-v1/orders-and-products.js.map +1 -1
- package/src/lib/server/billing-webhook.js +135 -28
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cancel-order.js +29 -0
- package/src/lib/server/cancel-order.js.map +1 -1
- package/src/lib/server/carrier-rates-availability.d.ts +26 -0
- package/src/lib/server/carrier-rates-availability.js +76 -0
- package/src/lib/server/carrier-rates-availability.js.map +1 -0
- package/src/lib/server/carrier-shipping.d.ts +84 -0
- package/src/lib/server/carrier-shipping.js +135 -0
- package/src/lib/server/carrier-shipping.js.map +1 -0
- package/src/lib/server/cart-checkout.d.ts +2 -0
- package/src/lib/server/cart-checkout.js +226 -22
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/checkout-extras.d.ts +64 -0
- package/src/lib/server/checkout-extras.js +157 -0
- package/src/lib/server/checkout-extras.js.map +1 -0
- package/src/lib/server/{feed.d.ts → checkout-status.d.ts} +2 -7
- package/src/lib/server/checkout-status.js +111 -0
- package/src/lib/server/checkout-status.js.map +1 -0
- package/src/lib/server/checkout.js +86 -9
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/draft-order.js +34 -5
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/fulfill-order.d.ts +127 -10
- package/src/lib/server/fulfill-order.js +451 -49
- package/src/lib/server/fulfill-order.js.map +1 -1
- package/src/lib/server/legacy-feed.d.ts +28 -0
- package/src/lib/server/legacy-feed.js +52 -0
- package/src/lib/server/legacy-feed.js.map +1 -0
- package/src/lib/server/native-checkout.d.ts +34 -15
- package/src/lib/server/native-checkout.js +32 -17
- package/src/lib/server/native-checkout.js.map +1 -1
- package/src/lib/server/order-event-host-emit.d.ts +23 -0
- package/src/lib/server/order-event-host-emit.js +30 -0
- package/src/lib/server/order-event-host-emit.js.map +1 -0
- package/src/lib/server/order-event-triggers.d.ts +37 -0
- package/src/lib/server/order-event-triggers.js +119 -0
- package/src/lib/server/order-event-triggers.js.map +1 -0
- package/src/lib/server/order-events.d.ts +62 -0
- package/src/lib/server/order-events.js +109 -0
- package/src/lib/server/order-events.js.map +1 -0
- package/src/lib/server/order-history.d.ts +23 -0
- package/src/lib/server/order-history.js +65 -0
- package/src/lib/server/order-history.js.map +1 -0
- package/src/lib/server/order-notifications.d.ts +185 -0
- package/src/lib/server/order-notifications.js +567 -0
- package/src/lib/server/order-notifications.js.map +1 -0
- package/src/lib/server/order-receipt-send.d.ts +39 -0
- package/src/lib/server/order-receipt-send.js +151 -0
- package/src/lib/server/order-receipt-send.js.map +1 -0
- package/src/lib/server/order-status-token.d.ts +31 -0
- package/src/lib/server/order-status-token.js +75 -0
- package/src/lib/server/order-status-token.js.map +1 -0
- package/src/lib/server/order-status.d.ts +58 -0
- package/src/lib/server/order-status.js +141 -0
- package/src/lib/server/order-status.js.map +1 -0
- package/src/lib/server/order-webhooks.d.ts +108 -0
- package/src/lib/server/order-webhooks.js +622 -0
- package/src/lib/server/order-webhooks.js.map +1 -0
- package/src/lib/server/orders-shipping-prepare.d.ts +26 -0
- package/src/lib/server/orders-shipping-prepare.js +121 -0
- package/src/lib/server/orders-shipping-prepare.js.map +1 -0
- package/src/lib/server/payment-method-domains.d.ts +209 -0
- package/src/lib/server/payment-method-domains.js +444 -0
- package/src/lib/server/payment-method-domains.js.map +1 -0
- package/src/lib/server/payment-methods.d.ts +77 -0
- package/src/lib/server/payment-methods.js +284 -0
- package/src/lib/server/payment-methods.js.map +1 -0
- package/src/lib/server/pos-auth.d.ts +50 -0
- package/src/lib/server/pos-auth.js +121 -0
- package/src/lib/server/pos-auth.js.map +1 -0
- package/src/lib/server/pos-customer.d.ts +49 -0
- package/src/lib/server/pos-customer.js +257 -0
- package/src/lib/server/pos-customer.js.map +1 -0
- package/src/lib/server/pos-display.d.ts +57 -0
- package/src/lib/server/pos-display.js +419 -0
- package/src/lib/server/pos-display.js.map +1 -0
- package/src/lib/server/pos-fee-month.d.ts +34 -0
- package/src/lib/server/pos-fee-month.js +36 -0
- package/src/lib/server/pos-fee-month.js.map +1 -0
- package/src/lib/server/pos-ops-gate.d.ts +161 -0
- package/src/lib/server/pos-ops-gate.js +295 -0
- package/src/lib/server/pos-ops-gate.js.map +1 -0
- package/src/lib/server/pos-ops-routes.d.ts +22 -0
- package/src/lib/server/pos-ops-routes.js +36 -0
- package/src/lib/server/pos-ops-routes.js.map +1 -0
- package/src/lib/server/pos-order.d.ts +1 -18
- package/src/lib/server/pos-order.js +222 -42
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/pos-payment.d.ts +39 -0
- package/src/lib/server/pos-payment.js +922 -0
- package/src/lib/server/pos-payment.js.map +1 -0
- package/src/lib/server/pos-print.d.ts +82 -0
- package/src/lib/server/pos-print.js +193 -0
- package/src/lib/server/pos-print.js.map +1 -0
- package/src/lib/server/pos-readers.d.ts +27 -0
- package/src/lib/server/pos-readers.js +276 -0
- package/src/lib/server/pos-readers.js.map +1 -0
- package/src/lib/server/pos-refund-authority.d.ts +60 -0
- package/src/lib/server/pos-refund-authority.js +72 -0
- package/src/lib/server/pos-refund-authority.js.map +1 -0
- package/src/lib/server/pos-return.d.ts +58 -0
- package/src/lib/server/pos-return.js +891 -0
- package/src/lib/server/pos-return.js.map +1 -0
- package/src/lib/server/pos-sale-stamp.d.ts +66 -0
- package/src/lib/server/pos-sale-stamp.js +84 -0
- package/src/lib/server/pos-sale-stamp.js.map +1 -0
- package/src/lib/server/pos-sale.d.ts +170 -0
- package/src/lib/server/pos-sale.js +461 -0
- package/src/lib/server/pos-sale.js.map +1 -0
- package/src/lib/server/pos-shift.d.ts +45 -0
- package/src/lib/server/pos-shift.js +484 -0
- package/src/lib/server/pos-shift.js.map +1 -0
- package/src/lib/server/pos-staff-pin.d.ts +26 -0
- package/src/lib/server/pos-staff-pin.js +339 -0
- package/src/lib/server/pos-staff-pin.js.map +1 -0
- package/src/lib/server/pos-stripe.d.ts +91 -0
- package/src/lib/server/pos-stripe.js +129 -0
- package/src/lib/server/pos-stripe.js.map +1 -0
- package/src/lib/server/pos-terminal-connection-token.d.ts +48 -0
- package/src/lib/server/pos-terminal-connection-token.js +166 -0
- package/src/lib/server/pos-terminal-connection-token.js.map +1 -0
- package/src/lib/server/pos-terminal.d.ts +218 -0
- package/src/lib/server/pos-terminal.js +1014 -0
- package/src/lib/server/pos-terminal.js.map +1 -0
- package/src/lib/server/print-queue.d.ts +124 -0
- package/src/lib/server/print-queue.js +301 -0
- package/src/lib/server/print-queue.js.map +1 -0
- package/src/lib/server/printer-poll.d.ts +75 -0
- package/src/lib/server/printer-poll.js +325 -0
- package/src/lib/server/printer-poll.js.map +1 -0
- package/src/lib/server/printer-secret.d.ts +39 -0
- package/src/lib/server/printer-secret.js +56 -0
- package/src/lib/server/printer-secret.js.map +1 -0
- package/src/lib/server/printers.d.ts +114 -0
- package/src/lib/server/printers.js +546 -0
- package/src/lib/server/printers.js.map +1 -0
- package/src/lib/server/product-catalog.d.ts +42 -0
- package/src/lib/server/product-catalog.js +305 -0
- package/src/lib/server/product-catalog.js.map +1 -0
- package/src/lib/server/refund.js +110 -45
- package/src/lib/server/refund.js.map +1 -1
- package/src/lib/server/reserve.js +2 -0
- package/src/lib/server/reserve.js.map +1 -1
- package/src/lib/server/return-status-action.d.ts +37 -0
- package/src/lib/server/return-status-action.js +57 -0
- package/src/lib/server/return-status-action.js.map +1 -0
- package/src/lib/server/returns.d.ts +119 -0
- package/src/lib/server/returns.js +938 -0
- package/src/lib/server/returns.js.map +1 -0
- package/src/lib/server/shipment-records.d.ts +36 -0
- package/src/lib/server/shipment-records.js +289 -0
- package/src/lib/server/shipment-records.js.map +1 -0
- package/src/lib/server/shipping-connector-gates.d.ts +40 -0
- package/src/lib/server/shipping-connector-gates.js +97 -0
- package/src/lib/server/shipping-connector-gates.js.map +1 -0
- package/src/lib/server/shipping-connectors.d.ts +29 -0
- package/src/lib/server/shipping-connectors.js +236 -0
- package/src/lib/server/shipping-connectors.js.map +1 -0
- package/src/lib/server/shippingeasy.d.ts +238 -0
- package/src/lib/server/shippingeasy.js +787 -0
- package/src/lib/server/shippingeasy.js.map +1 -0
- package/src/lib/server/shipstation.d.ts +129 -0
- package/src/lib/server/shipstation.js +483 -0
- package/src/lib/server/shipstation.js.map +1 -0
- package/src/lib/server/site-page-resolver.d.ts +5 -0
- package/src/lib/server/site-page-resolver.js +156 -1
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server/stock-levels.d.ts +21 -0
- package/src/lib/server/stock-levels.js +263 -0
- package/src/lib/server/stock-levels.js.map +1 -0
- package/src/lib/server/stripe-refund.d.ts +47 -0
- package/src/lib/server/stripe-refund.js +87 -0
- package/src/lib/server/stripe-refund.js.map +1 -0
- package/src/lib/server/supplier-update.js +46 -5
- package/src/lib/server/supplier-update.js.map +1 -1
- package/src/lib/server/tax-engine-quote.d.ts +47 -0
- package/src/lib/server/tax-engine-quote.js +105 -0
- package/src/lib/server/tax-engine-quote.js.map +1 -0
- package/src/lib/server.js +138 -2
- package/src/lib/server.js.map +1 -1
- package/src/lib/site.js +12 -0
- package/src/lib/site.js.map +1 -1
- package/src/lib/subprocessors.d.ts +26 -0
- package/src/lib/subprocessors.js +50 -0
- package/src/lib/subprocessors.js.map +1 -0
- package/src/lib/tenant-emails.js +461 -3
- package/src/lib/tenant-emails.js.map +1 -1
- package/src/lib/testing/fake-firestore.d.ts +45 -0
- package/src/lib/testing/fake-firestore.js +189 -0
- package/src/lib/testing/fake-firestore.js.map +1 -0
- package/src/lib/testing/memory-firestore.d.ts +123 -0
- package/src/lib/testing/memory-firestore.js +274 -0
- package/src/lib/testing/memory-firestore.js.map +1 -0
- package/src/lib/testing/pos-ops-harness.d.ts +50 -0
- package/src/lib/testing/pos-ops-harness.js +184 -0
- package/src/lib/testing/pos-ops-harness.js.map +1 -0
- package/src/lib/testing/pos-ops-memory-firestore.d.ts +55 -0
- package/src/lib/testing/pos-ops-memory-firestore.js +376 -0
- package/src/lib/testing/pos-ops-memory-firestore.js.map +1 -0
- package/src/lib/transfer/records-transfer.d.ts +11 -1
- package/src/lib/transfer/records-transfer.js +127 -5
- package/src/lib/transfer/records-transfer.js.map +1 -1
- package/src/lib/transfer/records.server.d.ts +9 -2
- package/src/lib/transfer/records.server.js +58 -7
- package/src/lib/transfer/records.server.js.map +1 -1
- package/src/lib/transfer/register-transfer-resources.js +40 -1
- package/src/lib/transfer/register-transfer-resources.js.map +1 -1
- package/src/lib/transfer/register-transfer-ui.js +4 -1
- package/src/lib/transfer/register-transfer-ui.js.map +1 -1
- package/src/lib/transfer/shipping-presets.d.ts +23 -0
- package/src/lib/transfer/shipping-presets.js +156 -0
- package/src/lib/transfer/shipping-presets.js.map +1 -0
- package/src/lib/transfer/tracking-transfer.d.ts +101 -0
- package/src/lib/transfer/tracking-transfer.js +546 -0
- package/src/lib/transfer/tracking-transfer.js.map +1 -0
- package/src/lib/transfer/tracking.server.d.ts +37 -0
- package/src/lib/transfer/tracking.server.js +393 -0
- package/src/lib/transfer/tracking.server.js.map +1 -0
- package/src/lib/transfer/transfer-keys.d.ts +2 -1
- package/src/lib/transfer/transfer-keys.js +6 -2
- package/src/lib/transfer/transfer-keys.js.map +1 -1
- package/src/lib/utils/code128.d.ts +37 -0
- package/src/lib/utils/code128.js +199 -0
- package/src/lib/utils/code128.js.map +1 -0
- package/src/lib/utils/order-invoice.d.ts +55 -0
- package/src/lib/utils/order-invoice.js +127 -0
- package/src/lib/utils/order-invoice.js.map +1 -0
- package/src/lib/utils/use-checkout-return-notice.d.ts +34 -0
- package/src/lib/utils/use-checkout-return-notice.js +78 -0
- package/src/lib/utils/use-checkout-return-notice.js.map +1 -0
- package/src/lib/server/feed.js +0 -69
- package/src/lib/server/feed.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/order-notifications.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 * as Aglyn from '@aglyn/aglyn/server'\nimport { pluginSmsMessaging } from '@aglyn/aglyn/plugin-manager/plugin-sms-messaging'\nimport { resolveSiteTimeZone } from '@aglyn/aglyn/app-utils/collection-entry-date'\nimport {\n resolveHostToken,\n type HostTokenSource,\n} from '@aglyn/aglyn/app-utils/host-tokens'\nimport {\n firebaseAdmin,\n getOrgForHost,\n hostSendingIdentity,\n meterHostEmail,\n renderHostEmailWithTokens,\n} from '@aglyn/tenant-data-admin'\nimport { isEmailConfigured, sendEmail } from '@aglyn/shared-util-email'\nimport * as CommerceModel from '../model'\nimport { mintDownloadToken } from './download'\nimport { orderStatusUrl } from './order-status-token'\n\n/**\n * Every message a buyer gets about their order, through ONE door (AGL-3610).\n *\n * Before this, the buyer heard about their order once — the online receipt —\n * and never again: a register sale sent nothing, a paid payment link sent\n * nothing, and shipping, delivery, refunds and cancellations were silent\n * (`fulfill-order.ts` said so on purpose). Every path that moves an order now\n * makes one call here, and this decides what that moment owes the buyer:\n *\n * - whether the store turned the moment off (`buyerNotifications` on the\n * store settings; ON unless explicitly off — see the model),\n * - which channels can carry it (email when mail is configured and the\n * order has an address; a text when the platform's SMS provider is\n * configured and the order carries the buyer's phone),\n * - and whether it was already sent.\n *\n * ## ONCE PER MOMENT, PER OCCURRENCE, PER CHANNEL\n *\n * Stripe redelivers webhooks, admins click twice, a lost response is retried.\n * Each message is CLAIMED before it is sent: a transaction reads the order's\n * `buyerNotifications` map and writes a `sending` marker for\n * `event__occurrence__channel` only if none is there. Exactly one of two\n * racing callers wins the claim; the other returns `already`. The occurrence\n * is the shipment id for `shipped` and the refund id for `refunded`, so a\n * second parcel or a second partial refund is a new message, while a\n * redelivered one is not.\n *\n * A send that fails RELEASES its claim so the next attempt can try again. A\n * process that dies between claim and send leaves the marker in `sending` and\n * the message unsent — at most once, never twice, which is the right side to\n * fail on for mail a buyer reads as \"did they charge me again?\".\n *\n * ## NEVER THROWS\n *\n * Every caller is past its point of no return — money moved, an order\n * shipped — so a mail outage must not turn into a 500 that invites the\n * merchant to do it again. Failures are logged and returned as outcomes.\n *\n * Shared with the order-processing and POS lanes (AGL-3611, AGL-3607): call\n * {@link notifyOrderBuyer} from any new path that moves an order, and\n * {@link sendOrderReceipt} for an explicit, merchant-asked send.\n */\n\nexport type OrderBuyerEvent = CommerceModel.BuyerNotificationEvent\n\nexport interface OrderRef {\n hostId: string\n orderId: string\n}\n\nexport interface NotifyOrderBuyerOptions {\n /** `shipped`: which shipment. Absent means the order's latest one. */\n fulfillmentId?: string\n /** `refunded`: the refund's own id (Stripe's), the occurrence key. */\n refundId?: string\n /** `refunded`: what THIS refund moved, in cents. */\n refundCents?: number\n /** `refunded`: the line indexes refunded by name, when any were. */\n refundLineIndexes?: readonly number[]\n /** `refunded`: whether this refund closed the order. */\n fullyRefunded?: boolean\n /**\n * The buyer's address when the order document does not carry it — a POS\n * folio sale attributed to the reservation's guest, a payment link whose\n * payer Stripe named. The order's own `customerEmail` wins when present.\n */\n email?: string | null\n /** Injected in specs. */\n firestore?: FirebaseFirestore.Firestore\n}\n\nexport type OrderBuyerChannelOutcome =\n | { channel: CommerceModel.BuyerNotificationChannel; outcome: 'sent' }\n | { channel: CommerceModel.BuyerNotificationChannel; outcome: 'already' }\n | {\n channel: CommerceModel.BuyerNotificationChannel\n outcome: 'failed'\n error: string\n }\n\nexport type NotifyOrderBuyerOutcome =\n | { outcome: 'disabled' }\n | { outcome: 'no_such_order' }\n /** The moment does not apply to this order (a counter sale \"shipping\"). */\n | { outcome: 'not_applicable' }\n /** No channel could carry it: no address, no mail or text configured. */\n | { outcome: 'no_recipient' }\n | { outcome: 'handled'; channels: OrderBuyerChannelOutcome[] }\n\ninterface StoreContext {\n firestore: FirebaseFirestore.Firestore\n hostRef: FirebaseFirestore.DocumentReference\n host: Record<string, unknown>\n settings: Record<string, unknown>\n currency: string\n}\n\nasync function readStore(\n firestore: FirebaseFirestore.Firestore,\n hostId: string,\n): Promise<StoreContext> {\n const hostRef = firestore.collection('hosts').doc(hostId)\n const [hostSnapshot, settingsSnapshot] = await Promise.all([\n hostRef.get().catch(() => null),\n hostRef.collection('settings').doc('store').get().catch(() => null),\n ])\n const settings = (settingsSnapshot?.data?.() ?? {}) as Record<string, unknown>\n return {\n firestore,\n hostRef,\n host: (hostSnapshot?.data?.() ?? {}) as Record<string, unknown>,\n settings,\n currency: String(settings['currency'] ?? 'USD') || 'USD',\n }\n}\n\nfunction lineLabel(line: CommerceModel.OrderLineItem): string {\n return `${line.quantity}× ${line.name}${\n line.variantLabel ? ` (${line.variantLabel})` : ''\n }`\n}\n\n/** The status link, or the store's home page when no link can be minted. */\nfunction statusLink(store: StoreContext, ref: OrderRef): string {\n return (\n orderStatusUrl(store.host as never, ref.hostId, ref.orderId) ??\n Aglyn.hostPublicOrigin({\n cname: typeof store.host['cname'] === 'string' ? store.host['cname'] : null,\n subdomain:\n typeof store.host['subdomain'] === 'string'\n ? store.host['subdomain']\n : null,\n }) ??\n ''\n )\n}\n\ninterface ComposedMessage {\n emailKey: string\n /** Tokens for the designed email. */\n tokens: Record<string, string>\n /** Subject when the template renders none. */\n subject: string\n /** Plain-text body when the template renders none. */\n text: string\n /** The text message, kept short: one or two segments. */\n sms: string\n /** The order timeline line recording the send, channel appended. */\n timelineDetail: string\n}\n\n/** What the moment says. `null` when it does not apply to this order. */\nexport function composeOrderBuyerMessage(input: {\n event: OrderBuyerEvent\n order: CommerceModel.HostOrder\n orderId: string\n businessName: string\n currency: string\n statusUrl: string\n receiptFooter?: string\n /** Absolute digital-delivery links for a receipt, by product id. */\n downloadLinks?: Record<string, string>\n options?: NotifyOrderBuyerOptions\n}): (ComposedMessage & { occurrence: string }) | null {\n const {\n event,\n order,\n orderId,\n businessName,\n currency,\n statusUrl,\n options = {},\n } = input\n const lines = order.lineItems ?? []\n const number = CommerceModel.formatOrderNumber(order, orderId)\n const money = (cents: number) =>\n CommerceModel.formatOrderMoney(cents, currency)\n const store = businessName || 'The store'\n const base = {\n 'order.number': number,\n 'order.statusUrl': statusUrl,\n 'order.ref': orderId,\n }\n const link = statusUrl ? `\\n\\nView your order: ${statusUrl}` : ''\n const smsLink = statusUrl ? ` ${statusUrl}` : ''\n\n if (event === 'receipt') {\n const totalCents =\n order.totals?.totalCents ?? Number(order.amountCents ?? 0)\n const licenseText = Object.entries(\n ((order as { licenseKeys?: Record<string, string[]> }).licenseKeys ??\n {}) as Record<string, string[]>,\n )\n .flatMap(([productId, keys]) => {\n const line = lines.find((item) => item.productId === productId)\n return (keys ?? []).map(\n (key) => `License key (${line?.name ?? 'product'}): ${key}`,\n )\n })\n .join('\\n')\n const downloads = Object.entries(input.downloadLinks ?? {})\n .map(([productId, url]) => {\n const line = lines.find((item) => item.productId === productId)\n return `Download ${line?.name ?? 'your file'}: ${url}`\n })\n .join('\\n')\n const linesText = lines\n .map(\n (line) =>\n `${lineLabel(line)} — ${money(line.unitAmountCents * line.quantity)}`,\n )\n .join('\\n')\n const summary = [linesText, licenseText, downloads]\n .filter(Boolean)\n .join('\\n\\n')\n const footer = String(input.receiptFooter ?? '')\n return {\n occurrence: 'order',\n emailKey: CommerceModel.BUYER_NOTIFICATION_EMAIL_KEYS.receipt,\n tokens: {\n ...base,\n 'order.summary': summary,\n 'order.total': money(totalCents),\n 'store.receiptFooter': footer,\n },\n subject: 'Receipt for your order',\n text:\n `Thanks for your purchase!\\n\\n${summary}\\n\\nTotal: ${money(totalCents)}\\n` +\n `Order ${number}` +\n (footer ? `\\n\\n${footer}` : '') +\n link,\n sms: `${store}: receipt for order ${number}, ${money(totalCents)}.${smsLink}`,\n timelineDetail: 'Receipt sent',\n }\n }\n\n if (event === 'shipped') {\n const fulfillments = order.fulfillments ?? []\n const fulfillment = options.fulfillmentId\n ? fulfillments.find((entry) => entry.id === options.fulfillmentId)\n : [...fulfillments].sort((a, b) => (b.atMs ?? 0) - (a.atMs ?? 0))[0]\n if (!fulfillment) return null\n // The units THIS shipment carried (AGL-3611): a partial shipment of a\n // line names its own count, not the line's.\n const shipped = CommerceModel.fulfillmentLineQuantities(order, fulfillment)\n .map((entry) =>\n lines[entry.lineItemId] ? { ...lines[entry.lineItemId], quantity: entry.quantity } : null,\n )\n .filter(Boolean) as CommerceModel.OrderLineItem[]\n const tracking = CommerceModel.fulfillmentTrackingUrl(fulfillment)\n const hasTracking = Boolean(fulfillment.trackingNumber)\n // Nothing physical left the building: a register sale handed over the\n // counter, or a digital-only order the merchant marked fulfilled. A\n // \"your order is on its way\" email for either is noise at best.\n const physical = shipped.some(\n (line) =>\n line.productType !== 'digital' && line.productType !== 'service',\n )\n if (!hasTracking && (order.channel === 'pos' || !physical)) return null\n // Shippable units still out, over every active shipment (AGL-3611).\n const remainingUnits = CommerceModel.remainingFulfillmentLines(order).reduce(\n (sum, entry) => sum + entry.quantity,\n 0,\n )\n const carrier = CommerceModel.carrierLabelFor(fulfillment.carrier)\n const trackingSentence = hasTracking\n ? `Tracking: ${[carrier, fulfillment.trackingNumber]\n .filter(Boolean)\n .join(' ')}${tracking ? ` — ${tracking}` : ''}`\n : ''\n const remainingSentence =\n remainingUnits > 0\n ? `${remainingUnits} more item${remainingUnits === 1 ? '' : 's'} will ship separately.`\n : ''\n const summary = shipped.map(lineLabel).join('\\n')\n return {\n occurrence: fulfillment.id,\n emailKey: CommerceModel.BUYER_NOTIFICATION_EMAIL_KEYS.shipped,\n tokens: {\n ...base,\n 'shipment.summary': summary,\n 'shipment.carrier': carrier,\n 'shipment.trackingNumber': String(fulfillment.trackingNumber ?? ''),\n 'shipment.tracking': trackingSentence,\n 'shipment.remaining': remainingSentence,\n },\n subject: `Your order ${number} has shipped`,\n text:\n `${store} shipped items from order ${number}.\\n\\n${summary}` +\n (trackingSentence ? `\\n\\n${trackingSentence}` : '') +\n (remainingSentence ? `\\n\\n${remainingSentence}` : '') +\n link,\n sms:\n `${store}: order ${number} has shipped.` +\n (tracking ? ` Track it: ${tracking}` : smsLink),\n timelineDetail: 'Shipping confirmation sent',\n }\n }\n\n if (event === 'delivered') {\n // A counter sale is \"delivered\" the moment it is handed over.\n if (order.channel === 'pos') return null\n const summary = lines.map(lineLabel).join('\\n')\n return {\n occurrence: 'order',\n emailKey: CommerceModel.BUYER_NOTIFICATION_EMAIL_KEYS.delivered,\n tokens: { ...base, 'order.summary': summary },\n subject: `Your order ${number} was delivered`,\n text:\n `${store} marked order ${number} as delivered.\\n\\n${summary}` + link,\n sms: `${store}: order ${number} was delivered.${smsLink}`,\n timelineDetail: 'Delivery confirmation sent',\n }\n }\n\n if (event === 'refunded') {\n const refundCents = Math.max(0, Math.round(Number(options.refundCents ?? 0)))\n if (!(refundCents > 0)) return null\n const refundedLines = (options.refundLineIndexes ?? [])\n .map((index) => lines[index])\n .filter(Boolean) as CommerceModel.OrderLineItem[]\n const summary = refundedLines.map(lineLabel).join('\\n')\n const note = options.fullyRefunded\n ? 'Your order has been refunded in full.'\n : 'The rest of your order is unchanged.'\n return {\n // The refund's own id is the occurrence; without one, the running\n // total after this refund is (each settled refund raises it).\n occurrence:\n options.refundId ||\n `total-${Number(order.refundedCents ?? 0) || refundCents}`,\n emailKey: CommerceModel.BUYER_NOTIFICATION_EMAIL_KEYS.refunded,\n tokens: {\n ...base,\n 'refund.amount': money(refundCents),\n 'refund.summary': summary,\n 'refund.note': note,\n },\n subject: `Refund for your order ${number}`,\n text:\n `${store} refunded ${money(refundCents)} on order ${number}.` +\n (summary ? `\\n\\n${summary}` : '') +\n `\\n\\n${note}\\n\\nRefunds go back to the original payment method and ` +\n 'usually appear within 5 to 10 business days, depending on your bank.' +\n link,\n sms: `${store}: ${money(refundCents)} refunded on order ${number}.${smsLink}`,\n timelineDetail: `Refund confirmation sent (${money(refundCents)})`,\n }\n }\n\n // cancelled\n const summary = lines.map(lineLabel).join('\\n')\n const wasPaid = Boolean(\n order.paymentIntentId ||\n (order.channel === 'pos' && order.status !== 'pending') ||\n (order.timeline ?? []).some((entry) => entry?.event === 'paid'),\n )\n const note = wasPaid\n ? `Questions about a payment? Reply to this email or contact ${store}.`\n : ''\n return {\n occurrence: 'order',\n emailKey: CommerceModel.BUYER_NOTIFICATION_EMAIL_KEYS.cancelled,\n tokens: { ...base, 'order.summary': summary, 'cancel.note': note },\n subject: `Your order ${number} was canceled`,\n text:\n `${store} canceled order ${number}.\\n\\n${summary}` +\n (note ? `\\n\\n${note}` : '') +\n link,\n sms: `${store}: order ${number} was canceled.${smsLink}`,\n timelineDetail: 'Cancellation notice sent',\n }\n}\n\n/**\n * Claims one message in a transaction. `true` when this caller owns the send;\n * `false` when a marker is already there (sent, or being sent).\n */\nasync function claimMessage(\n firestore: FirebaseFirestore.Firestore,\n orderRef: FirebaseFirestore.DocumentReference,\n marker: string,\n channel: CommerceModel.BuyerNotificationChannel,\n): Promise<boolean> {\n return firestore.runTransaction(async (transaction) => {\n const snapshot = await transaction.get(orderRef)\n if (!snapshot.exists) return false\n const sent = (snapshot.get('buyerNotifications') ?? {}) as Record<\n string,\n unknown\n >\n if (sent[marker]) return false\n transaction.update(orderRef, {\n [`buyerNotifications.${marker}`]: {\n state: 'sending',\n channel,\n atMs: Date.now(),\n },\n })\n return true\n })\n}\n\n/** Marks a claimed message sent and records it on the order timeline. */\nasync function settleMessage(\n firestore: FirebaseFirestore.Firestore,\n orderRef: FirebaseFirestore.DocumentReference,\n marker: string | null,\n channel: CommerceModel.BuyerNotificationChannel,\n detail: string,\n): Promise<void> {\n await firestore\n .runTransaction(async (transaction) => {\n const snapshot = await transaction.get(orderRef)\n if (!snapshot.exists) return\n const order = CommerceModel.liftLegacyOrder(\n (snapshot.data() ?? {}) as never,\n )\n const atMs = Date.now()\n transaction.update(orderRef, {\n ...(marker\n ? {\n [`buyerNotifications.${marker}`]: { state: 'sent', channel, atMs },\n }\n : {}),\n timeline: CommerceModel.appendOrderEvent(\n order,\n 'buyer-notified',\n `${detail} by ${channel === 'sms' ? 'text' : 'email'}`,\n atMs,\n ),\n })\n })\n .catch((error) => {\n console.error('[order-notifications] settle failed', marker, error)\n })\n}\n\nasync function releaseMessage(\n orderRef: FirebaseFirestore.DocumentReference,\n marker: string,\n): Promise<void> {\n await orderRef\n .update({\n [`buyerNotifications.${marker}`]:\n firebaseAdmin.firestore.FieldValue.delete(),\n })\n .catch(() => undefined)\n}\n\nasync function deliverEmail(\n store: StoreContext,\n ref: OrderRef,\n to: string,\n message: ComposedMessage,\n context: string,\n): Promise<void> {\n const designed = await renderHostEmailWithTokens(\n store.firestore,\n ref.hostId,\n message.emailKey,\n message.tokens,\n )\n const org = await getOrgForHost(ref.hostId).catch(() => null)\n await sendEmail({\n to,\n subject: designed?.subject ?? message.subject,\n text: designed?.text || message.text,\n ...(designed?.html ? { html: designed.html } : {}),\n fromName: Aglyn.resolveBrandingProfile(org?.org as never).fromName,\n sendingIdentity: await hostSendingIdentity(ref.hostId),\n audience: 'tenant',\n context,\n // Owed to the recipient by their own order: the phishing screen's soft\n // rules never hold it (AGL-3356).\n owedFor: 'order',\n })\n // Cost meter (AGL-1438), as every transactional store email.\n await meterHostEmail(ref.hostId)\n}\n\n/**\n * What a text attempt came to: `refused` names why nothing went (null when it\n * went), and `scheduledForMs` says it was held for the buyer's morning.\n */\ninterface SmsDelivery {\n refused: string | null\n scheduledForMs?: number\n}\n\n/**\n * Sends one order text. A receipt goes at once — the buyer is at the counter\n * or the checkout waiting for it. Every later moment (shipped, delivered,\n * refunded, canceled) can fire at any hour, from a carrier scan or a late\n * packing shift, so it asks the provider to keep quiet hours in the STORE's\n * zone: the best stand-in the platform has for a buyer whose own zone it was\n * never told, and right for the local and regional stores most texts come\n * from. Held texts arrive at 8 AM there.\n */\nasync function deliverSms(\n ref: OrderRef,\n to: string,\n message: ComposedMessage,\n context: string,\n quiet: { host: Record<string, unknown> } | null,\n): Promise<SmsDelivery> {\n const sms = pluginSmsMessaging()\n if (!sms?.isConfigured()) return { refused: 'Text messages are not configured' }\n let timeZone: string | null = null\n if (quiet) {\n const owner = await getOrgForHost(ref.hostId).catch(() => null)\n timeZone = resolveSiteTimeZone(\n (owner?.org ?? null) as { timeZone?: string } | null,\n quiet.host as { timeZone?: string },\n )\n }\n const outcome = await sms.send({\n to,\n body: message.sms,\n hostId: ref.hostId,\n purpose: 'transactional',\n context,\n ...(timeZone ? { quietHours: { timeZone } } : {}),\n })\n if (outcome.status !== 'sent') return { refused: outcome.status }\n return {\n refused: null,\n ...(outcome.scheduledForMs ? { scheduledForMs: outcome.scheduledForMs } : {}),\n }\n}\n\n/** Absolute download links for a receipt's digital lines. */\nfunction downloadLinksFor(\n store: StoreContext,\n ref: OrderRef,\n order: CommerceModel.HostOrder,\n): Record<string, string> {\n const origin = Aglyn.hostPublicOrigin({\n cname: typeof store.host['cname'] === 'string' ? store.host['cname'] : null,\n subdomain:\n typeof store.host['subdomain'] === 'string' ? store.host['subdomain'] : null,\n })\n const digital = (order.lineItems ?? []).filter(\n (line) => line.productType === 'digital',\n )\n if (!origin || !digital.length) return {}\n let token: string\n try {\n token = mintDownloadToken(ref.hostId, ref.orderId)\n } catch {\n return {}\n }\n return Object.fromEntries(\n digital.map((line) => [\n line.productId,\n `${origin}/api/commerce/download?hostId=${encodeURIComponent(ref.hostId)}` +\n `&orderId=${encodeURIComponent(ref.orderId)}` +\n `&productId=${encodeURIComponent(line.productId)}&token=${token}`,\n ]),\n )\n}\n\nasync function readOrder(\n firestore: FirebaseFirestore.Firestore,\n ref: OrderRef,\n): Promise<{\n orderRef: FirebaseFirestore.DocumentReference\n order: CommerceModel.HostOrder | null\n}> {\n const orderRef = firestore\n .collection('hosts')\n .doc(ref.hostId)\n .collection('orders')\n .doc(ref.orderId)\n const snapshot = await orderRef.get()\n return {\n orderRef,\n order: snapshot.exists\n ? CommerceModel.liftLegacyOrder((snapshot.data() ?? {}) as never)\n : null,\n }\n}\n\nfunction emailOf(value: unknown): string {\n const email = String(value ?? '').trim()\n return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email) ? email : ''\n}\n\n/**\n * Tells the buyer about one moment in their order's life, once. See the\n * module header. Never throws.\n */\nexport async function notifyOrderBuyer(\n ref: OrderRef,\n event: OrderBuyerEvent,\n options: NotifyOrderBuyerOptions = {},\n): Promise<NotifyOrderBuyerOutcome> {\n try {\n if (!ref.hostId || !ref.orderId) return { outcome: 'no_such_order' }\n const firestore = options.firestore ?? firebaseAdmin.app().firestore()\n const store = await readStore(firestore, ref.hostId)\n const toggles = store.settings['buyerNotifications']\n if (!CommerceModel.buyerNotificationEnabled(toggles, event)) {\n return { outcome: 'disabled' }\n }\n const { orderRef, order } = await readOrder(firestore, ref)\n if (!order) return { outcome: 'no_such_order' }\n const message = composeOrderBuyerMessage({\n event,\n order,\n orderId: ref.orderId,\n businessName: String(\n resolveHostToken('businessName', store.host as HostTokenSource) ?? '',\n ),\n currency: store.currency,\n statusUrl: statusLink(store, ref),\n receiptFooter: String(store.settings['receiptFooter'] ?? ''),\n downloadLinks:\n event === 'receipt' ? downloadLinksFor(store, ref, order) : undefined,\n options,\n })\n if (!message) return { outcome: 'not_applicable' }\n\n const email = emailOf(order.customerEmail) || emailOf(options.email)\n const phone = String(order.customerPhone ?? '').trim()\n const targets: {\n channel: CommerceModel.BuyerNotificationChannel\n to: string\n }[] = []\n if (email && isEmailConfigured()) targets.push({ channel: 'email', to: email })\n if (\n phone &&\n CommerceModel.buyerNotificationEnabled(toggles, 'texts') &&\n pluginSmsMessaging()?.isConfigured()\n ) {\n targets.push({ channel: 'sms', to: phone })\n }\n if (!targets.length) return { outcome: 'no_recipient' }\n\n const channels: OrderBuyerChannelOutcome[] = []\n for (const target of targets) {\n const marker = CommerceModel.buyerNotificationMarker(\n event,\n message.occurrence,\n target.channel,\n )\n const claimed = await claimMessage(\n firestore,\n orderRef,\n marker,\n target.channel,\n ).catch((error) => {\n console.error('[order-notifications] claim failed', marker, error)\n return false\n })\n if (!claimed) {\n channels.push({ channel: target.channel, outcome: 'already' })\n continue\n }\n const context = message.emailKey\n let held: number | null = null\n try {\n if (target.channel === 'email') {\n await deliverEmail(store, ref, target.to, message, context)\n } else {\n const sent = await deliverSms(\n ref,\n target.to,\n message,\n context,\n event === 'receipt' ? null : { host: store.host },\n )\n if (sent.refused) throw new Error(sent.refused)\n held = sent.scheduledForMs ?? null\n }\n await settleMessage(\n firestore,\n orderRef,\n marker,\n target.channel,\n held\n ? `${message.timelineDetail} (held for the morning)`\n : message.timelineDetail,\n )\n channels.push({ channel: target.channel, outcome: 'sent' })\n } catch (error) {\n await releaseMessage(orderRef, marker)\n console.error('[order-notifications] send failed', {\n hostId: ref.hostId,\n orderId: ref.orderId,\n event,\n channel: target.channel,\n error,\n })\n channels.push({\n channel: target.channel,\n outcome: 'failed',\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n return { outcome: 'handled', channels }\n } catch (error) {\n console.error('[order-notifications] notify failed', ref, event, error)\n return { outcome: 'handled', channels: [] }\n }\n}\n\nexport type SendOrderReceiptOutcome =\n | { outcome: 'sent'; channel: CommerceModel.BuyerNotificationChannel }\n | { outcome: 'no_such_order' }\n | { outcome: 'not_configured' }\n | { outcome: 'invalid_recipient' }\n | { outcome: 'failed'; error: string }\n\n/**\n * Sends the receipt again, on the merchant's say-so, to the address or number\n * they gave (AGL-3610) — the order dialog's \"Resend receipt\".\n *\n * NOT idempotent, on purpose: a resend IS the second send. The route in front\n * of it is rate-limited instead, and the send is recorded on the timeline so\n * the merchant can see it went. It does not consult the store's moment\n * switches either — a merchant who turned automatic receipts off can still\n * hand one to a customer who asks.\n */\nexport async function sendOrderReceipt(\n ref: OrderRef,\n input: {\n channel: CommerceModel.BuyerNotificationChannel\n to: string\n firestore?: FirebaseFirestore.Firestore\n },\n): Promise<SendOrderReceiptOutcome> {\n try {\n const firestore = input.firestore ?? firebaseAdmin.app().firestore()\n const { orderRef, order } = await readOrder(firestore, ref)\n if (!order) return { outcome: 'no_such_order' }\n if (input.channel === 'email') {\n if (!isEmailConfigured()) return { outcome: 'not_configured' }\n if (!emailOf(input.to)) return { outcome: 'invalid_recipient' }\n } else {\n if (!pluginSmsMessaging()?.isConfigured()) {\n return { outcome: 'not_configured' }\n }\n if (!Aglyn.normalizePhone(input.to)) {\n return { outcome: 'invalid_recipient' }\n }\n }\n const store = await readStore(firestore, ref.hostId)\n const message = composeOrderBuyerMessage({\n event: 'receipt',\n order,\n orderId: ref.orderId,\n businessName: String(\n resolveHostToken('businessName', store.host as HostTokenSource) ?? '',\n ),\n currency: store.currency,\n statusUrl: statusLink(store, ref),\n receiptFooter: String(store.settings['receiptFooter'] ?? ''),\n downloadLinks: downloadLinksFor(store, ref, order),\n })\n if (!message) return { outcome: 'failed', error: 'Nothing to send' }\n if (input.channel === 'email') {\n await deliverEmail(store, ref, emailOf(input.to), message, 'order-receipt')\n } else {\n const sent = await deliverSms(ref, input.to, message, 'order-receipt', null)\n if (sent.refused) return { outcome: 'failed', error: sent.refused }\n }\n await settleMessage(firestore, orderRef, null, input.channel, 'Receipt re-sent')\n return { outcome: 'sent', channel: input.channel }\n } catch (error) {\n console.error('[order-notifications] receipt send failed', ref, error)\n return {\n outcome: 'failed',\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n/**\n * What the online receipt sends needs from this module (AGL-3610): whether\n * the store left receipts on, and the `order.number` / `order.statusUrl`\n * tokens. The cart and buy-now receipts compose their own summary (license\n * keys and download links built from the Stripe session) inside the\n * webhook's redelivery guard, so they ask here rather than route through\n * {@link notifyOrderBuyer}.\n */\nexport async function onlineReceiptExtras(\n hostId: string,\n orderId: string,\n firestore: FirebaseFirestore.Firestore = firebaseAdmin.app().firestore(),\n): Promise<{ enabled: boolean; tokens: Record<string, string> }> {\n try {\n const store = await readStore(firestore, hostId)\n const { order } = await readOrder(firestore, { hostId, orderId })\n return {\n enabled: CommerceModel.buyerNotificationEnabled(\n store.settings['buyerNotifications'],\n 'receipt',\n ),\n tokens: {\n 'order.number': CommerceModel.formatOrderNumber(order ?? {}, orderId),\n 'order.statusUrl': statusLink(store, { hostId, orderId }),\n },\n }\n } catch (error) {\n console.error('[order-notifications] receipt extras failed', error)\n return { enabled: true, tokens: { 'order.number': '', 'order.statusUrl': '' } }\n }\n}\n"],"names":["Aglyn","pluginSmsMessaging","resolveSiteTimeZone","resolveHostToken","firebaseAdmin","getOrgForHost","hostSendingIdentity","meterHostEmail","renderHostEmailWithTokens","isEmailConfigured","sendEmail","CommerceModel","mintDownloadToken","orderStatusUrl","readStore","firestore","hostId","settings","settingsSnapshot","hostSnapshot","hostRef","collection","doc","Promise","all","get","catch","data","host","currency","String","lineLabel","line","quantity","name","variantLabel","statusLink","store","ref","orderId","hostPublicOrigin","cname","subdomain","composeOrderBuyerMessage","input","order","event","businessName","statusUrl","options","lines","lineItems","number","formatOrderNumber","money","cents","formatOrderMoney","base","link","smsLink","totalCents","totals","Number","amountCents","licenseText","Object","entries","licenseKeys","flatMap","productId","keys","find","item","map","key","join","downloads","downloadLinks","url","linesText","unitAmountCents","summary","filter","Boolean","footer","receiptFooter","occurrence","emailKey","BUYER_NOTIFICATION_EMAIL_KEYS","receipt","tokens","subject","text","sms","timelineDetail","fulfillment","fulfillments","fulfillmentId","entry","id","sort","a","b","atMs","shipped","fulfillmentLineQuantities","lineItemId","tracking","fulfillmentTrackingUrl","hasTracking","trackingNumber","physical","some","productType","channel","remainingUnits","remainingFulfillmentLines","reduce","sum","carrier","carrierLabelFor","trackingSentence","remainingSentence","delivered","refundCents","Math","max","round","refundedLines","refundLineIndexes","index","note","fullyRefunded","refundId","refundedCents","refunded","wasPaid","paymentIntentId","status","timeline","cancelled","claimMessage","orderRef","marker","runTransaction","transaction","snapshot","exists","sent","update","state","Date","now","settleMessage","detail","liftLegacyOrder","appendOrderEvent","error","console","releaseMessage","FieldValue","delete","undefined","deliverEmail","to","message","context","designed","org","html","fromName","resolveBrandingProfile","sendingIdentity","audience","owedFor","deliverSms","quiet","isConfigured","refused","timeZone","owner","outcome","send","body","purpose","quietHours","scheduledForMs","downloadLinksFor","origin","digital","length","token","fromEntries","encodeURIComponent","readOrder","emailOf","value","email","trim","test","notifyOrderBuyer","app","toggles","buyerNotificationEnabled","customerEmail","phone","customerPhone","targets","push","channels","target","buyerNotificationMarker","claimed","held","Error","sendOrderReceipt","normalizePhone","onlineReceiptExtras","enabled"],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type PluginApiHandler } from '@aglyn/aglyn/server';
|
|
18
|
+
/** Receipts one member may re-send per minute, across every order. */
|
|
19
|
+
export declare const RECEIPT_RESENDS_PER_MINUTE_PER_MEMBER = 10;
|
|
20
|
+
/** Receipts one order may be re-sent per hour, by anybody. */
|
|
21
|
+
export declare const RECEIPT_RESENDS_PER_HOUR_PER_ORDER = 5;
|
|
22
|
+
/**
|
|
23
|
+
* "Resend receipt" from the order dialog (AGL-3610): the console route
|
|
24
|
+
* `commerce/order-receipt-send`.
|
|
25
|
+
*
|
|
26
|
+
* GET answers which channels can carry a receipt right now —
|
|
27
|
+
* `{ email, sms }` — so the dialog never OFFERS a text the platform cannot
|
|
28
|
+
* send (no SMS provider configured) or an email it cannot (no mail rail).
|
|
29
|
+
*
|
|
30
|
+
* POST `{ hostId, orderId, channel, to }` sends the order's receipt to the
|
|
31
|
+
* address or number the merchant typed, through `sendOrderReceipt`.
|
|
32
|
+
*
|
|
33
|
+
* Role gate: `admin` and `editor`, the roles that already see and act on the
|
|
34
|
+
* order in the dialog (fulfil, cancel). Two rate limits, both durable: a
|
|
35
|
+
* member's resends per minute, and an order's resends per hour by anybody —
|
|
36
|
+
* the second is what stops the dialog becoming a way to mail one stranger the
|
|
37
|
+
* same receipt fifty times.
|
|
38
|
+
*/
|
|
39
|
+
export declare const orderReceiptSendHandler: PluginApiHandler;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { pluginSmsAvailable } from "@aglyn/aglyn/plugin-manager/plugin-sms-messaging";
|
|
17
|
+
import { consumeRateLimit, firebaseAdmin } from "@aglyn/tenant-data-admin";
|
|
18
|
+
import { isEmailConfigured } from "@aglyn/shared-util-email";
|
|
19
|
+
import { sendOrderReceipt } from "./order-notifications.js";
|
|
20
|
+
/** Receipts one member may re-send per minute, across every order. */ export const RECEIPT_RESENDS_PER_MINUTE_PER_MEMBER = 10;
|
|
21
|
+
/** Receipts one order may be re-sent per hour, by anybody. */ export const RECEIPT_RESENDS_PER_HOUR_PER_ORDER = 5;
|
|
22
|
+
/**
|
|
23
|
+
* "Resend receipt" from the order dialog (AGL-3610): the console route
|
|
24
|
+
* `commerce/order-receipt-send`.
|
|
25
|
+
*
|
|
26
|
+
* GET answers which channels can carry a receipt right now —
|
|
27
|
+
* `{ email, sms }` — so the dialog never OFFERS a text the platform cannot
|
|
28
|
+
* send (no SMS provider configured) or an email it cannot (no mail rail).
|
|
29
|
+
*
|
|
30
|
+
* POST `{ hostId, orderId, channel, to }` sends the order's receipt to the
|
|
31
|
+
* address or number the merchant typed, through `sendOrderReceipt`.
|
|
32
|
+
*
|
|
33
|
+
* Role gate: `admin` and `editor`, the roles that already see and act on the
|
|
34
|
+
* order in the dialog (fulfil, cancel). Two rate limits, both durable: a
|
|
35
|
+
* member's resends per minute, and an order's resends per hour by anybody —
|
|
36
|
+
* the second is what stops the dialog becoming a way to mail one stranger the
|
|
37
|
+
* same receipt fifty times.
|
|
38
|
+
*/ export const orderReceiptSendHandler = async (req, res)=>{
|
|
39
|
+
var _req_headers_authorization, _req_body, _ref;
|
|
40
|
+
if (req.method !== 'GET' && req.method !== 'POST') {
|
|
41
|
+
return res.status(405).json({
|
|
42
|
+
error: 'Method not allowed'
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const authorization = String((_req_headers_authorization = req.headers.authorization) != null ? _req_headers_authorization : '');
|
|
46
|
+
const idToken = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : undefined;
|
|
47
|
+
if (!idToken) return res.status(401).json({
|
|
48
|
+
error: 'Unauthenticated'
|
|
49
|
+
});
|
|
50
|
+
const body = req.method === 'POST' ? typeof req.body === 'string' ? JSON.parse(req.body || '{}') : (_req_body = req.body) != null ? _req_body : {} : {};
|
|
51
|
+
const hostId = String((_ref = req.method === 'GET' ? req.query.hostId : body.hostId) != null ? _ref : '');
|
|
52
|
+
if (!hostId || /^__.*__$/.test(hostId)) {
|
|
53
|
+
return res.status(400).json({
|
|
54
|
+
error: 'Missing hostId'
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
var _hostSnapshot_get, _body_orderId, _body_channel, _body_to;
|
|
59
|
+
const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken);
|
|
60
|
+
const hostSnapshot = await firebaseAdmin.app().firestore().collection('hosts').doc(hostId).get();
|
|
61
|
+
if (!hostSnapshot.exists) {
|
|
62
|
+
return res.status(404).json({
|
|
63
|
+
error: 'Unknown site'
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const memberRole = ((_hostSnapshot_get = hostSnapshot.get('memberRoles')) != null ? _hostSnapshot_get : {})[decoded.uid];
|
|
67
|
+
if (memberRole !== 'admin' && memberRole !== 'editor') {
|
|
68
|
+
return res.status(403).json({
|
|
69
|
+
error: 'Not permitted'
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (req.method === 'GET') {
|
|
73
|
+
return res.status(200).json({
|
|
74
|
+
email: isEmailConfigured(),
|
|
75
|
+
sms: pluginSmsAvailable()
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const orderId = String((_body_orderId = body.orderId) != null ? _body_orderId : '');
|
|
79
|
+
const channel = String((_body_channel = body.channel) != null ? _body_channel : 'email');
|
|
80
|
+
const to = String((_body_to = body.to) != null ? _body_to : '').trim().slice(0, 200);
|
|
81
|
+
if (!orderId || /^__.*__$/.test(orderId)) {
|
|
82
|
+
return res.status(400).json({
|
|
83
|
+
error: 'Missing orderId'
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (channel !== 'email' && channel !== 'sms') {
|
|
87
|
+
return res.status(400).json({
|
|
88
|
+
error: 'channel must be email or sms'
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (!to) {
|
|
92
|
+
return res.status(400).json({
|
|
93
|
+
error: channel === 'sms' ? 'Enter a phone number' : 'Enter an email address'
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const [member, order] = await Promise.all([
|
|
97
|
+
consumeRateLimit(`commerce-receipt-send:uid:${decoded.uid}`, {
|
|
98
|
+
limit: RECEIPT_RESENDS_PER_MINUTE_PER_MEMBER,
|
|
99
|
+
windowMs: 60000
|
|
100
|
+
}),
|
|
101
|
+
consumeRateLimit(`commerce-receipt-send:order:${hostId}:${orderId}`, {
|
|
102
|
+
limit: RECEIPT_RESENDS_PER_HOUR_PER_ORDER,
|
|
103
|
+
windowMs: 60 * 60000
|
|
104
|
+
})
|
|
105
|
+
]);
|
|
106
|
+
if (!member.allowed || !order.allowed) {
|
|
107
|
+
const resetMs = Math.max(member.allowed ? 0 : member.resetMs, order.allowed ? 0 : order.resetMs);
|
|
108
|
+
res.setHeader('Retry-After', String(Math.max(1, Math.ceil((resetMs - Date.now()) / 1000))));
|
|
109
|
+
return res.status(429).json({
|
|
110
|
+
error: order.allowed ? 'Too many receipts sent — wait a minute and try again.' : 'This receipt was re-sent several times in the last hour — try again later.'
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const outcome = await sendOrderReceipt({
|
|
114
|
+
hostId,
|
|
115
|
+
orderId
|
|
116
|
+
}, {
|
|
117
|
+
channel,
|
|
118
|
+
to
|
|
119
|
+
});
|
|
120
|
+
switch(outcome.outcome){
|
|
121
|
+
case 'sent':
|
|
122
|
+
return res.status(200).json({
|
|
123
|
+
ok: true,
|
|
124
|
+
channel
|
|
125
|
+
});
|
|
126
|
+
case 'no_such_order':
|
|
127
|
+
return res.status(404).json({
|
|
128
|
+
error: 'Unknown order'
|
|
129
|
+
});
|
|
130
|
+
case 'not_configured':
|
|
131
|
+
return res.status(409).json({
|
|
132
|
+
error: channel === 'sms' ? 'Text messages are not set up on this platform.' : 'Email is not set up on this platform.'
|
|
133
|
+
});
|
|
134
|
+
case 'invalid_recipient':
|
|
135
|
+
return res.status(400).json({
|
|
136
|
+
error: channel === 'sms' ? 'That phone number could not be read.' : 'That email address could not be read.'
|
|
137
|
+
});
|
|
138
|
+
default:
|
|
139
|
+
return res.status(502).json({
|
|
140
|
+
error: outcome.error === 'suppressed' ? 'That number has opted out of texts.' : 'The receipt could not be sent. Try again.'
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
console.error('orderReceiptSend failed', error);
|
|
145
|
+
return res.status(500).json({
|
|
146
|
+
error: 'The receipt could not be sent.'
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
//# sourceMappingURL=order-receipt-send.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/order-receipt-send.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 { type PluginApiHandler } from '@aglyn/aglyn/server'\nimport { pluginSmsAvailable } from '@aglyn/aglyn/plugin-manager/plugin-sms-messaging'\nimport { consumeRateLimit, firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport { isEmailConfigured } from '@aglyn/shared-util-email'\nimport { sendOrderReceipt } from './order-notifications'\n\n/** Receipts one member may re-send per minute, across every order. */\nexport const RECEIPT_RESENDS_PER_MINUTE_PER_MEMBER = 10\n/** Receipts one order may be re-sent per hour, by anybody. */\nexport const RECEIPT_RESENDS_PER_HOUR_PER_ORDER = 5\n\n/**\n * \"Resend receipt\" from the order dialog (AGL-3610): the console route\n * `commerce/order-receipt-send`.\n *\n * GET answers which channels can carry a receipt right now —\n * `{ email, sms }` — so the dialog never OFFERS a text the platform cannot\n * send (no SMS provider configured) or an email it cannot (no mail rail).\n *\n * POST `{ hostId, orderId, channel, to }` sends the order's receipt to the\n * address or number the merchant typed, through `sendOrderReceipt`.\n *\n * Role gate: `admin` and `editor`, the roles that already see and act on the\n * order in the dialog (fulfil, cancel). Two rate limits, both durable: a\n * member's resends per minute, and an order's resends per hour by anybody —\n * the second is what stops the dialog becoming a way to mail one stranger the\n * same receipt fifty times.\n */\nexport const orderReceiptSendHandler: PluginApiHandler = async (req, res) => {\n if (req.method !== 'GET' && req.method !== 'POST') {\n return res.status(405).json({ error: 'Method not allowed' })\n }\n const authorization = String(req.headers.authorization ?? '')\n const idToken = authorization.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : undefined\n if (!idToken) return res.status(401).json({ error: 'Unauthenticated' })\n const body =\n req.method === 'POST'\n ? typeof req.body === 'string'\n ? JSON.parse(req.body || '{}')\n : (req.body ?? {})\n : {}\n const hostId = String(\n (req.method === 'GET' ? req.query.hostId : body.hostId) ?? '',\n )\n if (!hostId || /^__.*__$/.test(hostId)) {\n return res.status(400).json({ error: 'Missing hostId' })\n }\n try {\n const decoded = await firebaseAdmin.app().auth().verifyIdToken(idToken)\n const hostSnapshot = await firebaseAdmin\n .app()\n .firestore()\n .collection('hosts')\n .doc(hostId)\n .get()\n if (!hostSnapshot.exists) {\n return res.status(404).json({ error: 'Unknown site' })\n }\n const memberRole = (hostSnapshot.get('memberRoles') ?? {})[decoded.uid]\n if (memberRole !== 'admin' && memberRole !== 'editor') {\n return res.status(403).json({ error: 'Not permitted' })\n }\n if (req.method === 'GET') {\n return res\n .status(200)\n .json({ email: isEmailConfigured(), sms: pluginSmsAvailable() })\n }\n\n const orderId = String(body.orderId ?? '')\n const channel = String(body.channel ?? 'email')\n const to = String(body.to ?? '').trim().slice(0, 200)\n if (!orderId || /^__.*__$/.test(orderId)) {\n return res.status(400).json({ error: 'Missing orderId' })\n }\n if (channel !== 'email' && channel !== 'sms') {\n return res.status(400).json({ error: 'channel must be email or sms' })\n }\n if (!to) {\n return res.status(400).json({\n error: channel === 'sms' ? 'Enter a phone number' : 'Enter an email address',\n })\n }\n const [member, order] = await Promise.all([\n consumeRateLimit(`commerce-receipt-send:uid:${decoded.uid}`, {\n limit: RECEIPT_RESENDS_PER_MINUTE_PER_MEMBER,\n windowMs: 60_000,\n }),\n consumeRateLimit(`commerce-receipt-send:order:${hostId}:${orderId}`, {\n limit: RECEIPT_RESENDS_PER_HOUR_PER_ORDER,\n windowMs: 60 * 60_000,\n }),\n ])\n if (!member.allowed || !order.allowed) {\n const resetMs = Math.max(\n member.allowed ? 0 : member.resetMs,\n order.allowed ? 0 : order.resetMs,\n )\n res.setHeader(\n 'Retry-After',\n String(Math.max(1, Math.ceil((resetMs - Date.now()) / 1000))),\n )\n return res.status(429).json({\n error: order.allowed\n ? 'Too many receipts sent — wait a minute and try again.'\n : 'This receipt was re-sent several times in the last hour — try again later.',\n })\n }\n const outcome = await sendOrderReceipt(\n { hostId, orderId },\n { channel, to },\n )\n switch (outcome.outcome) {\n case 'sent':\n return res.status(200).json({ ok: true, channel })\n case 'no_such_order':\n return res.status(404).json({ error: 'Unknown order' })\n case 'not_configured':\n return res.status(409).json({\n error:\n channel === 'sms'\n ? 'Text messages are not set up on this platform.'\n : 'Email is not set up on this platform.',\n })\n case 'invalid_recipient':\n return res.status(400).json({\n error:\n channel === 'sms'\n ? 'That phone number could not be read.'\n : 'That email address could not be read.',\n })\n default:\n return res.status(502).json({\n error:\n outcome.error === 'suppressed'\n ? 'That number has opted out of texts.'\n : 'The receipt could not be sent. Try again.',\n })\n }\n } catch (error) {\n console.error('orderReceiptSend failed', error)\n return res.status(500).json({ error: 'The receipt could not be sent.' })\n }\n}\n"],"names":["pluginSmsAvailable","consumeRateLimit","firebaseAdmin","isEmailConfigured","sendOrderReceipt","RECEIPT_RESENDS_PER_MINUTE_PER_MEMBER","RECEIPT_RESENDS_PER_HOUR_PER_ORDER","orderReceiptSendHandler","req","res","method","status","json","error","authorization","String","headers","idToken","startsWith","slice","length","undefined","body","JSON","parse","hostId","query","test","hostSnapshot","decoded","app","auth","verifyIdToken","firestore","collection","doc","get","exists","memberRole","uid","email","sms","orderId","channel","to","trim","member","order","Promise","all","limit","windowMs","allowed","resetMs","Math","max","setHeader","ceil","Date","now","outcome","ok","console"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/** The token for one order, or `null` when the signing secret is unset. */
|
|
18
|
+
export declare function mintOrderStatusToken(hostId: string, orderId: string): string | null;
|
|
19
|
+
/** Constant-time check of a presented token. False when the secret is unset. */
|
|
20
|
+
export declare function verifyOrderStatusToken(hostId: string, orderId: string, token: string): boolean;
|
|
21
|
+
/** The path the commerce page resolver serves the status page at. */
|
|
22
|
+
export declare const ORDER_STATUS_PATH = "order-status";
|
|
23
|
+
/**
|
|
24
|
+
* The absolute status link for an order, or `null` when either half is
|
|
25
|
+
* missing: a site with no public address, or no signing secret. Callers fall
|
|
26
|
+
* back to the store's home page rather than mail a broken link.
|
|
27
|
+
*/
|
|
28
|
+
export declare function orderStatusUrl(host: {
|
|
29
|
+
cname?: unknown;
|
|
30
|
+
subdomain?: unknown;
|
|
31
|
+
} | null | undefined, hostId: string, orderId: string): string | null;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { hostPublicOrigin } from "@aglyn/aglyn/server";
|
|
17
|
+
import { createHmac, timingSafeEqual } from "crypto";
|
|
18
|
+
import { tokenSigningSecret } from "./download.js";
|
|
19
|
+
/**
|
|
20
|
+
* The guest order-status link (AGL-3610): `/order-status?o=<orderId>&t=<token>`
|
|
21
|
+
* on the store's own address, mailed in every buyer email so a guest can see
|
|
22
|
+
* their order without an account.
|
|
23
|
+
*
|
|
24
|
+
* The token is `HMAC(order-status:hostId:orderId)` under the commerce
|
|
25
|
+
* `TOKEN_SIGNING_SECRET` — the supplier-callback shape: a pure function of
|
|
26
|
+
* identifiers the order already has, so nothing is stored and every email ever
|
|
27
|
+
* sent for the order carries the same working link. The `order-status:` prefix
|
|
28
|
+
* keeps it from being replayed as a download or supplier token, which sign
|
|
29
|
+
* other payloads under the same secret.
|
|
30
|
+
*
|
|
31
|
+
* NO EXPIRY, deliberately. A buyer opens a shipping email weeks later to find
|
|
32
|
+
* a tracking number, and a link that has died by then is a support ticket.
|
|
33
|
+
* What the page shows is bounded to match: the order's own lines, totals,
|
|
34
|
+
* status and shipments — never the buyer's email, phone or full address — so
|
|
35
|
+
* a forwarded link discloses no more than the email it came in.
|
|
36
|
+
*/ const ORDER_STATUS_TOKEN_LENGTH = 32;
|
|
37
|
+
function signOrderStatus(hostId, orderId) {
|
|
38
|
+
return createHmac('sha256', tokenSigningSecret()).update(`order-status:${hostId}:${orderId}`).digest('hex').slice(0, ORDER_STATUS_TOKEN_LENGTH);
|
|
39
|
+
}
|
|
40
|
+
/** The token for one order, or `null` when the signing secret is unset. */ export function mintOrderStatusToken(hostId, orderId) {
|
|
41
|
+
try {
|
|
42
|
+
return signOrderStatus(hostId, orderId);
|
|
43
|
+
} catch (unused) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Constant-time check of a presented token. False when the secret is unset. */ export function verifyOrderStatusToken(hostId, orderId, token) {
|
|
48
|
+
if (!hostId || !orderId || !token) return false;
|
|
49
|
+
let expected;
|
|
50
|
+
try {
|
|
51
|
+
expected = signOrderStatus(hostId, orderId);
|
|
52
|
+
} catch (unused) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const a = Buffer.from(String(token));
|
|
56
|
+
const b = Buffer.from(expected);
|
|
57
|
+
if (a.length !== b.length) return false;
|
|
58
|
+
return timingSafeEqual(new Uint8Array(a), new Uint8Array(b));
|
|
59
|
+
}
|
|
60
|
+
/** The path the commerce page resolver serves the status page at. */ export const ORDER_STATUS_PATH = 'order-status';
|
|
61
|
+
/**
|
|
62
|
+
* The absolute status link for an order, or `null` when either half is
|
|
63
|
+
* missing: a site with no public address, or no signing secret. Callers fall
|
|
64
|
+
* back to the store's home page rather than mail a broken link.
|
|
65
|
+
*/ export function orderStatusUrl(host, hostId, orderId) {
|
|
66
|
+
const origin = hostPublicOrigin({
|
|
67
|
+
cname: typeof (host == null ? void 0 : host.cname) === 'string' ? host.cname : null,
|
|
68
|
+
subdomain: typeof (host == null ? void 0 : host.subdomain) === 'string' ? host.subdomain : null
|
|
69
|
+
});
|
|
70
|
+
const token = mintOrderStatusToken(hostId, orderId);
|
|
71
|
+
if (!origin || !token) return null;
|
|
72
|
+
return `${origin}/${ORDER_STATUS_PATH}?o=${encodeURIComponent(orderId)}` + `&t=${token}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=order-status-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/order-status-token.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 { hostPublicOrigin } from '@aglyn/aglyn/server'\nimport { createHmac, timingSafeEqual } from 'crypto'\nimport { tokenSigningSecret } from './download'\n\n/**\n * The guest order-status link (AGL-3610): `/order-status?o=<orderId>&t=<token>`\n * on the store's own address, mailed in every buyer email so a guest can see\n * their order without an account.\n *\n * The token is `HMAC(order-status:hostId:orderId)` under the commerce\n * `TOKEN_SIGNING_SECRET` — the supplier-callback shape: a pure function of\n * identifiers the order already has, so nothing is stored and every email ever\n * sent for the order carries the same working link. The `order-status:` prefix\n * keeps it from being replayed as a download or supplier token, which sign\n * other payloads under the same secret.\n *\n * NO EXPIRY, deliberately. A buyer opens a shipping email weeks later to find\n * a tracking number, and a link that has died by then is a support ticket.\n * What the page shows is bounded to match: the order's own lines, totals,\n * status and shipments — never the buyer's email, phone or full address — so\n * a forwarded link discloses no more than the email it came in.\n */\nconst ORDER_STATUS_TOKEN_LENGTH = 32\n\nfunction signOrderStatus(hostId: string, orderId: string): string {\n return createHmac('sha256', tokenSigningSecret())\n .update(`order-status:${hostId}:${orderId}`)\n .digest('hex')\n .slice(0, ORDER_STATUS_TOKEN_LENGTH)\n}\n\n/** The token for one order, or `null` when the signing secret is unset. */\nexport function mintOrderStatusToken(\n hostId: string,\n orderId: string,\n): string | null {\n try {\n return signOrderStatus(hostId, orderId)\n } catch {\n return null\n }\n}\n\n/** Constant-time check of a presented token. False when the secret is unset. */\nexport function verifyOrderStatusToken(\n hostId: string,\n orderId: string,\n token: string,\n): boolean {\n if (!hostId || !orderId || !token) return false\n let expected: string\n try {\n expected = signOrderStatus(hostId, orderId)\n } catch {\n return false\n }\n const a = Buffer.from(String(token))\n const b = Buffer.from(expected)\n if (a.length !== b.length) return false\n return timingSafeEqual(new Uint8Array(a), new Uint8Array(b))\n}\n\n/** The path the commerce page resolver serves the status page at. */\nexport const ORDER_STATUS_PATH = 'order-status'\n\n/**\n * The absolute status link for an order, or `null` when either half is\n * missing: a site with no public address, or no signing secret. Callers fall\n * back to the store's home page rather than mail a broken link.\n */\nexport function orderStatusUrl(\n host: { cname?: unknown; subdomain?: unknown } | null | undefined,\n hostId: string,\n orderId: string,\n): string | null {\n const origin = hostPublicOrigin({\n cname: typeof host?.cname === 'string' ? host.cname : null,\n subdomain: typeof host?.subdomain === 'string' ? host.subdomain : null,\n })\n const token = mintOrderStatusToken(hostId, orderId)\n if (!origin || !token) return null\n return (\n `${origin}/${ORDER_STATUS_PATH}?o=${encodeURIComponent(orderId)}` +\n `&t=${token}`\n )\n}\n"],"names":["hostPublicOrigin","createHmac","timingSafeEqual","tokenSigningSecret","ORDER_STATUS_TOKEN_LENGTH","signOrderStatus","hostId","orderId","update","digest","slice","mintOrderStatusToken","verifyOrderStatusToken","token","expected","a","Buffer","from","String","b","length","Uint8Array","ORDER_STATUS_PATH","orderStatusUrl","host","origin","cname","subdomain","encodeURIComponent"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,gBAAgB,QAAQ,sBAAqB;AACtD,SAASC,UAAU,EAAEC,eAAe,QAAQ,SAAQ;AACpD,SAASC,kBAAkB,QAAQ,gBAAY;AAE/C;;;;;;;;;;;;;;;;;CAiBC,GACD,MAAMC,4BAA4B;AAElC,SAASC,gBAAgBC,MAAc,EAAEC,OAAe;IACtD,OAAON,WAAW,UAAUE,sBACzBK,MAAM,CAAC,CAAC,aAAa,EAAEF,OAAO,CAAC,EAAEC,SAAS,EAC1CE,MAAM,CAAC,OACPC,KAAK,CAAC,GAAGN;AACd;AAEA,yEAAyE,GACzE,OAAO,SAASO,qBACdL,MAAc,EACdC,OAAe;IAEf,IAAI;QACF,OAAOF,gBAAgBC,QAAQC;IACjC,EAAE,eAAM;QACN,OAAO;IACT;AACF;AAEA,8EAA8E,GAC9E,OAAO,SAASK,uBACdN,MAAc,EACdC,OAAe,EACfM,KAAa;IAEb,IAAI,CAACP,UAAU,CAACC,WAAW,CAACM,OAAO,OAAO;IAC1C,IAAIC;IACJ,IAAI;QACFA,WAAWT,gBAAgBC,QAAQC;IACrC,EAAE,eAAM;QACN,OAAO;IACT;IACA,MAAMQ,IAAIC,OAAOC,IAAI,CAACC,OAAOL;IAC7B,MAAMM,IAAIH,OAAOC,IAAI,CAACH;IACtB,IAAIC,EAAEK,MAAM,KAAKD,EAAEC,MAAM,EAAE,OAAO;IAClC,OAAOlB,gBAAgB,IAAImB,WAAWN,IAAI,IAAIM,WAAWF;AAC3D;AAEA,mEAAmE,GACnE,OAAO,MAAMG,oBAAoB,eAAc;AAE/C;;;;CAIC,GACD,OAAO,SAASC,eACdC,IAAiE,EACjElB,MAAc,EACdC,OAAe;IAEf,MAAMkB,SAASzB,iBAAiB;QAC9B0B,OAAO,QAAOF,wBAAAA,KAAME,KAAK,MAAK,WAAWF,KAAKE,KAAK,GAAG;QACtDC,WAAW,QAAOH,wBAAAA,KAAMG,SAAS,MAAK,WAAWH,KAAKG,SAAS,GAAG;IACpE;IACA,MAAMd,QAAQF,qBAAqBL,QAAQC;IAC3C,IAAI,CAACkB,UAAU,CAACZ,OAAO,OAAO;IAC9B,OACE,GAAGY,OAAO,CAAC,EAAEH,kBAAkB,GAAG,EAAEM,mBAAmBrB,UAAU,GACjE,CAAC,GAAG,EAAEM,OAAO;AAEjB"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import { type PluginApiHandler } from '@aglyn/aglyn/server';
|
|
18
|
+
import * as CommerceModel from '../model';
|
|
19
|
+
/** How long the page waits for a tracking service's own page link (AGL-3635). */
|
|
20
|
+
export declare const TRACKING_PAGE_TIMEOUT_MS = 1500;
|
|
21
|
+
/** Status lookups one address may make per site, per ten minutes. */
|
|
22
|
+
export declare const ORDER_STATUS_LOOKUPS_PER_WINDOW = 60;
|
|
23
|
+
export declare const ORDER_STATUS_WINDOW_MS: number;
|
|
24
|
+
/**
|
|
25
|
+
* Adds buyer actions to the status page (AGL-3610) — the typed extension
|
|
26
|
+
* point for "Request a return", which the order-processing work (AGL-3611)
|
|
27
|
+
* owns. A provider sees the verified order and answers the actions that apply
|
|
28
|
+
* to it (none, usually); the page draws each as a button to its `url`. Kept
|
|
29
|
+
* inside commerce: returns are this plugin's, so no cross-plugin seam is
|
|
30
|
+
* needed, and a provider that throws costs the page its buttons, not the page.
|
|
31
|
+
*/
|
|
32
|
+
export type OrderStatusActionsProvider = (input: {
|
|
33
|
+
hostId: string;
|
|
34
|
+
orderId: string;
|
|
35
|
+
order: CommerceModel.HostOrder;
|
|
36
|
+
/** The verified status token, for a provider building a signed follow-up link. */
|
|
37
|
+
token: string;
|
|
38
|
+
}) => CommerceModel.OrderStatusAction[] | Promise<CommerceModel.OrderStatusAction[]>;
|
|
39
|
+
export declare function registerOrderStatusActions(provider: OrderStatusActionsProvider): void;
|
|
40
|
+
/** Specs only. */
|
|
41
|
+
export declare function clearOrderStatusActions(): void;
|
|
42
|
+
/**
|
|
43
|
+
* The guest order-status page's data (AGL-3610): `GET
|
|
44
|
+
* /api/commerce/order-status?hostId=…&o=<orderId>&t=<token>` on the store.
|
|
45
|
+
*
|
|
46
|
+
* The token is the whole credential — `verifyOrderStatusToken`, constant
|
|
47
|
+
* time — and a wrong one answers exactly as a missing order does (404), so
|
|
48
|
+
* the route cannot be used to learn which order ids exist. Rate-limited per
|
|
49
|
+
* address per site, durable, BEFORE the token check so guessing costs the
|
|
50
|
+
* same as looking. The answer is `buildOrderStatusView`, an allow-listed
|
|
51
|
+
* projection, never the order document; `Cache-Control: private, no-store`
|
|
52
|
+
* and `X-Robots-Tag: noindex` keep it out of shared caches and search.
|
|
53
|
+
*
|
|
54
|
+
* Registered as a `recipientLink` route: an emailed link has to keep working
|
|
55
|
+
* for the buyer it was sent to, so the per-site enablement gates do not
|
|
56
|
+
* stand between them and their order.
|
|
57
|
+
*/
|
|
58
|
+
export declare const orderStatusHandler: PluginApiHandler;
|