@aglyn/plugins-commerce 1.0.0-beta.284 → 1.0.0-beta.287
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/README.md +1 -1
- package/package.json +13 -13
- package/src/lib/components/cart.js +25 -12
- package/src/lib/components/cart.js.map +1 -1
- package/src/lib/components/commerce-console-page.js +70 -13
- package/src/lib/components/commerce-console-page.js.map +1 -1
- package/src/lib/components/console/order-detail-dialog.component.js +2 -1
- package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
- package/src/lib/components/console/payment-methods-card.component.js +1 -1
- package/src/lib/components/console/payment-methods-card.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-cart-panel.component.d.ts +5 -0
- package/src/lib/components/console/pos/pos-cart-panel.component.js +18 -12
- package/src/lib/components/console/pos/pos-cart-panel.component.js.map +1 -1
- package/src/lib/components/console/pos/pos-hardware-api.d.ts +76 -5
- package/src/lib/components/console/pos/pos-hardware-api.js +53 -11
- package/src/lib/components/console/pos/pos-hardware-api.js.map +1 -1
- package/src/lib/components/console/pos/pos-hardware-buyer.component.d.ts +18 -0
- package/src/lib/components/console/pos/pos-hardware-buyer.component.js +629 -0
- package/src/lib/components/console/pos/pos-hardware-buyer.component.js.map +1 -0
- package/src/lib/components/console/pos/pos-hardware-sale-api.d.ts +162 -0
- package/src/lib/components/console/pos/pos-hardware-sale-api.js +122 -0
- package/src/lib/components/console/pos/pos-hardware-sale-api.js.map +1 -0
- package/src/lib/components/console/pos/pos-hardware-sale-terms.component.d.ts +39 -0
- package/src/lib/components/console/pos/pos-hardware-sale-terms.component.js +251 -0
- package/src/lib/components/console/pos/pos-hardware-sale-terms.component.js.map +1 -0
- package/src/lib/components/console/pos/use-pos-display.js +50 -0
- package/src/lib/components/console/pos/use-pos-display.js.map +1 -1
- package/src/lib/components/console/pos-cellular-control.component.d.ts +41 -0
- package/src/lib/components/console/pos-cellular-control.component.js +211 -0
- package/src/lib/components/console/pos-cellular-control.component.js.map +1 -0
- package/src/lib/components/console/pos-devices-card.component.d.ts +9 -1
- package/src/lib/components/console/pos-devices-card.component.js +47 -15
- package/src/lib/components/console/pos-devices-card.component.js.map +1 -1
- package/src/lib/components/console/pos-display/pos-display-api.d.ts +6 -0
- package/src/lib/components/console/pos-display/pos-display-api.js +8 -1
- package/src/lib/components/console/pos-display/pos-display-api.js.map +1 -1
- package/src/lib/components/console/pos-display-page.component.d.ts +13 -0
- package/src/lib/components/console/pos-display-page.component.js +44 -2
- package/src/lib/components/console/pos-display-page.component.js.map +1 -1
- package/src/lib/components/console/pos-hardware-card.component.d.ts +12 -4
- package/src/lib/components/console/pos-hardware-card.component.js +157 -36
- package/src/lib/components/console/pos-hardware-card.component.js.map +1 -1
- package/src/lib/components/console/pos-hardware-orders-staff-page.component.js +360 -80
- package/src/lib/components/console/pos-hardware-orders-staff-page.component.js.map +1 -1
- package/src/lib/components/console/pos-hardware-terms-page.component.d.ts +34 -0
- package/src/lib/components/console/pos-hardware-terms-page.component.js +257 -0
- package/src/lib/components/console/pos-hardware-terms-page.component.js.map +1 -0
- package/src/lib/components/console/pos-hardware-terms-sign-page.component.d.ts +27 -0
- package/src/lib/components/console/pos-hardware-terms-sign-page.component.js +104 -0
- package/src/lib/components/console/pos-hardware-terms-sign-page.component.js.map +1 -0
- package/src/lib/components/console/pos-hardware-waiting-card.component.d.ts +24 -0
- package/src/lib/components/console/pos-hardware-waiting-card.component.js +205 -0
- package/src/lib/components/console/pos-hardware-waiting-card.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js +297 -201
- package/src/lib/components/console/pos-ops/pos-check-in-dialog.component.js.map +1 -1
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.d.ts +3 -0
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js +260 -92
- package/src/lib/components/console/pos-ops/pos-check-ins-card.component.js.map +1 -1
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.d.ts +9 -0
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js +15 -6
- package/src/lib/components/console/pos-ops/pos-operations-bar.component.js.map +1 -1
- package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.d.ts +25 -0
- package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.js +136 -0
- package/src/lib/components/console/pos-ops/pos-rain-check-dialog.component.js.map +1 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.d.ts +6 -0
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.js +48 -31
- package/src/lib/components/console/pos-ops/pos-shift-panel.component.js.map +1 -1
- package/src/lib/components/console/pos-page.component.d.ts +1 -1
- package/src/lib/components/console/pos-page.component.js +63 -12
- package/src/lib/components/console/pos-page.component.js.map +1 -1
- package/src/lib/components/console/pos-reader-splash-setting.component.js +1 -1
- package/src/lib/components/console/pos-reader-splash-setting.component.js.map +1 -1
- package/src/lib/components/console/product-check-in-fields.component.d.ts +4 -2
- package/src/lib/components/console/product-check-in-fields.component.js +60 -14
- package/src/lib/components/console/product-check-in-fields.component.js.map +1 -1
- package/src/lib/components/console/product-editor-dialog.component.js +44 -1
- package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
- package/src/lib/components/console/product-hardware-sku-field.component.d.ts +19 -0
- package/src/lib/components/console/product-hardware-sku-field.component.js +92 -0
- package/src/lib/components/console/product-hardware-sku-field.component.js.map +1 -0
- package/src/lib/components/console/product-kiosk-offer-field.component.d.ts +22 -0
- package/src/lib/components/console/product-kiosk-offer-field.component.js +94 -0
- package/src/lib/components/console/product-kiosk-offer-field.component.js.map +1 -0
- package/src/lib/components/console/product-register-fields.component.d.ts +4 -0
- package/src/lib/components/console/product-register-fields.component.js +31 -2
- package/src/lib/components/console/product-register-fields.component.js.map +1 -1
- package/src/lib/components/console/products-hub-card.component.js +11 -2
- package/src/lib/components/console/products-hub-card.component.js.map +1 -1
- package/src/lib/components/console/register-hardware-card.component.d.ts +11 -0
- package/src/lib/components/console/register-hardware-card.component.js +56 -27
- package/src/lib/components/console/register-hardware-card.component.js.map +1 -1
- package/src/lib/components/console/reservation-guests-dialog.component.d.ts +28 -0
- package/src/lib/components/console/reservation-guests-dialog.component.js +189 -0
- package/src/lib/components/console/reservation-guests-dialog.component.js.map +1 -0
- package/src/lib/components/console/reservations-card.component.d.ts +9 -0
- package/src/lib/components/console/reservations-card.component.js +158 -23
- package/src/lib/components/console/reservations-card.component.js.map +1 -1
- package/src/lib/components/console/shippingeasy-card.component.js +1 -1
- package/src/lib/components/console/shippingeasy-card.component.js.map +1 -1
- package/src/lib/components/console/shipstation-card.component.d.ts +1 -1
- package/src/lib/components/console/shipstation-card.component.js +3 -3
- package/src/lib/components/console/shipstation-card.component.js.map +1 -1
- package/src/lib/components/product-detail.d.ts +4 -0
- package/src/lib/components/product-detail.js +40 -5
- package/src/lib/components/product-detail.js.map +1 -1
- package/src/lib/components/product-reviews.js +28 -11
- package/src/lib/components/product-reviews.js.map +1 -1
- package/src/lib/components/reservation-widget.js +28 -5
- package/src/lib/components/reservation-widget.js.map +1 -1
- package/src/lib/components/use-day-picker.d.ts +25 -0
- package/src/lib/components/use-day-picker.js +57 -0
- package/src/lib/components/use-day-picker.js.map +1 -0
- package/src/lib/constants/operator-alerts.d.ts +55 -0
- package/src/lib/constants/operator-alerts.js +156 -1
- package/src/lib/constants/operator-alerts.js.map +1 -1
- package/src/lib/declarations.console-server.js +54 -1
- package/src/lib/declarations.console-server.js.map +1 -1
- package/src/lib/declarations.d.ts +4 -2
- package/src/lib/declarations.js +11 -4
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/declarations.server.js +9 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/model/commerce-cart.d.ts +9 -1
- package/src/lib/model/commerce-cart.js +4 -3
- package/src/lib/model/commerce-cart.js.map +1 -1
- package/src/lib/model/commerce-check-in-product.d.ts +26 -0
- package/src/lib/model/commerce-check-in-product.js +64 -2
- package/src/lib/model/commerce-check-in-product.js.map +1 -1
- package/src/lib/model/commerce-check-in-record.d.ts +127 -0
- package/src/lib/model/commerce-check-in-record.js +36 -0
- package/src/lib/model/commerce-check-in-record.js.map +1 -0
- package/src/lib/model/commerce-check-in.d.ts +91 -87
- package/src/lib/model/commerce-check-in.js +186 -62
- package/src/lib/model/commerce-check-in.js.map +1 -1
- package/src/lib/model/commerce-kiosk-offer.d.ts +31 -0
- package/src/lib/model/commerce-kiosk-offer.js +35 -0
- package/src/lib/model/commerce-kiosk-offer.js.map +1 -0
- package/src/lib/model/commerce-orders.d.ts +76 -0
- package/src/lib/model/commerce-orders.js +6 -0
- package/src/lib/model/commerce-orders.js.map +1 -1
- package/src/lib/model/commerce-pos-day-report.d.ts +54 -1
- package/src/lib/model/commerce-pos-day-report.js +39 -2
- package/src/lib/model/commerce-pos-day-report.js.map +1 -1
- package/src/lib/model/commerce-pos-offline.d.ts +1 -1
- package/src/lib/model/commerce-pos-offline.js +6 -1
- package/src/lib/model/commerce-pos-offline.js.map +1 -1
- package/src/lib/model/commerce-pos-ops.js +3 -2
- package/src/lib/model/commerce-pos-ops.js.map +1 -1
- package/src/lib/model/commerce-pos.d.ts +36 -3
- package/src/lib/model/commerce-pos.js +14 -6
- package/src/lib/model/commerce-pos.js.map +1 -1
- package/src/lib/model/commerce-receipt.js +3 -2
- package/src/lib/model/commerce-receipt.js.map +1 -1
- package/src/lib/model/commerce-reservations.d.ts +69 -4
- package/src/lib/model/commerce-reservations.js +57 -5
- package/src/lib/model/commerce-reservations.js.map +1 -1
- package/src/lib/model/commerce.d.ts +66 -0
- package/src/lib/model/commerce.js +15 -0
- package/src/lib/model/commerce.js.map +1 -1
- package/src/lib/model/order-list-fields.d.ts +11 -0
- package/src/lib/model/order-list-fields.js +17 -1
- package/src/lib/model/order-list-fields.js.map +1 -1
- package/src/lib/model/own-store-tax-summary.d.ts +152 -0
- package/src/lib/model/own-store-tax-summary.js +288 -0
- package/src/lib/model/own-store-tax-summary.js.map +1 -0
- package/src/lib/model/plugin-permissions.d.ts +67 -0
- package/src/lib/model/plugin-permissions.js +224 -7
- package/src/lib/model/plugin-permissions.js.map +1 -1
- package/src/lib/model/pos-cellular.d.ts +223 -0
- package/src/lib/model/pos-cellular.js +293 -0
- package/src/lib/model/pos-cellular.js.map +1 -0
- package/src/lib/model/pos-hardware-in-person.d.ts +98 -0
- package/src/lib/model/pos-hardware-in-person.js +224 -0
- package/src/lib/model/pos-hardware-in-person.js.map +1 -0
- package/src/lib/model/pos-hardware-tax-summary.d.ts +166 -0
- package/src/lib/model/pos-hardware-tax-summary.js +225 -0
- package/src/lib/model/pos-hardware-tax-summary.js.map +1 -0
- package/src/lib/model/pos-hardware-terms.d.ts +74 -0
- package/src/lib/model/pos-hardware-terms.js +115 -0
- package/src/lib/model/pos-hardware-terms.js.map +1 -0
- package/src/lib/model/pos-hardware.d.ts +349 -7
- package/src/lib/model/pos-hardware.js +291 -20
- package/src/lib/model/pos-hardware.js.map +1 -1
- package/src/lib/model/product-write.d.ts +10 -0
- package/src/lib/model/product-write.js +4 -2
- package/src/lib/model/product-write.js.map +1 -1
- package/src/lib/plugin.js +16 -0
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/server/billing-webhook.js +25 -1
- package/src/lib/server/billing-webhook.js.map +1 -1
- package/src/lib/server/cancel-order.js +7 -5
- package/src/lib/server/cancel-order.js.map +1 -1
- package/src/lib/server/cart-checkout.js +20 -8
- package/src/lib/server/cart-checkout.js.map +1 -1
- package/src/lib/server/cart.js +29 -0
- package/src/lib/server/cart.js.map +1 -1
- package/src/lib/server/checkout.js +42 -13
- package/src/lib/server/checkout.js.map +1 -1
- package/src/lib/server/collection-membership.d.ts +4 -0
- package/src/lib/server/collection-membership.js +5 -10
- package/src/lib/server/collection-membership.js.map +1 -1
- package/src/lib/server/commerce-site-keys.d.ts +29 -0
- package/src/lib/server/commerce-site-keys.js +40 -0
- package/src/lib/server/commerce-site-keys.js.map +1 -0
- package/src/lib/server/draft-order.js +8 -5
- package/src/lib/server/draft-order.js.map +1 -1
- package/src/lib/server/fulfill-order.js +7 -5
- package/src/lib/server/fulfill-order.js.map +1 -1
- package/src/lib/server/gift-cards.js +8 -5
- package/src/lib/server/gift-cards.js.map +1 -1
- package/src/lib/server/local-fulfillment-status.js +9 -6
- package/src/lib/server/local-fulfillment-status.js.map +1 -1
- package/src/lib/server/member-post.js +6 -5
- package/src/lib/server/member-post.js.map +1 -1
- package/src/lib/server/membership-admin-password.d.ts +4 -2
- package/src/lib/server/membership-admin-password.js +10 -8
- package/src/lib/server/membership-admin-password.js.map +1 -1
- package/src/lib/server/membership-admin-remove.d.ts +7 -5
- package/src/lib/server/membership-admin-remove.js +9 -9
- package/src/lib/server/membership-admin-remove.js.map +1 -1
- package/src/lib/server/order-notifications.js +5 -2
- package/src/lib/server/order-notifications.js.map +1 -1
- package/src/lib/server/order-print.js +7 -5
- package/src/lib/server/order-print.js.map +1 -1
- package/src/lib/server/order-receipt-send.js +8 -5
- package/src/lib/server/order-receipt-send.js.map +1 -1
- package/src/lib/server/orders-shipping-prepare.js +9 -6
- package/src/lib/server/orders-shipping-prepare.js.map +1 -1
- package/src/lib/server/own-store-tax-return-source.d.ts +47 -0
- package/src/lib/server/own-store-tax-return-source.js +312 -0
- package/src/lib/server/own-store-tax-return-source.js.map +1 -0
- package/src/lib/server/payment-methods.js +13 -9
- package/src/lib/server/payment-methods.js.map +1 -1
- package/src/lib/server/pos-auth.d.ts +37 -6
- package/src/lib/server/pos-auth.js +40 -15
- package/src/lib/server/pos-auth.js.map +1 -1
- package/src/lib/server/pos-cellular-billing.d.ts +81 -0
- package/src/lib/server/pos-cellular-billing.js +711 -0
- package/src/lib/server/pos-cellular-billing.js.map +1 -0
- package/src/lib/server/pos-cellular-notify.d.ts +47 -0
- package/src/lib/server/pos-cellular-notify.js +150 -0
- package/src/lib/server/pos-cellular-notify.js.map +1 -0
- package/src/lib/server/pos-cellular.d.ts +88 -0
- package/src/lib/server/pos-cellular.js +339 -0
- package/src/lib/server/pos-cellular.js.map +1 -0
- package/src/lib/server/pos-check-in.d.ts +5 -1
- package/src/lib/server/pos-check-in.js +296 -28
- package/src/lib/server/pos-check-in.js.map +1 -1
- package/src/lib/server/pos-day-report.js +29 -3
- package/src/lib/server/pos-day-report.js.map +1 -1
- package/src/lib/server/pos-display.d.ts +16 -0
- package/src/lib/server/pos-display.js +129 -2
- package/src/lib/server/pos-display.js.map +1 -1
- package/src/lib/server/pos-hardware-admin.d.ts +10 -1
- package/src/lib/server/pos-hardware-admin.js +250 -84
- package/src/lib/server/pos-hardware-admin.js.map +1 -1
- package/src/lib/server/pos-hardware-attach.d.ts +105 -0
- package/src/lib/server/pos-hardware-attach.js +443 -0
- package/src/lib/server/pos-hardware-attach.js.map +1 -0
- package/src/lib/server/pos-hardware-buyer-lookup.d.ts +58 -0
- package/src/lib/server/pos-hardware-buyer-lookup.js +180 -0
- package/src/lib/server/pos-hardware-buyer-lookup.js.map +1 -0
- package/src/lib/server/pos-hardware-buyer-record.d.ts +29 -0
- package/src/lib/server/pos-hardware-buyer-record.js +86 -0
- package/src/lib/server/pos-hardware-buyer-record.js.map +1 -0
- package/src/lib/server/pos-hardware-cancel.d.ts +58 -0
- package/src/lib/server/pos-hardware-cancel.js +147 -0
- package/src/lib/server/pos-hardware-cancel.js.map +1 -0
- package/src/lib/server/pos-hardware-closure-notices.d.ts +27 -0
- package/src/lib/server/pos-hardware-closure-notices.js +85 -0
- package/src/lib/server/pos-hardware-closure-notices.js.map +1 -0
- package/src/lib/server/pos-hardware-closure.d.ts +60 -0
- package/src/lib/server/pos-hardware-closure.js +153 -0
- package/src/lib/server/pos-hardware-closure.js.map +1 -0
- package/src/lib/server/pos-hardware-in-person.d.ts +61 -0
- package/src/lib/server/pos-hardware-in-person.js +236 -0
- package/src/lib/server/pos-hardware-in-person.js.map +1 -0
- package/src/lib/server/pos-hardware-notify.d.ts +40 -2
- package/src/lib/server/pos-hardware-notify.js +195 -14
- package/src/lib/server/pos-hardware-notify.js.map +1 -1
- package/src/lib/server/pos-hardware-readers.d.ts +115 -0
- package/src/lib/server/pos-hardware-readers.js +430 -0
- package/src/lib/server/pos-hardware-readers.js.map +1 -0
- package/src/lib/server/pos-hardware-returns.d.ts +116 -0
- package/src/lib/server/pos-hardware-returns.js +502 -0
- package/src/lib/server/pos-hardware-returns.js.map +1 -0
- package/src/lib/server/pos-hardware-sale-fields.d.ts +28 -0
- package/src/lib/server/pos-hardware-sale-fields.js +44 -0
- package/src/lib/server/pos-hardware-sale-fields.js.map +1 -0
- package/src/lib/server/pos-hardware-sale.d.ts +54 -0
- package/src/lib/server/pos-hardware-sale.js +369 -0
- package/src/lib/server/pos-hardware-sale.js.map +1 -0
- package/src/lib/server/pos-hardware-tax-return-source.d.ts +99 -0
- package/src/lib/server/pos-hardware-tax-return-source.js +462 -0
- package/src/lib/server/pos-hardware-tax-return-source.js.map +1 -0
- package/src/lib/server/pos-hardware-terms.d.ts +64 -0
- package/src/lib/server/pos-hardware-terms.js +154 -0
- package/src/lib/server/pos-hardware-terms.js.map +1 -0
- package/src/lib/server/pos-hardware-user-eraser.d.ts +3 -0
- package/src/lib/server/pos-hardware-user-eraser.js +31 -7
- package/src/lib/server/pos-hardware-user-eraser.js.map +1 -1
- package/src/lib/server/pos-hardware-webhook.d.ts +5 -0
- package/src/lib/server/pos-hardware-webhook.js +51 -6
- package/src/lib/server/pos-hardware-webhook.js.map +1 -1
- package/src/lib/server/pos-hardware.d.ts +74 -1
- package/src/lib/server/pos-hardware.js +175 -10
- package/src/lib/server/pos-hardware.js.map +1 -1
- package/src/lib/server/pos-kiosk.d.ts +2 -0
- package/src/lib/server/pos-kiosk.js +17 -12
- package/src/lib/server/pos-kiosk.js.map +1 -1
- package/src/lib/server/pos-offline-card-order.d.ts +64 -0
- package/src/lib/server/pos-offline-card-order.js +102 -0
- package/src/lib/server/pos-offline-card-order.js.map +1 -0
- package/src/lib/server/pos-offline-card-reconcile.d.ts +67 -1
- package/src/lib/server/pos-offline-card-reconcile.js +215 -18
- package/src/lib/server/pos-offline-card-reconcile.js.map +1 -1
- package/src/lib/server/pos-offline-sync.d.ts +39 -0
- package/src/lib/server/pos-offline-sync.js +190 -55
- package/src/lib/server/pos-offline-sync.js.map +1 -1
- package/src/lib/server/pos-ops-gate.d.ts +15 -4
- package/src/lib/server/pos-ops-gate.js +42 -9
- package/src/lib/server/pos-ops-gate.js.map +1 -1
- package/src/lib/server/pos-ops-routes.js +24 -0
- package/src/lib/server/pos-ops-routes.js.map +1 -1
- package/src/lib/server/pos-order-refund-ids.d.ts +28 -0
- package/src/lib/server/pos-order-refund-ids.js +31 -0
- package/src/lib/server/pos-order-refund-ids.js.map +1 -0
- package/src/lib/server/pos-order.js +52 -49
- package/src/lib/server/pos-order.js.map +1 -1
- package/src/lib/server/pos-payment.js +1 -1
- package/src/lib/server/pos-payment.js.map +1 -1
- package/src/lib/server/pos-reader-splash-admin.js +30 -32
- package/src/lib/server/pos-reader-splash-admin.js.map +1 -1
- package/src/lib/server/pos-reader-splash-images-route.d.ts +17 -0
- package/src/lib/server/pos-reader-splash-images-route.js +113 -0
- package/src/lib/server/pos-reader-splash-images-route.js.map +1 -0
- package/src/lib/server/pos-reader-splash-images.d.ts +51 -0
- package/src/lib/server/pos-reader-splash-images.js +123 -0
- package/src/lib/server/pos-reader-splash-images.js.map +1 -0
- package/src/lib/server/pos-reader-splash.d.ts +68 -1
- package/src/lib/server/pos-reader-splash.js +182 -11
- package/src/lib/server/pos-reader-splash.js.map +1 -1
- package/src/lib/server/pos-readers.d.ts +29 -0
- package/src/lib/server/pos-readers.js +134 -35
- package/src/lib/server/pos-readers.js.map +1 -1
- package/src/lib/server/pos-return.d.ts +10 -0
- package/src/lib/server/pos-return.js +26 -1
- package/src/lib/server/pos-return.js.map +1 -1
- package/src/lib/server/pos-sale.js +6 -1
- package/src/lib/server/pos-sale.js.map +1 -1
- package/src/lib/server/pos-shift.js +11 -0
- package/src/lib/server/pos-shift.js.map +1 -1
- package/src/lib/server/pos-staff-pin.js +25 -20
- package/src/lib/server/pos-staff-pin.js.map +1 -1
- package/src/lib/server/pos-terminal.d.ts +14 -4
- package/src/lib/server/pos-terminal.js +34 -38
- package/src/lib/server/pos-terminal.js.map +1 -1
- package/src/lib/server/printers.d.ts +7 -3
- package/src/lib/server/printers.js +18 -12
- package/src/lib/server/printers.js.map +1 -1
- package/src/lib/server/product-drafts.d.ts +11 -1
- package/src/lib/server/product-drafts.js +12 -8
- package/src/lib/server/product-drafts.js.map +1 -1
- package/src/lib/server/product.d.ts +15 -1
- package/src/lib/server/product.js +16 -3
- package/src/lib/server/product.js.map +1 -1
- package/src/lib/server/products-write.d.ts +26 -2
- package/src/lib/server/products-write.js +19 -7
- package/src/lib/server/products-write.js.map +1 -1
- package/src/lib/server/refund.js +34 -11
- package/src/lib/server/refund.js.map +1 -1
- package/src/lib/server/reservation-availability-query.d.ts +8 -0
- package/src/lib/server/reservation-availability-query.js +11 -1
- package/src/lib/server/reservation-availability-query.js.map +1 -1
- package/src/lib/server/reservation-availability.d.ts +1 -1
- package/src/lib/server/reservation-availability.js +7 -2
- package/src/lib/server/reservation-availability.js.map +1 -1
- package/src/lib/server/reservation-guests.d.ts +32 -0
- package/src/lib/server/reservation-guests.js +285 -0
- package/src/lib/server/reservation-guests.js.map +1 -0
- package/src/lib/server/reservation-walk-in.d.ts +11 -1
- package/src/lib/server/reservation-walk-in.js +56 -12
- package/src/lib/server/reservation-walk-in.js.map +1 -1
- package/src/lib/server/reserve.js +18 -5
- package/src/lib/server/reserve.js.map +1 -1
- package/src/lib/server/returns.js +9 -6
- package/src/lib/server/returns.js.map +1 -1
- package/src/lib/server/reviews.d.ts +22 -2
- package/src/lib/server/reviews.js +88 -20
- package/src/lib/server/reviews.js.map +1 -1
- package/src/lib/server/shipping-connectors.js +10 -11
- package/src/lib/server/shipping-connectors.js.map +1 -1
- package/src/lib/server/site-page-resolver.js +20 -3
- package/src/lib/server/site-page-resolver.js.map +1 -1
- package/src/lib/server/store-day.d.ts +33 -0
- package/src/lib/server/store-day.js +46 -0
- package/src/lib/server/store-day.js.map +1 -0
- package/src/lib/server/store-setup-ai-writer.d.ts +2 -1
- package/src/lib/server/store-setup-ai-writer.js +5 -6
- package/src/lib/server/store-setup-ai-writer.js.map +1 -1
- package/src/lib/server/storefront-tax.d.ts +13 -0
- package/src/lib/server/storefront-tax.js +8 -1
- package/src/lib/server/storefront-tax.js.map +1 -1
- package/src/lib/server/tax-return-source.d.ts +32 -3
- package/src/lib/server/tax-return-source.js +99 -20
- package/src/lib/server/tax-return-source.js.map +1 -1
- package/src/lib/server.js +100 -2
- package/src/lib/server.js.map +1 -1
- package/src/lib/testing/fake-firestore.js +58 -16
- package/src/lib/testing/fake-firestore.js.map +1 -1
- package/src/lib/testing/pos-ops-memory-firestore.js +18 -4
- package/src/lib/testing/pos-ops-memory-firestore.js.map +1 -1
- package/src/lib/testing/site-register-permissions.d.ts +29 -0
- package/src/lib/testing/site-register-permissions.js +38 -0
- package/src/lib/testing/site-register-permissions.js.map +1 -0
- package/src/lib/transfer/tracking.server.js +8 -5
- package/src/lib/transfer/tracking.server.js.map +1 -1
- package/src/lib/utils/add-to-cart.d.ts +2 -0
- package/src/lib/utils/add-to-cart.js +6 -3
- package/src/lib/utils/add-to-cart.js.map +1 -1
|
@@ -14,9 +14,18 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
|
-
import type { BillingWebhookHandler } from '@aglyn/aglyn/server';
|
|
17
|
+
import type { BillingWebhookHandler, PluginJobHostGate } from '@aglyn/aglyn/server';
|
|
18
|
+
/** The subcollection, under `hosts/{hostId}/terminal/config`, and its collection-group name. */
|
|
19
|
+
export declare const POS_OFFLINE_CARD_CHARGES = "offlineCardCharges";
|
|
20
|
+
/**
|
|
21
|
+
* How long an intent that holds money may wait for its sale before the sweep
|
|
22
|
+
* says so: six hours, which leaves well over a day of Stripe's two-day
|
|
23
|
+
* authorization to sync it, and is far past the seconds a healthy sync takes.
|
|
24
|
+
*/
|
|
25
|
+
export declare const POS_OFFLINE_CARD_UNSYNCED_ALERT_MS: number;
|
|
18
26
|
/** What the site keeps about one offline card's PaymentIntent. */
|
|
19
27
|
export interface PosOfflineCardChargeRecord {
|
|
28
|
+
hostId: string;
|
|
20
29
|
saleKey: string;
|
|
21
30
|
paymentIntentId: string;
|
|
22
31
|
status: string;
|
|
@@ -25,9 +34,18 @@ export interface PosOfflineCardChargeRecord {
|
|
|
25
34
|
livemode: boolean;
|
|
26
35
|
/** Whether the sale has synced (its order exists). */
|
|
27
36
|
synced: boolean;
|
|
37
|
+
orderId?: string;
|
|
38
|
+
syncedAtMs?: number;
|
|
28
39
|
lastEvent: string;
|
|
29
40
|
updatedAtMs: number;
|
|
41
|
+
/** When the sweep says the sale never synced; absent once synced or told. */
|
|
42
|
+
alertDueAtMs?: number;
|
|
43
|
+
alertedAtMs?: number;
|
|
44
|
+
/** The capture's idempotency attempt, raised only after a definite failure. */
|
|
45
|
+
captureAttempt?: number;
|
|
30
46
|
}
|
|
47
|
+
/** `hosts/{hostId}/terminal/config/offlineCardCharges/{saleKey}`. */
|
|
48
|
+
export declare function posOfflineCardChargeRef(hostRef: FirebaseFirestore.DocumentReference, saleKey: string): FirebaseFirestore.DocumentReference;
|
|
31
49
|
export declare function reconcilePosOfflineCardEvent(input: {
|
|
32
50
|
type: string;
|
|
33
51
|
object: any;
|
|
@@ -36,3 +54,51 @@ export declare function reconcilePosOfflineCardEvent(input: {
|
|
|
36
54
|
} | null>;
|
|
37
55
|
/** The platform webhook's handler for offline card intents: claims only its own. */
|
|
38
56
|
export declare const posOfflineCardBillingWebhookHandler: BillingWebhookHandler;
|
|
57
|
+
export interface UnsyncedOfflineCardReport {
|
|
58
|
+
hostId: string;
|
|
59
|
+
saleKey: string;
|
|
60
|
+
paymentIntentId: string;
|
|
61
|
+
status: string;
|
|
62
|
+
amountCents: number;
|
|
63
|
+
registerId: string;
|
|
64
|
+
/** How long ago the intent was first seen waiting, in ms. */
|
|
65
|
+
waitedMs: number;
|
|
66
|
+
}
|
|
67
|
+
export interface UnsyncedOfflineCardScan {
|
|
68
|
+
due: number;
|
|
69
|
+
alerted: number;
|
|
70
|
+
/** Synced, or no longer holding money, since the alert was set. */
|
|
71
|
+
cleared: number;
|
|
72
|
+
skippedLocked: number;
|
|
73
|
+
}
|
|
74
|
+
/** Tells the operator and the site's managers about one card. Never throws. */
|
|
75
|
+
export declare function reportUnsyncedOfflineCard(report: UnsyncedOfflineCardReport): Promise<void>;
|
|
76
|
+
/** Tells the operator a card was made out to an account that is not the store's. Never throws. */
|
|
77
|
+
export declare function reportPosOfflineCardWrongAccount(input: {
|
|
78
|
+
hostId: string;
|
|
79
|
+
orderId: string;
|
|
80
|
+
paymentIntentId: string;
|
|
81
|
+
status: string;
|
|
82
|
+
amountCents: number;
|
|
83
|
+
destination: string;
|
|
84
|
+
expected: string;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* The hourly sweep (registered in `server.ts`): every offline card record
|
|
88
|
+
* whose alert is due. Re-checks the sale first, so a sale that synced while
|
|
89
|
+
* the event was in flight clears the record and tells nobody.
|
|
90
|
+
*
|
|
91
|
+
* One collection-group range query on `alertDueAtMs`, which is set only on a
|
|
92
|
+
* record that holds money and has not synced, so the ordinary beat reads an
|
|
93
|
+
* empty result. Its COLLECTION_GROUP index is declared in
|
|
94
|
+
* `cloud/firebase-firestore.indexes.json` and pinned by
|
|
95
|
+
* `cron-scan-indexes.spec.ts`.
|
|
96
|
+
*/
|
|
97
|
+
export declare function scanUnsyncedOfflineCards(
|
|
98
|
+
/** The lockdown gate, injected by the runner (AGL-2495). */
|
|
99
|
+
gate: PluginJobHostGate, options?: {
|
|
100
|
+
firestore?: FirebaseFirestore.Firestore;
|
|
101
|
+
nowMs?: number;
|
|
102
|
+
limit?: number;
|
|
103
|
+
report?: (report: UnsyncedOfflineCardReport) => Promise<void>;
|
|
104
|
+
}): Promise<UnsyncedOfflineCardScan>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
1
2
|
/**
|
|
2
3
|
* @license
|
|
3
4
|
* Copyright 2026 Aglyn LLC
|
|
@@ -13,32 +14,102 @@
|
|
|
13
14
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
15
|
* See the License for the specific language governing permissions and
|
|
15
16
|
* limitations under the License.
|
|
16
|
-
*/ import {
|
|
17
|
+
*/ import { formatOperatorAlertAmount } from "@aglyn/aglyn/app-utils/operator-alerts";
|
|
18
|
+
import { firebaseAdmin, hostDisplayName, notifyHostManagers, raiseOperatorAlert } from "@aglyn/tenant-data-admin";
|
|
19
|
+
import { FieldValue } from "firebase-admin/firestore";
|
|
20
|
+
import { COMMERCE_POS_OFFLINE_CARD_UNSYNCED, COMMERCE_POS_OFFLINE_CARD_WRONG_ACCOUNT } from "../constants/operator-alerts.js";
|
|
17
21
|
import { POS_OFFLINE_CARD_INTENT_KIND, posOfflineSaleKeyIsValid } from "../model/commerce-pos-offline.js";
|
|
22
|
+
/*==========================================
|
|
23
|
+
* A CARD TAKEN OFFLINE, AS STRIPE REPORTS IT (AGL-4039).
|
|
24
|
+
*
|
|
25
|
+
* The Aglyn POS app creates the PaymentIntent of a card it takes offline, so
|
|
26
|
+
* Stripe can know of a charge before the sale itself reaches the server: the
|
|
27
|
+
* SDK forwards the card the moment a connection returns, and the sale syncs
|
|
28
|
+
* right after, from the same device. Usually seconds apart. If the sale never
|
|
29
|
+
* arrives (the device was lost or wiped between the two, or it waits for a
|
|
30
|
+
* member who never signs in again), the charge must not be invisible: each
|
|
31
|
+
* event on such an intent is kept on the site, by the sale's key, under the
|
|
32
|
+
* site's server-only Terminal document
|
|
33
|
+
* (`terminal/config/offlineCardCharges/{saleKey}`), beside whether the sale
|
|
34
|
+
* has synced. The sync itself never depends on this: it reads Stripe.
|
|
35
|
+
*
|
|
36
|
+
* WHO SAYS "SYNCED". Both writers, in transactions, so neither can undo the
|
|
37
|
+
* other: the sync marks the record when it creates the order, and an event
|
|
38
|
+
* reads the record and the sync record together. An event that arrives after
|
|
39
|
+
* the sync therefore keeps `synced`, and one that raced it retries and sees
|
|
40
|
+
* it.
|
|
41
|
+
*
|
|
42
|
+
* NOBODY LOOKING. An intent that holds or has taken money while its sale has
|
|
43
|
+
* not synced gets `alertDueAtMs`, {@link POS_OFFLINE_CARD_UNSYNCED_ALERT_MS}
|
|
44
|
+
* after it was first seen. The hourly sweep ({@link scanUnsyncedOfflineCards})
|
|
45
|
+
* tells the operator and the site's managers once, with time left to sync the
|
|
46
|
+
* sale before Stripe's authorization lapses (about two days). It never
|
|
47
|
+
* captures: without the sale there is no fee to capture with.
|
|
48
|
+
*
|
|
49
|
+
* Self-selecting: every other event returns `null` untouched. Registered on
|
|
50
|
+
* the platform webhook from the commerce server module, beside the other
|
|
51
|
+
* commerce handlers, rather than as a branch of the storefront sections. The
|
|
52
|
+
* four `payment_intent.*` events it reads are in the platform destination's
|
|
53
|
+
* `WEBHOOK_EVENTS` (`tools/scripts/lib/stripe-webhook-health.mjs`).
|
|
54
|
+
*=========================================*/ /** The subcollection, under `hosts/{hostId}/terminal/config`, and its collection-group name. */ export const POS_OFFLINE_CARD_CHARGES = 'offlineCardCharges';
|
|
55
|
+
/**
|
|
56
|
+
* How long an intent that holds money may wait for its sale before the sweep
|
|
57
|
+
* says so: six hours, which leaves well over a day of Stripe's two-day
|
|
58
|
+
* authorization to sync it, and is far past the seconds a healthy sync takes.
|
|
59
|
+
*/ export const POS_OFFLINE_CARD_UNSYNCED_ALERT_MS = 6 * 60 * 60 * 1000;
|
|
60
|
+
/** Statuses in which an intent holds money or has taken it. */ const HOLDS_MONEY = new Set([
|
|
61
|
+
'requires_capture',
|
|
62
|
+
'processing',
|
|
63
|
+
'succeeded'
|
|
64
|
+
]);
|
|
65
|
+
/** Statuses an intent never leaves; an older event must not turn one back. */ const FINAL = new Set([
|
|
66
|
+
'succeeded',
|
|
67
|
+
'canceled'
|
|
68
|
+
]);
|
|
69
|
+
/** `hosts/{hostId}/terminal/config/offlineCardCharges/{saleKey}`. */ export function posOfflineCardChargeRef(hostRef, saleKey) {
|
|
70
|
+
return hostRef.collection('terminal').doc('config').collection(POS_OFFLINE_CARD_CHARGES).doc(saleKey);
|
|
71
|
+
}
|
|
72
|
+
const validHostId = (hostId)=>/^[A-Za-z0-9_-]{1,128}$/.test(hostId);
|
|
18
73
|
export async function reconcilePosOfflineCardEvent(input, firestore, now = Date.now) {
|
|
19
|
-
var _input_object, _intent_metadata, _metadata_hostId, _metadata_saleKey
|
|
74
|
+
var _input_object, _intent_metadata, _metadata_hostId, _metadata_saleKey;
|
|
20
75
|
if (!input.type.startsWith('payment_intent.')) return null;
|
|
21
76
|
const intent = (_input_object = input.object) != null ? _input_object : {};
|
|
22
77
|
const metadata = (_intent_metadata = intent.metadata) != null ? _intent_metadata : {};
|
|
23
78
|
if (metadata['kind'] !== POS_OFFLINE_CARD_INTENT_KIND) return null;
|
|
24
79
|
const hostId = String((_metadata_hostId = metadata['hostId']) != null ? _metadata_hostId : '');
|
|
25
80
|
const saleKey = String((_metadata_saleKey = metadata['saleKey']) != null ? _metadata_saleKey : '');
|
|
26
|
-
if (
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
synced
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
81
|
+
if (!validHostId(hostId) || !posOfflineSaleKeyIsValid(saleKey)) return null;
|
|
82
|
+
const db = firestore != null ? firestore : firebaseAdmin.app().firestore();
|
|
83
|
+
const hostRef = db.collection('hosts').doc(hostId);
|
|
84
|
+
const chargeRef = posOfflineCardChargeRef(hostRef, saleKey);
|
|
85
|
+
const saleRef = hostRef.collection('posOfflineSales').doc(saleKey);
|
|
86
|
+
await db.runTransaction(async (transaction)=>{
|
|
87
|
+
var _intent_status, _intent_id, _intent_amount, _metadata_registerId;
|
|
88
|
+
const charge = await transaction.get(chargeRef);
|
|
89
|
+
const sale = await transaction.get(saleRef);
|
|
90
|
+
const previous = charge.exists ? charge.data() : {};
|
|
91
|
+
const synced = previous.synced === true || sale.exists;
|
|
92
|
+
const incoming = String((_intent_status = intent.status) != null ? _intent_status : '');
|
|
93
|
+
// Stripe does not promise event order: a late `amount_capturable_updated`
|
|
94
|
+
// must not turn a captured or canceled intent back into an authorized one.
|
|
95
|
+
const status = previous.status && FINAL.has(previous.status) && !FINAL.has(incoming) ? previous.status : incoming;
|
|
96
|
+
const at = now();
|
|
97
|
+
const alertDueAtMs = !synced && HOLDS_MONEY.has(status) && !previous.alertedAtMs ? typeof previous.alertDueAtMs === 'number' ? previous.alertDueAtMs : at + POS_OFFLINE_CARD_UNSYNCED_ALERT_MS : null;
|
|
98
|
+
transaction.set(chargeRef, {
|
|
99
|
+
hostId,
|
|
100
|
+
saleKey,
|
|
101
|
+
paymentIntentId: String((_intent_id = intent.id) != null ? _intent_id : ''),
|
|
102
|
+
status,
|
|
103
|
+
amountCents: Math.max(0, Math.round(Number((_intent_amount = intent.amount) != null ? _intent_amount : 0))),
|
|
104
|
+
registerId: String((_metadata_registerId = metadata['registerId']) != null ? _metadata_registerId : '').slice(0, 128),
|
|
105
|
+
livemode: Boolean(intent.livemode),
|
|
106
|
+
synced,
|
|
107
|
+
lastEvent: input.type,
|
|
108
|
+
updatedAtMs: at,
|
|
109
|
+
alertDueAtMs: alertDueAtMs != null ? alertDueAtMs : FieldValue.delete()
|
|
110
|
+
}, {
|
|
111
|
+
merge: true
|
|
112
|
+
});
|
|
42
113
|
});
|
|
43
114
|
return {
|
|
44
115
|
hostId
|
|
@@ -54,5 +125,131 @@ export async function reconcilePosOfflineCardEvent(input, firestore, now = Date.
|
|
|
54
125
|
hostId: reconciled.hostId
|
|
55
126
|
} : undefined;
|
|
56
127
|
};
|
|
128
|
+
const describeWait = (ms)=>{
|
|
129
|
+
const hours = Math.max(1, Math.round(ms / (60 * 60 * 1000)));
|
|
130
|
+
return hours >= 48 ? `${Math.round(hours / 24)} days` : `${hours} hour${hours === 1 ? '' : 's'}`;
|
|
131
|
+
};
|
|
132
|
+
/** Tells the operator and the site's managers about one card. Never throws. */ export async function reportUnsyncedOfflineCard(report) {
|
|
133
|
+
try {
|
|
134
|
+
const host = await firebaseAdmin.app().firestore().collection('hosts').doc(report.hostId).get().catch(()=>null);
|
|
135
|
+
const site = (host == null ? void 0 : host.exists) ? hostDisplayName(host.data(), report.hostId) : report.hostId;
|
|
136
|
+
const amount = formatOperatorAlertAmount(report.amountCents);
|
|
137
|
+
await raiseOperatorAlert(COMMERCE_POS_OFFLINE_CARD_UNSYNCED, {
|
|
138
|
+
dedupeKey: `${report.hostId}:${report.saleKey}`,
|
|
139
|
+
hostId: report.hostId,
|
|
140
|
+
context: {
|
|
141
|
+
amount,
|
|
142
|
+
paymentIntentId: report.paymentIntentId,
|
|
143
|
+
status: report.status,
|
|
144
|
+
site: `${site} (${report.hostId})`,
|
|
145
|
+
registerId: report.registerId || 'unknown',
|
|
146
|
+
age: describeWait(report.waitedMs)
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
await notifyHostManagers(report.hostId, {
|
|
150
|
+
type: 'content.order',
|
|
151
|
+
title: `A ${amount} card taken offline on {site} has no sale`,
|
|
152
|
+
body: report.status === 'succeeded' ? `A ${amount} card taken offline was charged, but the register never sent its sale, so there is no order for it. ` + 'Open the register that took it, signed in as the member who rang it, so the sale syncs.' : `A ${amount} card taken offline is approved but not yet charged, because the register never sent its sale. ` + 'Open the register that took it, signed in as the member who rang it, so the sale syncs. ' + 'If it does not sync within about two days of the sale, the card is never charged.',
|
|
153
|
+
link: `/${report.hostId}/products`
|
|
154
|
+
});
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error('[pos-offline-card] unsynced report failed', report.hostId, report.saleKey, error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** Tells the operator a card was made out to an account that is not the store's. Never throws. */ export async function reportPosOfflineCardWrongAccount(input) {
|
|
160
|
+
try {
|
|
161
|
+
const host = await firebaseAdmin.app().firestore().collection('hosts').doc(input.hostId).get().catch(()=>null);
|
|
162
|
+
const site = (host == null ? void 0 : host.exists) ? hostDisplayName(host.data(), input.hostId) : input.hostId;
|
|
163
|
+
await raiseOperatorAlert(COMMERCE_POS_OFFLINE_CARD_WRONG_ACCOUNT, {
|
|
164
|
+
dedupeKey: `${input.hostId}:${input.orderId}`,
|
|
165
|
+
hostId: input.hostId,
|
|
166
|
+
context: {
|
|
167
|
+
amount: formatOperatorAlertAmount(input.amountCents),
|
|
168
|
+
paymentIntentId: input.paymentIntentId,
|
|
169
|
+
status: input.status,
|
|
170
|
+
orderId: input.orderId,
|
|
171
|
+
site: `${site} (${input.hostId})`,
|
|
172
|
+
destination: input.destination || 'none',
|
|
173
|
+
expected: input.expected
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error('[pos-offline-card] wrong-account alert failed', input.hostId, input.orderId, error);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The hourly sweep (registered in `server.ts`): every offline card record
|
|
182
|
+
* whose alert is due. Re-checks the sale first, so a sale that synced while
|
|
183
|
+
* the event was in flight clears the record and tells nobody.
|
|
184
|
+
*
|
|
185
|
+
* One collection-group range query on `alertDueAtMs`, which is set only on a
|
|
186
|
+
* record that holds money and has not synced, so the ordinary beat reads an
|
|
187
|
+
* empty result. Its COLLECTION_GROUP index is declared in
|
|
188
|
+
* `cloud/firebase-firestore.indexes.json` and pinned by
|
|
189
|
+
* `cron-scan-indexes.spec.ts`.
|
|
190
|
+
*/ export async function scanUnsyncedOfflineCards(/** The lockdown gate, injected by the runner (AGL-2495). */ gate, options = {}) {
|
|
191
|
+
var _options_firestore, _options_nowMs, _options_report, _options_limit;
|
|
192
|
+
const db = (_options_firestore = options.firestore) != null ? _options_firestore : firebaseAdmin.app().firestore();
|
|
193
|
+
const nowMs = (_options_nowMs = options.nowMs) != null ? _options_nowMs : Date.now();
|
|
194
|
+
const report = (_options_report = options.report) != null ? _options_report : reportUnsyncedOfflineCard;
|
|
195
|
+
const scan = {
|
|
196
|
+
due: 0,
|
|
197
|
+
alerted: 0,
|
|
198
|
+
cleared: 0,
|
|
199
|
+
skippedLocked: 0
|
|
200
|
+
};
|
|
201
|
+
const due = await db.collectionGroup(POS_OFFLINE_CARD_CHARGES).where('alertDueAtMs', '<=', nowMs).orderBy('alertDueAtMs', 'asc').limit((_options_limit = options.limit) != null ? _options_limit : 100).get().catch((error)=>{
|
|
202
|
+
console.error('[pos-offline-card] unsynced scan failed', error);
|
|
203
|
+
return null;
|
|
204
|
+
});
|
|
205
|
+
if (!due) return scan;
|
|
206
|
+
for (const doc of due.docs){
|
|
207
|
+
var _doc_data, _record_status, _record_paymentIntentId, _record_status1, _record_amountCents, _record_registerId;
|
|
208
|
+
var _doc_ref_parent_parent_parent_parent, _doc_ref_parent_parent_parent, _doc_ref_parent_parent;
|
|
209
|
+
const record = (_doc_data = doc.data()) != null ? _doc_data : {};
|
|
210
|
+
if (typeof record.alertDueAtMs !== 'number') continue;
|
|
211
|
+
// `hosts/{hostId}/terminal/config/offlineCardCharges/{saleKey}`.
|
|
212
|
+
const hostId = String(record.hostId || ((_doc_ref_parent_parent = doc.ref.parent.parent) == null ? void 0 : (_doc_ref_parent_parent_parent = _doc_ref_parent_parent.parent) == null ? void 0 : (_doc_ref_parent_parent_parent_parent = _doc_ref_parent_parent_parent.parent) == null ? void 0 : _doc_ref_parent_parent_parent_parent.id) || '');
|
|
213
|
+
const saleKey = String(record.saleKey || doc.id);
|
|
214
|
+
if (!validHostId(hostId) || !posOfflineSaleKeyIsValid(saleKey)) continue;
|
|
215
|
+
scan.due += 1;
|
|
216
|
+
// A locked site is skipped, not dropped: its record stays due and is
|
|
217
|
+
// told on the first beat after the lift.
|
|
218
|
+
if (await gate.isLocked(hostId)) {
|
|
219
|
+
scan.skippedLocked += 1;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const hostRef = db.collection('hosts').doc(hostId);
|
|
223
|
+
const synced = (await hostRef.collection('posOfflineSales').doc(saleKey).get()).exists;
|
|
224
|
+
if (synced || !HOLDS_MONEY.has(String((_record_status = record.status) != null ? _record_status : ''))) {
|
|
225
|
+
await doc.ref.set(_extends({}, synced ? {
|
|
226
|
+
synced: true
|
|
227
|
+
} : {}, {
|
|
228
|
+
alertDueAtMs: FieldValue.delete()
|
|
229
|
+
}), {
|
|
230
|
+
merge: true
|
|
231
|
+
});
|
|
232
|
+
scan.cleared += 1;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
await report({
|
|
236
|
+
hostId,
|
|
237
|
+
saleKey,
|
|
238
|
+
paymentIntentId: String((_record_paymentIntentId = record.paymentIntentId) != null ? _record_paymentIntentId : ''),
|
|
239
|
+
status: String((_record_status1 = record.status) != null ? _record_status1 : ''),
|
|
240
|
+
amountCents: Math.max(0, Math.round(Number((_record_amountCents = record.amountCents) != null ? _record_amountCents : 0))),
|
|
241
|
+
registerId: String((_record_registerId = record.registerId) != null ? _record_registerId : ''),
|
|
242
|
+
waitedMs: Math.max(0, nowMs - (record.alertDueAtMs - POS_OFFLINE_CARD_UNSYNCED_ALERT_MS))
|
|
243
|
+
});
|
|
244
|
+
await doc.ref.set({
|
|
245
|
+
alertedAtMs: nowMs,
|
|
246
|
+
alertDueAtMs: FieldValue.delete()
|
|
247
|
+
}, {
|
|
248
|
+
merge: true
|
|
249
|
+
});
|
|
250
|
+
scan.alerted += 1;
|
|
251
|
+
}
|
|
252
|
+
return scan;
|
|
253
|
+
}
|
|
57
254
|
|
|
58
255
|
//# sourceMappingURL=pos-offline-card-reconcile.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-offline-card-reconcile.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 { BillingWebhookHandler } from '@aglyn/aglyn/server'\nimport { firebaseAdmin } from '@aglyn/tenant-data-admin'\nimport {\n POS_OFFLINE_CARD_INTENT_KIND,\n posOfflineSaleKeyIsValid,\n} from '../model/commerce-pos-offline'\n\n/*==========================================\n * A CARD TAKEN OFFLINE, AS STRIPE REPORTS IT (AGL-4039).\n *\n * The Aglyn POS app creates the PaymentIntent of a card it takes offline, so\n * Stripe can know of a charge before the sale itself reaches the server: the\n * SDK forwards the card the moment a connection returns, and the sale syncs\n * right after, from the same device. Usually seconds apart. If the sale never\n * arrives (the device was lost or wiped between the two), the charge must not\n * be invisible: each event on such an intent is kept on the site, by the\n * sale's key, under the site's server-only Terminal document\n * (`terminal/config/offlineCardCharges/{saleKey}`), beside whether the sale\n * has synced. The sync itself never depends on this: it reads Stripe.\n *\n * Self-selecting: every other event returns `null` untouched. Registered on\n * the platform webhook from the commerce server module, beside the other\n * commerce handlers, rather than as a branch of the storefront sections.\n *=========================================*/\n\n/** What the site keeps about one offline card's PaymentIntent. */\nexport interface PosOfflineCardChargeRecord {\n saleKey: string\n paymentIntentId: string\n status: string\n amountCents: number\n registerId: string\n livemode: boolean\n /** Whether the sale has synced (its order exists). */\n synced: boolean\n lastEvent: string\n updatedAtMs: number\n}\n\nexport async function reconcilePosOfflineCardEvent(\n input: { type: string; object: any },\n firestore?: FirebaseFirestore.Firestore,\n now: () => number = Date.now,\n): Promise<{ hostId: string } | null> {\n if (!input.type.startsWith('payment_intent.')) return null\n const intent = input.object ?? {}\n const metadata = (intent.metadata ?? {}) as Record<string, string>\n if (metadata['kind'] !== POS_OFFLINE_CARD_INTENT_KIND) return null\n const hostId = String(metadata['hostId'] ?? '')\n const saleKey = String(metadata['saleKey'] ?? '')\n if (\n !/^[A-Za-z0-9_-]{1,128}$/.test(hostId) ||\n !posOfflineSaleKeyIsValid(saleKey)\n )\n return null\n const hostRef = (firestore ?? firebaseAdmin.app().firestore())\n .collection('hosts')\n .doc(hostId)\n const synced = (\n await hostRef.collection('posOfflineSales').doc(saleKey).get()\n ).exists\n const record: PosOfflineCardChargeRecord = {\n saleKey,\n paymentIntentId: String(intent.id ?? ''),\n status: String(intent.status ?? ''),\n amountCents: Number(intent.amount ?? 0),\n registerId: String(metadata['registerId'] ?? '').slice(0, 128),\n livemode: Boolean(intent.livemode),\n synced,\n lastEvent: input.type,\n updatedAtMs: now(),\n }\n await hostRef\n .collection('terminal')\n .doc('config')\n .collection('offlineCardCharges')\n .doc(saleKey)\n .set(record, { merge: true })\n return { hostId }\n}\n\n/** The platform webhook's handler for offline card intents: claims only its own. */\nexport const posOfflineCardBillingWebhookHandler: BillingWebhookHandler =\n async ({ type, object }) => {\n const reconciled = await reconcilePosOfflineCardEvent({ type, object })\n return reconciled ? { claimed: true, hostId: reconciled.hostId } : undefined\n }\n"],"names":["firebaseAdmin","POS_OFFLINE_CARD_INTENT_KIND","posOfflineSaleKeyIsValid","reconcilePosOfflineCardEvent","input","firestore","now","Date","intent","metadata","type","startsWith","object","hostId","String","saleKey","test","hostRef","app","collection","doc","synced","get","exists","record","paymentIntentId","id","status","amountCents","Number","amount","registerId","slice","livemode","Boolean","lastEvent","updatedAtMs","set","merge","posOfflineCardBillingWebhookHandler","reconciled","claimed","undefined"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,aAAa,QAAQ,2BAA0B;AACxD,SACEC,4BAA4B,EAC5BC,wBAAwB,QACnB,mCAA+B;AAkCtC,OAAO,eAAeC,6BACpBC,KAAoC,EACpCC,SAAuC,EACvCC,MAAoBC,KAAKD,GAAG;QAGbF,eACGI,kBAEIC,kBACCA,mBAcGD,YACTA,gBACKA,gBACDC;IAtBrB,IAAI,CAACL,MAAMM,IAAI,CAACC,UAAU,CAAC,oBAAoB,OAAO;IACtD,MAAMH,UAASJ,gBAAAA,MAAMQ,MAAM,YAAZR,gBAAgB,CAAC;IAChC,MAAMK,YAAYD,mBAAAA,OAAOC,QAAQ,YAAfD,mBAAmB,CAAC;IACtC,IAAIC,QAAQ,CAAC,OAAO,KAAKR,8BAA8B,OAAO;IAC9D,MAAMY,SAASC,QAAOL,mBAAAA,QAAQ,CAAC,SAAS,YAAlBA,mBAAsB;IAC5C,MAAMM,UAAUD,QAAOL,oBAAAA,QAAQ,CAAC,UAAU,YAAnBA,oBAAuB;IAC9C,IACE,CAAC,yBAAyBO,IAAI,CAACH,WAC/B,CAACX,yBAAyBa,UAE1B,OAAO;IACT,MAAME,UAAU,CAACZ,oBAAAA,YAAaL,cAAckB,GAAG,GAAGb,SAAS,IACxDc,UAAU,CAAC,SACXC,GAAG,CAACP;IACP,MAAMQ,SAAS,AACb,CAAA,MAAMJ,QAAQE,UAAU,CAAC,mBAAmBC,GAAG,CAACL,SAASO,GAAG,EAAC,EAC7DC,MAAM;IACR,MAAMC,SAAqC;QACzCT;QACAU,iBAAiBX,QAAON,aAAAA,OAAOkB,EAAE,YAATlB,aAAa;QACrCmB,QAAQb,QAAON,iBAAAA,OAAOmB,MAAM,YAAbnB,iBAAiB;QAChCoB,aAAaC,QAAOrB,iBAAAA,OAAOsB,MAAM,YAAbtB,iBAAiB;QACrCuB,YAAYjB,QAAOL,uBAAAA,QAAQ,CAAC,aAAa,YAAtBA,uBAA0B,IAAIuB,KAAK,CAAC,GAAG;QAC1DC,UAAUC,QAAQ1B,OAAOyB,QAAQ;QACjCZ;QACAc,WAAW/B,MAAMM,IAAI;QACrB0B,aAAa9B;IACf;IACA,MAAMW,QACHE,UAAU,CAAC,YACXC,GAAG,CAAC,UACJD,UAAU,CAAC,sBACXC,GAAG,CAACL,SACJsB,GAAG,CAACb,QAAQ;QAAEc,OAAO;IAAK;IAC7B,OAAO;QAAEzB;IAAO;AAClB;AAEA,kFAAkF,GAClF,OAAO,MAAM0B,sCACX,OAAO,EAAE7B,IAAI,EAAEE,MAAM,EAAE;IACrB,MAAM4B,aAAa,MAAMrC,6BAA6B;QAAEO;QAAME;IAAO;IACrE,OAAO4B,aAAa;QAAEC,SAAS;QAAM5B,QAAQ2B,WAAW3B,MAAM;IAAC,IAAI6B;AACrE,EAAC"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/server/pos-offline-card-reconcile.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 { formatOperatorAlertAmount } from '@aglyn/aglyn/app-utils/operator-alerts'\nimport type { BillingWebhookHandler, PluginJobHostGate } from '@aglyn/aglyn/server'\nimport {\n firebaseAdmin,\n hostDisplayName,\n notifyHostManagers,\n raiseOperatorAlert,\n} from '@aglyn/tenant-data-admin'\nimport { FieldValue } from 'firebase-admin/firestore'\nimport {\n COMMERCE_POS_OFFLINE_CARD_UNSYNCED,\n COMMERCE_POS_OFFLINE_CARD_WRONG_ACCOUNT,\n} from '../constants/operator-alerts'\nimport {\n POS_OFFLINE_CARD_INTENT_KIND,\n posOfflineSaleKeyIsValid,\n} from '../model/commerce-pos-offline'\n\n/*==========================================\n * A CARD TAKEN OFFLINE, AS STRIPE REPORTS IT (AGL-4039).\n *\n * The Aglyn POS app creates the PaymentIntent of a card it takes offline, so\n * Stripe can know of a charge before the sale itself reaches the server: the\n * SDK forwards the card the moment a connection returns, and the sale syncs\n * right after, from the same device. Usually seconds apart. If the sale never\n * arrives (the device was lost or wiped between the two, or it waits for a\n * member who never signs in again), the charge must not be invisible: each\n * event on such an intent is kept on the site, by the sale's key, under the\n * site's server-only Terminal document\n * (`terminal/config/offlineCardCharges/{saleKey}`), beside whether the sale\n * has synced. The sync itself never depends on this: it reads Stripe.\n *\n * WHO SAYS \"SYNCED\". Both writers, in transactions, so neither can undo the\n * other: the sync marks the record when it creates the order, and an event\n * reads the record and the sync record together. An event that arrives after\n * the sync therefore keeps `synced`, and one that raced it retries and sees\n * it.\n *\n * NOBODY LOOKING. An intent that holds or has taken money while its sale has\n * not synced gets `alertDueAtMs`, {@link POS_OFFLINE_CARD_UNSYNCED_ALERT_MS}\n * after it was first seen. The hourly sweep ({@link scanUnsyncedOfflineCards})\n * tells the operator and the site's managers once, with time left to sync the\n * sale before Stripe's authorization lapses (about two days). It never\n * captures: without the sale there is no fee to capture with.\n *\n * Self-selecting: every other event returns `null` untouched. Registered on\n * the platform webhook from the commerce server module, beside the other\n * commerce handlers, rather than as a branch of the storefront sections. The\n * four `payment_intent.*` events it reads are in the platform destination's\n * `WEBHOOK_EVENTS` (`tools/scripts/lib/stripe-webhook-health.mjs`).\n *=========================================*/\n\n/** The subcollection, under `hosts/{hostId}/terminal/config`, and its collection-group name. */\nexport const POS_OFFLINE_CARD_CHARGES = 'offlineCardCharges'\n\n/**\n * How long an intent that holds money may wait for its sale before the sweep\n * says so: six hours, which leaves well over a day of Stripe's two-day\n * authorization to sync it, and is far past the seconds a healthy sync takes.\n */\nexport const POS_OFFLINE_CARD_UNSYNCED_ALERT_MS = 6 * 60 * 60 * 1000\n\n/** Statuses in which an intent holds money or has taken it. */\nconst HOLDS_MONEY: ReadonlySet<string> = new Set(['requires_capture', 'processing', 'succeeded'])\n\n/** Statuses an intent never leaves; an older event must not turn one back. */\nconst FINAL: ReadonlySet<string> = new Set(['succeeded', 'canceled'])\n\n/** What the site keeps about one offline card's PaymentIntent. */\nexport interface PosOfflineCardChargeRecord {\n hostId: string\n saleKey: string\n paymentIntentId: string\n status: string\n amountCents: number\n registerId: string\n livemode: boolean\n /** Whether the sale has synced (its order exists). */\n synced: boolean\n orderId?: string\n syncedAtMs?: number\n lastEvent: string\n updatedAtMs: number\n /** When the sweep says the sale never synced; absent once synced or told. */\n alertDueAtMs?: number\n alertedAtMs?: number\n /** The capture's idempotency attempt, raised only after a definite failure. */\n captureAttempt?: number\n}\n\n/** `hosts/{hostId}/terminal/config/offlineCardCharges/{saleKey}`. */\nexport function posOfflineCardChargeRef(\n hostRef: FirebaseFirestore.DocumentReference,\n saleKey: string,\n): FirebaseFirestore.DocumentReference {\n return hostRef.collection('terminal').doc('config').collection(POS_OFFLINE_CARD_CHARGES).doc(saleKey)\n}\n\nconst validHostId = (hostId: string) => /^[A-Za-z0-9_-]{1,128}$/.test(hostId)\n\nexport async function reconcilePosOfflineCardEvent(\n input: { type: string; object: any },\n firestore?: FirebaseFirestore.Firestore,\n now: () => number = Date.now,\n): Promise<{ hostId: string } | null> {\n if (!input.type.startsWith('payment_intent.')) return null\n const intent = input.object ?? {}\n const metadata = (intent.metadata ?? {}) as Record<string, string>\n if (metadata['kind'] !== POS_OFFLINE_CARD_INTENT_KIND) return null\n const hostId = String(metadata['hostId'] ?? '')\n const saleKey = String(metadata['saleKey'] ?? '')\n if (!validHostId(hostId) || !posOfflineSaleKeyIsValid(saleKey)) return null\n const db = firestore ?? firebaseAdmin.app().firestore()\n const hostRef = db.collection('hosts').doc(hostId)\n const chargeRef = posOfflineCardChargeRef(hostRef, saleKey)\n const saleRef = hostRef.collection('posOfflineSales').doc(saleKey)\n await db.runTransaction(async (transaction) => {\n const charge = await transaction.get(chargeRef)\n const sale = await transaction.get(saleRef)\n const previous = (charge.exists ? charge.data() : {}) as Partial<PosOfflineCardChargeRecord>\n const synced = previous.synced === true || sale.exists\n const incoming = String(intent.status ?? '')\n // Stripe does not promise event order: a late `amount_capturable_updated`\n // must not turn a captured or canceled intent back into an authorized one.\n const status =\n previous.status && FINAL.has(previous.status) && !FINAL.has(incoming) ? previous.status : incoming\n const at = now()\n const alertDueAtMs =\n !synced && HOLDS_MONEY.has(status) && !previous.alertedAtMs\n ? typeof previous.alertDueAtMs === 'number'\n ? previous.alertDueAtMs\n : at + POS_OFFLINE_CARD_UNSYNCED_ALERT_MS\n : null\n transaction.set(\n chargeRef,\n {\n hostId,\n saleKey,\n paymentIntentId: String(intent.id ?? ''),\n status,\n amountCents: Math.max(0, Math.round(Number(intent.amount ?? 0))),\n registerId: String(metadata['registerId'] ?? '').slice(0, 128),\n livemode: Boolean(intent.livemode),\n synced,\n lastEvent: input.type,\n updatedAtMs: at,\n alertDueAtMs: alertDueAtMs ?? FieldValue.delete(),\n },\n { merge: true },\n )\n })\n return { hostId }\n}\n\n/** The platform webhook's handler for offline card intents: claims only its own. */\nexport const posOfflineCardBillingWebhookHandler: BillingWebhookHandler =\n async ({ type, object }) => {\n const reconciled = await reconcilePosOfflineCardEvent({ type, object })\n return reconciled ? { claimed: true, hostId: reconciled.hostId } : undefined\n }\n\n/*==========================================\n * THE SWEEP: AN OFFLINE CARD WHOSE SALE NEVER SYNCED.\n *=========================================*/\n\nexport interface UnsyncedOfflineCardReport {\n hostId: string\n saleKey: string\n paymentIntentId: string\n status: string\n amountCents: number\n registerId: string\n /** How long ago the intent was first seen waiting, in ms. */\n waitedMs: number\n}\n\nexport interface UnsyncedOfflineCardScan {\n due: number\n alerted: number\n /** Synced, or no longer holding money, since the alert was set. */\n cleared: number\n skippedLocked: number\n}\n\nconst describeWait = (ms: number) => {\n const hours = Math.max(1, Math.round(ms / (60 * 60 * 1000)))\n return hours >= 48 ? `${Math.round(hours / 24)} days` : `${hours} hour${hours === 1 ? '' : 's'}`\n}\n\n/** Tells the operator and the site's managers about one card. Never throws. */\nexport async function reportUnsyncedOfflineCard(report: UnsyncedOfflineCardReport): Promise<void> {\n try {\n const host = await firebaseAdmin.app().firestore().collection('hosts').doc(report.hostId).get().catch(() => null)\n const site = host?.exists ? hostDisplayName(host.data() as Record<string, unknown>, report.hostId) : report.hostId\n const amount = formatOperatorAlertAmount(report.amountCents)\n await raiseOperatorAlert(COMMERCE_POS_OFFLINE_CARD_UNSYNCED, {\n dedupeKey: `${report.hostId}:${report.saleKey}`,\n hostId: report.hostId,\n context: {\n amount,\n paymentIntentId: report.paymentIntentId,\n status: report.status,\n site: `${site} (${report.hostId})`,\n registerId: report.registerId || 'unknown',\n age: describeWait(report.waitedMs),\n },\n })\n await notifyHostManagers(report.hostId, {\n type: 'content.order',\n title: `A ${amount} card taken offline on {site} has no sale`,\n body:\n report.status === 'succeeded'\n ? `A ${amount} card taken offline was charged, but the register never sent its sale, so there is no order for it. ` +\n 'Open the register that took it, signed in as the member who rang it, so the sale syncs.'\n : `A ${amount} card taken offline is approved but not yet charged, because the register never sent its sale. ` +\n 'Open the register that took it, signed in as the member who rang it, so the sale syncs. ' +\n 'If it does not sync within about two days of the sale, the card is never charged.',\n link: `/${report.hostId}/products`,\n })\n } catch (error) {\n console.error('[pos-offline-card] unsynced report failed', report.hostId, report.saleKey, error)\n }\n}\n\n/** Tells the operator a card was made out to an account that is not the store's. Never throws. */\nexport async function reportPosOfflineCardWrongAccount(input: {\n hostId: string\n orderId: string\n paymentIntentId: string\n status: string\n amountCents: number\n destination: string\n expected: string\n}): Promise<void> {\n try {\n const host = await firebaseAdmin.app().firestore().collection('hosts').doc(input.hostId).get().catch(() => null)\n const site = host?.exists ? hostDisplayName(host.data() as Record<string, unknown>, input.hostId) : input.hostId\n await raiseOperatorAlert(COMMERCE_POS_OFFLINE_CARD_WRONG_ACCOUNT, {\n dedupeKey: `${input.hostId}:${input.orderId}`,\n hostId: input.hostId,\n context: {\n amount: formatOperatorAlertAmount(input.amountCents),\n paymentIntentId: input.paymentIntentId,\n status: input.status,\n orderId: input.orderId,\n site: `${site} (${input.hostId})`,\n destination: input.destination || 'none',\n expected: input.expected,\n },\n })\n } catch (error) {\n console.error('[pos-offline-card] wrong-account alert failed', input.hostId, input.orderId, error)\n }\n}\n\n/**\n * The hourly sweep (registered in `server.ts`): every offline card record\n * whose alert is due. Re-checks the sale first, so a sale that synced while\n * the event was in flight clears the record and tells nobody.\n *\n * One collection-group range query on `alertDueAtMs`, which is set only on a\n * record that holds money and has not synced, so the ordinary beat reads an\n * empty result. Its COLLECTION_GROUP index is declared in\n * `cloud/firebase-firestore.indexes.json` and pinned by\n * `cron-scan-indexes.spec.ts`.\n */\nexport async function scanUnsyncedOfflineCards(\n /** The lockdown gate, injected by the runner (AGL-2495). */\n gate: PluginJobHostGate,\n options: {\n firestore?: FirebaseFirestore.Firestore\n nowMs?: number\n limit?: number\n report?: (report: UnsyncedOfflineCardReport) => Promise<void>\n } = {},\n): Promise<UnsyncedOfflineCardScan> {\n const db = options.firestore ?? firebaseAdmin.app().firestore()\n const nowMs = options.nowMs ?? Date.now()\n const report = options.report ?? reportUnsyncedOfflineCard\n const scan: UnsyncedOfflineCardScan = { due: 0, alerted: 0, cleared: 0, skippedLocked: 0 }\n const due = await db\n .collectionGroup(POS_OFFLINE_CARD_CHARGES)\n .where('alertDueAtMs', '<=', nowMs)\n .orderBy('alertDueAtMs', 'asc')\n .limit(options.limit ?? 100)\n .get()\n .catch((error: unknown) => {\n console.error('[pos-offline-card] unsynced scan failed', error)\n return null\n })\n if (!due) return scan\n for (const doc of due.docs) {\n const record = (doc.data() ?? {}) as Partial<PosOfflineCardChargeRecord>\n if (typeof record.alertDueAtMs !== 'number') continue\n // `hosts/{hostId}/terminal/config/offlineCardCharges/{saleKey}`.\n const hostId = String(record.hostId || doc.ref.parent.parent?.parent?.parent?.id || '')\n const saleKey = String(record.saleKey || doc.id)\n if (!validHostId(hostId) || !posOfflineSaleKeyIsValid(saleKey)) continue\n scan.due += 1\n // A locked site is skipped, not dropped: its record stays due and is\n // told on the first beat after the lift.\n if (await gate.isLocked(hostId)) {\n scan.skippedLocked += 1\n continue\n }\n const hostRef = db.collection('hosts').doc(hostId)\n const synced = (await hostRef.collection('posOfflineSales').doc(saleKey).get()).exists\n if (synced || !HOLDS_MONEY.has(String(record.status ?? ''))) {\n await doc.ref.set(\n { ...(synced ? { synced: true } : {}), alertDueAtMs: FieldValue.delete() },\n { merge: true },\n )\n scan.cleared += 1\n continue\n }\n await report({\n hostId,\n saleKey,\n paymentIntentId: String(record.paymentIntentId ?? ''),\n status: String(record.status ?? ''),\n amountCents: Math.max(0, Math.round(Number(record.amountCents ?? 0))),\n registerId: String(record.registerId ?? ''),\n waitedMs: Math.max(0, nowMs - (record.alertDueAtMs - POS_OFFLINE_CARD_UNSYNCED_ALERT_MS)),\n })\n await doc.ref.set({ alertedAtMs: nowMs, alertDueAtMs: FieldValue.delete() }, { merge: true })\n scan.alerted += 1\n }\n return scan\n}\n"],"names":["formatOperatorAlertAmount","firebaseAdmin","hostDisplayName","notifyHostManagers","raiseOperatorAlert","FieldValue","COMMERCE_POS_OFFLINE_CARD_UNSYNCED","COMMERCE_POS_OFFLINE_CARD_WRONG_ACCOUNT","POS_OFFLINE_CARD_INTENT_KIND","posOfflineSaleKeyIsValid","POS_OFFLINE_CARD_CHARGES","POS_OFFLINE_CARD_UNSYNCED_ALERT_MS","HOLDS_MONEY","Set","FINAL","posOfflineCardChargeRef","hostRef","saleKey","collection","doc","validHostId","hostId","test","reconcilePosOfflineCardEvent","input","firestore","now","Date","intent","metadata","type","startsWith","object","String","db","app","chargeRef","saleRef","runTransaction","transaction","charge","get","sale","previous","exists","data","synced","incoming","status","has","at","alertDueAtMs","alertedAtMs","set","paymentIntentId","id","amountCents","Math","max","round","Number","amount","registerId","slice","livemode","Boolean","lastEvent","updatedAtMs","delete","merge","posOfflineCardBillingWebhookHandler","reconciled","claimed","undefined","describeWait","ms","hours","reportUnsyncedOfflineCard","report","host","catch","site","dedupeKey","context","age","waitedMs","title","body","link","error","console","reportPosOfflineCardWrongAccount","orderId","destination","expected","scanUnsyncedOfflineCards","gate","options","nowMs","scan","due","alerted","cleared","skippedLocked","collectionGroup","where","orderBy","limit","docs","record","ref","parent","isLocked"],"mappings":"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"}
|
|
@@ -85,13 +85,28 @@ export interface PosOfflineCardDeps {
|
|
|
85
85
|
ok: false;
|
|
86
86
|
error: string;
|
|
87
87
|
}>;
|
|
88
|
+
/**
|
|
89
|
+
* Captures with the platform fee. A failure carries Stripe's HTTP status
|
|
90
|
+
* when Stripe answered; a request that never got an answer throws.
|
|
91
|
+
*/
|
|
88
92
|
capture(paymentIntentId: string, applicationFeeCents: number, idempotencyKey: string): Promise<{
|
|
89
93
|
ok: true;
|
|
90
94
|
intent: PosOfflineCardIntent;
|
|
91
95
|
} | {
|
|
92
96
|
ok: false;
|
|
93
97
|
error: string;
|
|
98
|
+
status?: number;
|
|
94
99
|
}>;
|
|
100
|
+
/** Tells the operator a card was made out to another account. Never throws. */
|
|
101
|
+
wrongAccount(input: {
|
|
102
|
+
hostId: string;
|
|
103
|
+
orderId: string;
|
|
104
|
+
paymentIntentId: string;
|
|
105
|
+
status: string;
|
|
106
|
+
amountCents: number;
|
|
107
|
+
destination: string;
|
|
108
|
+
expected: string;
|
|
109
|
+
}): Promise<void>;
|
|
95
110
|
}
|
|
96
111
|
export declare function defaultPosOfflineCardDeps(): PosOfflineCardDeps;
|
|
97
112
|
export declare function defaultPosOfflineSyncDeps(): PosOfflineSyncDeps;
|
|
@@ -111,6 +126,30 @@ export declare function posInPersonTax(settings: CommerceModel.TaxSettings | und
|
|
|
111
126
|
};
|
|
112
127
|
/** The kit a register caches to sell offline. */
|
|
113
128
|
export declare function buildPosOfflineKit(deps: PosOfflineSyncDeps, staff: PosOpsStaff): Promise<PosOfflineKit>;
|
|
129
|
+
/**
|
|
130
|
+
* How long after a capture that Stripe answered with a 5xx the sync waits
|
|
131
|
+
* before reading "still waiting for capture" as the definite answer.
|
|
132
|
+
*/
|
|
133
|
+
export declare const POS_OFFLINE_CAPTURE_SETTLE_MS = 60000;
|
|
134
|
+
/**
|
|
135
|
+
* The capture's idempotency key (AGL-4039). Attempt 0 is the key every
|
|
136
|
+
* capture used before attempts existed, so a retry of one already in flight
|
|
137
|
+
* still finds it.
|
|
138
|
+
*
|
|
139
|
+
* WHY AN ATTEMPT. Stripe keeps the answer to an idempotent request for a day,
|
|
140
|
+
* failures included, so a capture that failed once on a per-sale key would
|
|
141
|
+
* fail the same way on every sync for a day, deep into the two days the
|
|
142
|
+
* authorization lasts. The attempt moves on only when the previous one
|
|
143
|
+
* DEFINITELY did not capture: Stripe refused it with a 4xx (not 409, a
|
|
144
|
+
* request with the same key still running, and not 429, rate limited), or it
|
|
145
|
+
* answered 5xx and the intent still waits for capture a minute later. A
|
|
146
|
+
* request that got no answer at all throws before anything is recorded, so it
|
|
147
|
+
* retries on the same key. Every sync reads the intent before it captures,
|
|
148
|
+
* and Stripe captures an intent once, so a new key can never capture twice.
|
|
149
|
+
*/
|
|
150
|
+
export declare function posOfflineCaptureKey(hostId: string, saleKey: string, attempt: number): string;
|
|
151
|
+
/** A capture Stripe refused for good: retrying the same key would replay the refusal. */
|
|
152
|
+
export declare function posOfflineCaptureRefused(status: number | undefined): boolean;
|
|
114
153
|
export declare function handlePosOfflineSync(deps: PosOfflineSyncDeps, req: PluginApiRequest): Promise<Outcome>;
|
|
115
154
|
export declare function createPosOfflineSyncHandler(deps?: () => PosOfflineSyncDeps): PluginApiHandler;
|
|
116
155
|
export declare const posOfflineSyncHandler: Aglyn.PluginApiHandler;
|