@vtex/faststore 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/dist/actions.json +181 -0
- package/dist/catalog.json +1595 -481
- package/dist/client.d.ts +2 -1
- package/dist/client.js +2 -1
- package/dist/components.d.ts +31 -7
- package/dist/components.js +35 -13
- package/dist/content-types.json +73 -0
- package/dist/docs/access-code-sign-in.md +1 -1
- package/dist/docs/account-access-gate.md +153 -0
- package/dist/docs/account-entry-point.md +1 -2
- package/dist/docs/account-menu.md +164 -0
- package/dist/docs/address-summary.md +24 -3
- package/dist/docs/alert-dialog.md +204 -0
- package/dist/docs/billing-address.md +284 -0
- package/dist/docs/buy-button.md +1 -1
- package/dist/docs/card-entry.md +250 -0
- package/dist/docs/cardholder-document.md +142 -0
- package/dist/docs/cart.md +1 -1
- package/dist/docs/client-preferences-fieldset.md +148 -0
- package/dist/docs/client-preferences.md +2 -1
- package/dist/docs/client-profile-fieldset.md +184 -0
- package/dist/docs/client-profile.md +1 -1
- package/dist/docs/document-field.md +58 -4
- package/dist/docs/field.md +317 -0
- package/dist/docs/form.md +307 -0
- package/dist/docs/identity-edit.md +6 -6
- package/dist/docs/identity-summary.md +5 -5
- package/dist/docs/installments.md +248 -0
- package/dist/docs/my-account-profile.md +1 -0
- package/dist/docs/order-placed-payment-summary.md +138 -0
- package/dist/docs/payment-method-badges.md +232 -0
- package/dist/docs/payment-methods.md +1 -0
- package/dist/docs/payment.md +218 -0
- package/dist/docs/phone-field.md +58 -4
- package/dist/docs/place-order.md +157 -0
- package/dist/docs/save-card.md +186 -0
- package/dist/docs/saved-cards.md +218 -0
- package/dist/docs/select.md +465 -0
- package/dist/docs/session.md +1 -1
- package/dist/docs/shipping-address.md +110 -258
- package/dist/docs/sign-in-redirect.md +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/internal/client.d.ts +3 -1
- package/dist/internal/client.js +3 -1
- package/dist/matter/access-code-sign-in/access-code-sign-in.js +9 -8
- package/dist/matter/access-code-sign-in/access-code-sign-in.js.map +1 -1
- package/dist/matter/account-access-gate/account-access-gate.d.ts +86 -0
- package/dist/matter/account-access-gate/account-access-gate.js +283 -0
- package/dist/matter/account-access-gate/account-access-gate.js.map +1 -0
- package/dist/matter/account-access-gate/sign-out-intent.d.ts +17 -0
- package/dist/matter/account-access-gate/sign-out-intent.js +62 -0
- package/dist/matter/account-access-gate/sign-out-intent.js.map +1 -0
- package/dist/matter/account-entry-point/account-entry-point.js +2 -2
- package/dist/matter/account-entry-point/account-entry-point.js.map +1 -1
- package/dist/matter/account-menu/account-menu.d.ts +88 -0
- package/dist/matter/account-menu/account-menu.js +246 -0
- package/dist/matter/account-menu/account-menu.js.map +1 -0
- package/dist/matter/billing-address/billing-address.d.ts +165 -0
- package/dist/matter/billing-address/billing-address.js +408 -0
- package/dist/matter/billing-address/billing-address.js.map +1 -0
- package/dist/matter/card-entry/card-capture.d.ts +25 -0
- package/dist/matter/card-entry/card-capture.js +25 -0
- package/dist/matter/card-entry/card-capture.js.map +1 -0
- package/dist/matter/card-entry/card-entry.d.ts +120 -0
- package/dist/matter/card-entry/card-entry.js +832 -0
- package/dist/matter/card-entry/card-entry.js.map +1 -0
- package/dist/matter/cardholder-document/cardholder-document.d.ts +45 -0
- package/dist/matter/cardholder-document/cardholder-document.js +70 -0
- package/dist/matter/cardholder-document/cardholder-document.js.map +1 -0
- package/dist/matter/cart/cart.d.ts +1 -1
- package/dist/matter/cart/cart.js +2 -2
- package/dist/matter/cart/cart.js.map +1 -1
- package/dist/matter/client-preferences/client-preferences-fieldset.d.ts +77 -0
- package/dist/matter/client-preferences/client-preferences-fieldset.js +299 -0
- package/dist/matter/client-preferences/client-preferences-fieldset.js.map +1 -0
- package/dist/matter/client-preferences/client-preferences.d.ts +3 -5
- package/dist/matter/client-preferences/client-preferences.js +6 -6
- package/dist/matter/client-preferences/client-preferences.js.map +1 -1
- package/dist/matter/client-profile/client-profile.d.ts +4 -6
- package/dist/matter/client-profile/client-profile.js +9 -6
- package/dist/matter/client-profile/client-profile.js.map +1 -1
- package/dist/matter/client-profile-form/client-profile-fieldset.d.ts +119 -0
- package/dist/matter/client-profile-form/client-profile-fieldset.js +488 -0
- package/dist/matter/client-profile-form/client-profile-fieldset.js.map +1 -0
- package/dist/matter/delivery-options/delivery-options.js.map +1 -1
- package/dist/matter/identity-edit/identity-edit.js +2 -2
- package/dist/matter/identity-edit/identity-edit.js.map +1 -1
- package/dist/matter/identity-summary/identity-summary.js +2 -2
- package/dist/matter/identity-summary/identity-summary.js.map +1 -1
- package/dist/matter/installments/installments-select.d.ts +42 -0
- package/dist/matter/installments/installments-select.js +167 -0
- package/dist/matter/installments/installments-select.js.map +1 -0
- package/dist/matter/installments/installments.d.ts +58 -0
- package/dist/matter/installments/installments.js +176 -0
- package/dist/matter/installments/installments.js.map +1 -0
- package/dist/matter/installments/use-installments-selection.d.ts +29 -0
- package/dist/matter/installments/use-installments-selection.js +95 -0
- package/dist/matter/installments/use-installments-selection.js.map +1 -0
- package/dist/matter/my-account-profile/my-account-profile.js +5 -5
- package/dist/matter/my-account-profile/my-account-profile.js.map +1 -1
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.d.ts +63 -0
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js +305 -0
- package/dist/matter/order-placed-payment-summary/order-placed-payment-summary.js.map +1 -0
- package/dist/matter/payment/payment-card-capture.d.ts +19 -0
- package/dist/matter/payment/payment-card-capture.js +129 -0
- package/dist/matter/payment/payment-card-capture.js.map +1 -0
- package/dist/matter/payment/payment-controller.d.ts +24 -0
- package/dist/matter/payment/payment-controller.js +107 -0
- package/dist/matter/payment/payment-controller.js.map +1 -0
- package/dist/matter/payment/payment-method-badges.d.ts +57 -0
- package/dist/matter/payment/payment-method-badges.js +192 -0
- package/dist/matter/payment/payment-method-badges.js.map +1 -0
- package/dist/matter/payment/payment.d.ts +89 -0
- package/dist/matter/payment/payment.js +263 -0
- package/dist/matter/payment/payment.js.map +1 -0
- package/dist/matter/payment/use-payment-method-selection.d.ts +302 -0
- package/dist/matter/payment/use-payment-method-selection.js +458 -0
- package/dist/matter/payment/use-payment-method-selection.js.map +1 -0
- package/dist/matter/payment/use-payment-submit-gate.d.ts +111 -0
- package/dist/matter/payment/use-payment-submit-gate.js +144 -0
- package/dist/matter/payment/use-payment-submit-gate.js.map +1 -0
- package/dist/matter/place-order/place-order.d.ts +74 -0
- package/dist/matter/place-order/place-order.js +379 -0
- package/dist/matter/place-order/place-order.js.map +1 -0
- package/dist/matter/product-list/product-list.d.ts +1 -0
- package/dist/matter/product-list/product-list.js.map +1 -1
- package/dist/matter/product-shelf/product-shelf.d.ts +1 -0
- package/dist/matter/product-shelf/product-shelf.js +2 -0
- package/dist/matter/product-shelf/product-shelf.js.map +1 -1
- package/dist/matter/region/region.js +1 -1
- package/dist/matter/region/region.js.map +1 -1
- package/dist/matter/save-card/save-card.d.ts +49 -0
- package/dist/matter/save-card/save-card.js +188 -0
- package/dist/matter/save-card/save-card.js.map +1 -0
- package/dist/matter/saved-cards/saved-cards.d.ts +79 -0
- package/dist/matter/saved-cards/saved-cards.js +387 -0
- package/dist/matter/saved-cards/saved-cards.js.map +1 -0
- package/dist/matter/shipping-address/country-descriptors.js +20 -10
- package/dist/matter/shipping-address/country-descriptors.js.map +1 -1
- package/dist/matter/shipping-address/shipping-address.d.ts +113 -174
- package/dist/matter/shipping-address/shipping-address.js +481 -792
- package/dist/matter/shipping-address/shipping-address.js.map +1 -1
- package/dist/matter/shipping-simulator/shipping-simulator.js.map +1 -1
- package/dist/matter/variant-selector/variant-selector.js +1 -1
- package/dist/particles/address-summary/address-summary.d.ts +24 -0
- package/dist/particles/address-summary/address-summary.js +83 -2
- package/dist/particles/address-summary/address-summary.js.map +1 -1
- package/dist/particles/alert-dialog/alert-dialog.d.ts +68 -0
- package/dist/particles/alert-dialog/alert-dialog.js +187 -0
- package/dist/particles/alert-dialog/alert-dialog.js.map +1 -0
- package/dist/particles/checkbox/checkbox.d.ts +7 -3
- package/dist/particles/checkbox/checkbox.js +4 -1
- package/dist/particles/checkbox/checkbox.js.map +1 -1
- package/dist/particles/document-field/document-field.d.ts +48 -5
- package/dist/particles/document-field/document-field.js +189 -32
- package/dist/particles/document-field/document-field.js.map +1 -1
- package/dist/particles/field/bound-field-registry.js +52 -0
- package/dist/particles/field/bound-field-registry.js.map +1 -0
- package/dist/particles/field/field.d.ts +116 -0
- package/dist/particles/field/field.js +497 -6
- package/dist/particles/field/field.js.map +1 -1
- package/dist/particles/fieldset/fieldset.js +2 -2
- package/dist/particles/fieldset/fieldset.js.map +1 -1
- package/dist/particles/footer-social/footer-social.js +1 -1
- package/dist/particles/form/form.d.ts +104 -0
- package/dist/particles/form/form.js +485 -0
- package/dist/particles/form/form.js.map +1 -0
- package/dist/particles/incentives/incentives.js +1 -1
- package/dist/particles/phone-field/phone-field.d.ts +38 -10
- package/dist/particles/phone-field/phone-field.js +183 -39
- package/dist/particles/phone-field/phone-field.js.map +1 -1
- package/dist/particles/postal-code-field/postal-code-field.js +2 -2
- package/dist/particles/postal-code-field/postal-code-field.js.map +1 -1
- package/dist/particles/select/field-select.d.ts +43 -0
- package/dist/particles/select/field-select.js +150 -0
- package/dist/particles/select/field-select.js.map +1 -0
- package/dist/particles/select/select.d.ts +2 -0
- package/dist/particles/select/select.js.map +1 -1
- package/dist/particles/shared/compose-refs.js +21 -0
- package/dist/particles/shared/compose-refs.js.map +1 -0
- package/dist/schema.graphql +701 -2
- package/dist/sdk/actions/analytics-action-provider.d.ts +15 -0
- package/dist/sdk/actions/analytics-action-provider.js +19 -0
- package/dist/sdk/actions/analytics-action-provider.js.map +1 -0
- package/dist/sdk/actions/cart-action-provider.d.ts +1 -1
- package/dist/sdk/actions/cart-action-provider.js +1 -1
- package/dist/sdk/actions/cart-action-provider.js.map +1 -1
- package/dist/sdk/actions/index.d.ts +3 -1
- package/dist/sdk/actions/index.js +3 -1
- package/dist/sdk/actions/payment-action-provider.d.ts +22 -0
- package/dist/sdk/actions/payment-action-provider.js +26 -0
- package/dist/sdk/actions/payment-action-provider.js.map +1 -0
- package/dist/sdk/actions/session-action-provider.d.ts +1 -1
- package/dist/sdk/actions/session-action-provider.js +1 -1
- package/dist/sdk/actions/session-action-provider.js.map +1 -1
- package/dist/sdk/actions/shipping-action-provider.d.ts +1 -1
- package/dist/sdk/actions/shipping-action-provider.js +1 -1
- package/dist/sdk/actions/shipping-action-provider.js.map +1 -1
- package/dist/sdk/actions/types.d.ts +6 -2
- package/dist/sdk/analytics/analytics-destination.d.ts +43 -0
- package/dist/sdk/analytics/analytics-destination.js +90 -0
- package/dist/sdk/analytics/analytics-destination.js.map +1 -0
- package/dist/sdk/analytics/use-analytics-actions.d.ts +57 -0
- package/dist/sdk/analytics/use-analytics-actions.js +108 -0
- package/dist/sdk/analytics/use-analytics-actions.js.map +1 -0
- package/dist/sdk/cache/storefront-cache.js +1 -1
- package/dist/sdk/cache/storefront-cache.js.map +1 -1
- package/dist/sdk/cart/operations.d.ts +4 -4
- package/dist/sdk/cart/operations.js +1 -1
- package/dist/sdk/cart/operations.js.map +1 -1
- package/dist/sdk/cart/optimistic.js +1 -1
- package/dist/sdk/cart/optimistic.js.map +1 -1
- package/dist/sdk/cart/use-cart-actions.d.ts +1 -1
- package/dist/sdk/cart/use-cart-actions.js +4 -4
- package/dist/sdk/cart/use-cart-actions.js.map +1 -1
- package/dist/sdk/cart/use-cart-validate-on-open.js +1 -1
- package/dist/sdk/cart/use-cart-validate-on-open.js.map +1 -1
- package/dist/sdk/cart/write-gate.js +1 -1
- package/dist/sdk/cart/write-gate.js.map +1 -1
- package/dist/sdk/data/entity-resolution-context.js +31 -0
- package/dist/sdk/data/entity-resolution-context.js.map +1 -0
- package/dist/sdk/data/entity-subscriber.js +18 -8
- package/dist/sdk/data/entity-subscriber.js.map +1 -1
- package/dist/sdk/data/index.js +1 -0
- package/dist/sdk/data/page-reactive-data-root.d.ts +38 -2
- package/dist/sdk/data/page-reactive-data-root.js +4 -2
- package/dist/sdk/data/page-reactive-data-root.js.map +1 -1
- package/dist/sdk/data/reactive-data-provider.d.ts +1 -1
- package/dist/sdk/data/reactive-data-provider.js +8 -4
- package/dist/sdk/data/reactive-data-provider.js.map +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.d.ts +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.js +13 -5
- package/dist/sdk/data/reactive-list-item-data-provider.js.map +1 -1
- package/dist/sdk/data/use-reactive-data-settled.js +39 -0
- package/dist/sdk/data/use-reactive-data-settled.js.map +1 -0
- package/dist/sdk/data/use-reactive-entity.js +6 -2
- package/dist/sdk/data/use-reactive-entity.js.map +1 -1
- package/dist/sdk/generated/fragment-types.js +2 -0
- package/dist/sdk/generated/fragment-types.js.map +1 -1
- package/dist/sdk/generated/gql.js +2 -1
- package/dist/sdk/generated/gql.js.map +1 -1
- package/dist/sdk/generated/graphql.js +85 -1
- package/dist/sdk/generated/graphql.js.map +1 -1
- package/dist/sdk/i18n/messages/en-US.js +3 -0
- package/dist/sdk/i18n/messages/en-US.json +3 -0
- package/dist/sdk/i18n/messages/en-US.json.d.ts +3 -0
- package/dist/sdk/i18n/messages/es-ES.js +3 -0
- package/dist/sdk/i18n/messages/es-ES.json +3 -0
- package/dist/sdk/i18n/messages/pt-BR.js +3 -0
- package/dist/sdk/i18n/messages/pt-BR.json +3 -0
- package/dist/sdk/identity/operations.js.map +1 -1
- package/dist/sdk/identity/use-identity-actions.d.ts +1 -1
- package/dist/sdk/identity/use-identity-actions.js +3 -2
- package/dist/sdk/identity/use-identity-actions.js.map +1 -1
- package/dist/sdk/payment/gateway-origin.js +35 -0
- package/dist/sdk/payment/gateway-origin.js.map +1 -0
- package/dist/sdk/payment/gateway.d.ts +85 -0
- package/dist/sdk/payment/gateway.js +183 -0
- package/dist/sdk/payment/gateway.js.map +1 -0
- package/dist/sdk/payment/operations.d.ts +52 -0
- package/dist/sdk/payment/operations.js +223 -0
- package/dist/sdk/payment/operations.js.map +1 -0
- package/dist/sdk/payment/security-code-mask.js +71 -0
- package/dist/sdk/payment/security-code-mask.js.map +1 -0
- package/dist/sdk/payment/submission-registry.d.ts +33 -0
- package/dist/sdk/payment/submission-registry.js +58 -0
- package/dist/sdk/payment/submission-registry.js.map +1 -0
- package/dist/sdk/payment/use-payment-actions.d.ts +78 -0
- package/dist/sdk/payment/use-payment-actions.js +276 -0
- package/dist/sdk/payment/use-payment-actions.js.map +1 -0
- package/dist/sdk/payment/use-place-order.d.ts +65 -0
- package/dist/sdk/payment/use-place-order.js +640 -0
- package/dist/sdk/payment/use-place-order.js.map +1 -0
- package/dist/sdk/session/auth-operations.d.ts +1 -1
- package/dist/sdk/session/auth-operations.js.map +1 -1
- package/dist/sdk/session/use-session-actions.d.ts +1 -1
- package/dist/sdk/session/use-session-actions.js.map +1 -1
- package/dist/sdk/session/use-session-revalidation.js +65 -0
- package/dist/sdk/session/use-session-revalidation.js.map +1 -0
- package/dist/sdk/shipping/option-select-operations.d.ts +1 -1
- package/dist/sdk/shipping/option-select-operations.js.map +1 -1
- package/dist/sdk/shipping/request-timeout.js +1 -1
- package/dist/sdk/shipping/request-timeout.js.map +1 -1
- package/dist/sdk/shipping/shipping-operations.d.ts +1 -1
- package/dist/sdk/shipping/shipping-operations.js.map +1 -1
- package/dist/sdk/shipping/use-shipping-actions.d.ts +2 -2
- package/dist/sdk/shipping/use-shipping-actions.js +1 -1
- package/dist/sdk/shipping/use-shipping-actions.js.map +1 -1
- package/dist/sdk/state/cart.d.ts +3 -4
- package/dist/sdk/state/cart.js +6 -7
- package/dist/sdk/state/cart.js.map +1 -1
- package/dist/utils/loggable-error-name.js +49 -0
- package/dist/utils/loggable-error-name.js.map +1 -0
- package/dist/utils/resolve-authored-copy.js +18 -0
- package/dist/utils/resolve-authored-copy.js.map +1 -0
- package/dist/utils/safe-href.js +3 -3
- package/dist/utils/safe-href.js.map +1 -1
- package/dist/utils/substitute-template.js +33 -0
- package/dist/utils/substitute-template.js.map +1 -0
- package/package.json +2 -1
- package/src/styles/tailwind.css +34 -0
- package/dist/docs/client-profile-form.md +0 -192
- package/dist/matter/client-profile-form/client-profile-form.d.ts +0 -126
- package/dist/matter/client-profile-form/client-profile-form.js +0 -662
- package/dist/matter/client-profile-form/client-profile-form.js.map +0 -1
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { usePaymentSubmission } from "./submission-registry.js";
|
|
3
|
+
import { PAYMENT_NETWORK_ERROR_CODE } from "./operations.js";
|
|
4
|
+
import { loggableErrorName } from "../../utils/loggable-error-name.js";
|
|
5
|
+
import { isTrustedGatewayOrigin } from "./gateway-origin.js";
|
|
6
|
+
import { useCallback, useMemo, useRef, useState } from "react";
|
|
7
|
+
//#region src/sdk/payment/use-place-order.ts
|
|
8
|
+
/**
|
|
9
|
+
* The place-order orchestration (specs/use-place-order.md, T13; docs/features/credit-card/rfc.md
|
|
10
|
+
* § D9) — the PCI-critical core of the credit-card feature, and the only module that knows placing an
|
|
11
|
+
* order is three calls rather than one mutation.
|
|
12
|
+
*
|
|
13
|
+
* **This hook never holds card data.** It resolves an opaque continuation from the submission channel
|
|
14
|
+
* by the shopper's entry path and invokes it between the two backend legs; the card stays in the
|
|
15
|
+
* holder's closure (T7 for a new card, T12 for a saved one), which calls
|
|
16
|
+
* `$actions.payment.sendPayments` itself. So the actions this hook needs are `startTransaction`,
|
|
17
|
+
* `gatewayCallback`, and `analytics.track` — never the gateway façade. Two tests assert the boundary
|
|
18
|
+
* against this file's own source text: no card field is named in code, and the single import from
|
|
19
|
+
* `./gateway.js` is the transaction descriptor's type.
|
|
20
|
+
*
|
|
21
|
+
* The order of the pre-flight is load-bearing, not stylistic. The path, the continuation, and the
|
|
22
|
+
* selected method are all resolved *before* `startTransaction` runs, because a transaction that is
|
|
23
|
+
* opened and then cannot be completed is dangling state on the shopper's order form.
|
|
24
|
+
*
|
|
25
|
+
* v1 is the direct-API mode: the gateway leg resolves inline and the shopper stays on the page. The
|
|
26
|
+
* redirect mode — an external navigation with a resume-on-callback route, and with it 3D Secure — is
|
|
27
|
+
* phase 2, which is why no `callbackUrl` reaches the descriptor.
|
|
28
|
+
*
|
|
29
|
+
* Every failure resolves as `userErrors`; nothing here throws for an expected outcome, so the CTA
|
|
30
|
+
* (T14) renders one error path. Its one per-code branch is PLACE_ORDER_UNCONFIRMED, a pending outcome
|
|
31
|
+
* it shows as a status rather than an error (T176).
|
|
32
|
+
*/
|
|
33
|
+
/** No entry path chosen, or no holder registered for the one that was. */
|
|
34
|
+
const PAYMENT_MISSING_SUBMISSION_CODE = "MISSING_PAYMENT_SUBMISSION";
|
|
35
|
+
/**
|
|
36
|
+
* No payment method selected. Reuses `PaymentStartTransactionErrorCode`'s own value for the same
|
|
37
|
+
* state, so a consumer matches one code whichever side detected it.
|
|
38
|
+
*/
|
|
39
|
+
const PAYMENT_MISSING_METHOD_CODE = "MISSING_PAYMENT_METHOD_SELECTION";
|
|
40
|
+
/**
|
|
41
|
+
* The transaction opened but cannot be paid — an incomplete payload, or an origin this client will
|
|
42
|
+
* not post a card to. Reuses the server's code for the same meaning (T18 Decision 5).
|
|
43
|
+
*/
|
|
44
|
+
const PAYMENT_INVALID_TRANSACTION_CODE = "INVALID_TRANSACTION_DATA";
|
|
45
|
+
/**
|
|
46
|
+
* The attempt's own sequence is still running — opened or finalizing, not yet settled. Not a
|
|
47
|
+
* refusal: nothing failed, the record just belongs to a sequence this caller did not start.
|
|
48
|
+
*/
|
|
49
|
+
const PLACE_ORDER_IN_FLIGHT_CODE = "PLACE_ORDER_IN_FLIGHT";
|
|
50
|
+
/**
|
|
51
|
+
* The gatewayCallback leg ran after the gateway accepted the card POST, and this client could not confirm
|
|
52
|
+
* its outcome — the payment may or may not have been authorized. Not a refusal (T176).
|
|
53
|
+
*/
|
|
54
|
+
const PLACE_ORDER_UNCONFIRMED_CODE = "PLACE_ORDER_UNCONFIRMED";
|
|
55
|
+
const MISSING_SUBMISSION_ERROR = {
|
|
56
|
+
code: PAYMENT_MISSING_SUBMISSION_CODE,
|
|
57
|
+
message: "Enter your payment details before placing the order."
|
|
58
|
+
};
|
|
59
|
+
const MISSING_METHOD_ERROR = {
|
|
60
|
+
code: PAYMENT_MISSING_METHOD_CODE,
|
|
61
|
+
message: "Choose a payment method before placing the order."
|
|
62
|
+
};
|
|
63
|
+
const INVALID_TRANSACTION_ERROR = {
|
|
64
|
+
code: PAYMENT_INVALID_TRANSACTION_CODE,
|
|
65
|
+
message: "Unable to start the payment. Please try again."
|
|
66
|
+
};
|
|
67
|
+
const PLACE_ORDER_IN_FLIGHT_ERROR = {
|
|
68
|
+
code: PLACE_ORDER_IN_FLIGHT_CODE,
|
|
69
|
+
message: "This order is already being placed. Please wait."
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Worded as a pending state, never a failure: it must not tell the shopper to try again (FR-1).
|
|
73
|
+
* `PlaceOrder`'s `unconfirmedMessage` schema default repeats this text verbatim.
|
|
74
|
+
*/
|
|
75
|
+
const PLACE_ORDER_UNCONFIRMED_ERROR = {
|
|
76
|
+
code: PLACE_ORDER_UNCONFIRMED_CODE,
|
|
77
|
+
message: "We could not confirm your order yet. Your payment may still be processing, so please do not pay again."
|
|
78
|
+
};
|
|
79
|
+
/** Mirrors `usePaymentActions`' own infrastructure-failure userError, which that module keeps private. */
|
|
80
|
+
const NETWORK_ERROR = {
|
|
81
|
+
code: PAYMENT_NETWORK_ERROR_CODE,
|
|
82
|
+
message: "Unable to complete the request. Please try again."
|
|
83
|
+
};
|
|
84
|
+
/** Identifiers, a host, and amounts — nothing here is sensitive, so naming them in a log is safe. */
|
|
85
|
+
function missingTransactionFields(payload) {
|
|
86
|
+
return Object.entries({
|
|
87
|
+
transactionId: payload.transactionId,
|
|
88
|
+
merchantName: payload.merchantName,
|
|
89
|
+
orderGroup: payload.orderGroup,
|
|
90
|
+
value: payload.value,
|
|
91
|
+
valueInCents: payload.valueInCents,
|
|
92
|
+
referenceValueInCents: payload.referenceValueInCents
|
|
93
|
+
}).filter(([, field]) => field === null).map(([name]) => name);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Narrows a payload that reported no userErrors, or refuses it. The origin is checked first and
|
|
97
|
+
* separately: it is the field that decides where a card is sent, and a refusal here happens before
|
|
98
|
+
* any holder is asked to assemble one.
|
|
99
|
+
*/
|
|
100
|
+
function settleTransaction(payload) {
|
|
101
|
+
if (!isTrustedGatewayOrigin(payload.gatewayUrl)) {
|
|
102
|
+
console.error("[payment.placeOrder] the opened transaction carries no trusted https gateway origin");
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const { transactionId, merchantName, orderGroup, userProfileId, value, valueInCents, referenceValueInCents } = payload;
|
|
106
|
+
if (transactionId === null || merchantName === null || orderGroup === null || value === null || valueInCents === null || referenceValueInCents === null) {
|
|
107
|
+
console.error("[payment.placeOrder] the opened transaction payload is incomplete", missingTransactionFields(payload).join(", "));
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
gatewayUrl: payload.gatewayUrl,
|
|
112
|
+
transactionId,
|
|
113
|
+
merchantName,
|
|
114
|
+
orderGroup,
|
|
115
|
+
userProfileId,
|
|
116
|
+
value,
|
|
117
|
+
valueInCents,
|
|
118
|
+
referenceValueInCents
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The one derived amount on the descriptor. VTEX's `payments[0]` carries no per-installment minor-unit
|
|
123
|
+
* value, so T18 could not expose one and the conversion stays here — rounded, because
|
|
124
|
+
* `19.99 * 100 === 1998.9999999999998`. `Number()` is not redundant: the components codegen maps the
|
|
125
|
+
* `Decimal` scalar to `number` while the resolver serializes it as a string (see the spec's Open items).
|
|
126
|
+
*/
|
|
127
|
+
function toMinorUnits(amount) {
|
|
128
|
+
return Math.round(Number(amount) * 100);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The one call into holder-owned code. A holder's continuation is specified to resolve `userErrors` —
|
|
132
|
+
* `sendPayments` already collapses every gateway outcome to one — so a throw means its contract broke.
|
|
133
|
+
* Only a sanitised error *name* is logged (T160): the closure that threw holds the card, and a message
|
|
134
|
+
* or stack could echo it — and so, unsanitised, could `.name` itself, since it is a writable own
|
|
135
|
+
* property. See `loggableErrorName()`.
|
|
136
|
+
*/
|
|
137
|
+
async function invokeSubmission(submit, transaction) {
|
|
138
|
+
try {
|
|
139
|
+
const result = await submit(transaction);
|
|
140
|
+
if (result == null) {
|
|
141
|
+
console.error("[payment.placeOrder] the registered submission resolved with no result");
|
|
142
|
+
return [NETWORK_ERROR];
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error("[payment.placeOrder] the registered submission threw", loggableErrorName(error));
|
|
147
|
+
return [NETWORK_ERROR];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The pre-flight counterpart of `invokeSubmission`, and it inherits that function's one rule: the
|
|
152
|
+
* closure that throws holds the card, so only a sanitised error *name* is logged (T160) — never a
|
|
153
|
+
* message, a stack, or the raw writable `.name` property. See `loggableErrorName()`.
|
|
154
|
+
*/
|
|
155
|
+
function runPreflight(check) {
|
|
156
|
+
try {
|
|
157
|
+
return check();
|
|
158
|
+
} catch (error) {
|
|
159
|
+
console.error("[payment.placeOrder] the registered pre-flight threw", loggableErrorName(error));
|
|
160
|
+
return [NETWORK_ERROR];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The `startTransaction` counterpart of `invokeSubmission`/`runPreflight` (fix pass, Medium finding):
|
|
165
|
+
* before this, `run()` awaited `startTransaction()` directly, so a **rejection** — as opposed to a
|
|
166
|
+
* resolved payload carrying `userErrors` — escaped past every one of `run()`'s own outcome branches
|
|
167
|
+
* and `settleSequence`'s rejection handler never touches the attempt record, leaving it stuck at
|
|
168
|
+
* `opening` for the payment root's lifetime. `opening` is the one phase `placeOrder()` treats as
|
|
169
|
+
* "already running" with no failure exit, so every later click resolves `{ orderId: null, userErrors:
|
|
170
|
+
* [] }` without running anything — a false "Order placed successfully." with the CTA disabled forever.
|
|
171
|
+
* `usePlaceOrder` is exported public API and `$actions` is compiler-injected and pluggable, so a
|
|
172
|
+
* provider that rejects here is reachable, not hypothetical. Collapsing the rejection to a resolved
|
|
173
|
+
* payload with one `userErrors` entry means `run()`'s own existing `payload.userErrors.length > 0`
|
|
174
|
+
* branch is what catches it — the same `refused` outcome a server-reported refusal already gets,
|
|
175
|
+
* correct here too since nothing can have been paid before the transaction even opened.
|
|
176
|
+
*
|
|
177
|
+
* T156 fold: the result is destructured inside this `try`, not returned as `await startTransaction()`
|
|
178
|
+
* untouched — a provider resolving `undefined` used to throw at the caller's own
|
|
179
|
+
* `payload.userErrors.length` read, past this function's own `catch`, latching the attempt at
|
|
180
|
+
* `opening` exactly like the rejection case above. Destructuring a nullish resolution throws here
|
|
181
|
+
* instead, so it collapses the same way. Mirrors `invokeGatewayCallback`'s own reason for
|
|
182
|
+
* destructuring, below.
|
|
183
|
+
*
|
|
184
|
+
* Fix pass (review F1/S1): `userErrors` is defaulted with `?? [NETWORK_ERROR]`, not `?? []` — a
|
|
185
|
+
* *partial* payload (present, but missing `userErrors`, e.g. a provider whose GraphQL selection omits
|
|
186
|
+
* the field) used to leave it `undefined`, reaching the caller's `payload.userErrors.length` read
|
|
187
|
+
* (`:627`) and throwing past this function's own `catch`, latching the attempt at `opening` exactly
|
|
188
|
+
* like the cases already fixed. `userErrors` is this result's only signal of whether anything actually
|
|
189
|
+
* went wrong, so a payload that omits it defaults to a *failure*, mirroring a rejection — defaulting to
|
|
190
|
+
* `[]` instead would settle the attempt on a payload this function never actually saw. (The in-repo
|
|
191
|
+
* default provider's own `payload?.userErrors ?? []`, `use-payment-actions.ts:202`, defaults to `[]`
|
|
192
|
+
* only because there `payload` itself — not just the field — is what may be missing.)
|
|
193
|
+
*/
|
|
194
|
+
async function invokeStartTransaction(startTransaction) {
|
|
195
|
+
try {
|
|
196
|
+
const { transactionId, merchantName, gatewayUrl, orderGroup, userProfileId, value, valueInCents, referenceValueInCents, userErrors } = await startTransaction();
|
|
197
|
+
if (userErrors == null) console.error("[payment.placeOrder] startTransaction resolved with no userErrors", orderGroup);
|
|
198
|
+
return {
|
|
199
|
+
transactionId,
|
|
200
|
+
merchantName,
|
|
201
|
+
gatewayUrl,
|
|
202
|
+
orderGroup,
|
|
203
|
+
userProfileId,
|
|
204
|
+
value,
|
|
205
|
+
valueInCents,
|
|
206
|
+
referenceValueInCents,
|
|
207
|
+
userErrors: userErrors ?? [NETWORK_ERROR]
|
|
208
|
+
};
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.error("[payment.placeOrder] startTransaction threw", loggableErrorName(error));
|
|
211
|
+
return {
|
|
212
|
+
transactionId: null,
|
|
213
|
+
merchantName: null,
|
|
214
|
+
gatewayUrl: null,
|
|
215
|
+
orderGroup: null,
|
|
216
|
+
userProfileId: null,
|
|
217
|
+
value: null,
|
|
218
|
+
valueInCents: null,
|
|
219
|
+
referenceValueInCents: null,
|
|
220
|
+
userErrors: [NETWORK_ERROR]
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* The `gatewayCallback` counterpart of `invokeStartTransaction` above (T156, PR #756 review R-004):
|
|
226
|
+
* before this, both call sites below awaited `gatewayCallback` directly, so a **rejection**
|
|
227
|
+
* escaped past every outcome branch and `settleSequence`'s rejection handler never touches the attempt
|
|
228
|
+
* record, leaving it stuck at `finalizing` for the payment root's lifetime — `placeOrder()` treats
|
|
229
|
+
* `finalizing` exactly like `opening`, an already-running phase with no failure exit, so every later
|
|
230
|
+
* click resolved `{ orderId: attempt.orderId, userErrors: [] }` without issuing anything.
|
|
231
|
+
*
|
|
232
|
+
* The destination is `unresolved`, never `refused` like `invokeStartTransaction`'s own collapse: this
|
|
233
|
+
* leg only runs once the card already reached the gateway, so nothing here can pretend no money moved.
|
|
234
|
+
* `unresolved` is exactly the phase `PlaceOrderAttemptPhase` already documents for this case, below,
|
|
235
|
+
* and is already resumable.
|
|
236
|
+
*
|
|
237
|
+
* Three failure shapes collapse to the same `NETWORK_ERROR` result, logged distinctly (fix pass,
|
|
238
|
+
* review F1/S1/F3): a **rejection**, caught below; a **nullish resolution** (`undefined`/`null`),
|
|
239
|
+
* caught by the explicit check that follows rather than by letting a destructure throw, so it logs a
|
|
240
|
+
* line of its own instead of printing identically to a rejection; and a **partial resolution** — an
|
|
241
|
+
* object present but missing `userErrors`, e.g. a provider whose GraphQL selection omits the field —
|
|
242
|
+
* defaulted with `?? [NETWORK_ERROR]`, not `?? []`. That last case used to return `{ orderId,
|
|
243
|
+
* userErrors }` untouched, leaving `userErrors: undefined`, so the *caller's* `callback.userErrors.length`
|
|
244
|
+
* read threw one call site away from where this wrapper's whole job is to stop exactly that. `[]` would
|
|
245
|
+
* default a payload this function never actually saw to a clean success; `userErrors` is the only
|
|
246
|
+
* signal of whether the callback settled anything, so omitting it means treating it as a failure.
|
|
247
|
+
* Both failure branches log `orderGroup` and `attemptId` — neither is card data
|
|
248
|
+
* (`missingTransactionFields`'s comment above), and this is the one point in the flow where money may
|
|
249
|
+
* already have moved — but never the error's message, stack, or any call argument (FR-6).
|
|
250
|
+
*
|
|
251
|
+
* No `gatewayCallback` call may exist in this module outside this function (INV-2, asserted against
|
|
252
|
+
* this file's own source text in use-place-order.test.tsx) — the two call sites below (`resume()` and
|
|
253
|
+
* `run()`) call this and nothing else.
|
|
254
|
+
*/
|
|
255
|
+
async function invokeGatewayCallback(gatewayCallback, orderGroup, attemptId) {
|
|
256
|
+
let result;
|
|
257
|
+
try {
|
|
258
|
+
result = await gatewayCallback(orderGroup);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error("[payment.placeOrder] gatewayCallback threw", orderGroup, attemptId, loggableErrorName(error));
|
|
261
|
+
return {
|
|
262
|
+
orderId: null,
|
|
263
|
+
userErrors: [NETWORK_ERROR]
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
if (result == null) {
|
|
267
|
+
console.error("[payment.placeOrder] gatewayCallback resolved with no result", orderGroup, attemptId);
|
|
268
|
+
return {
|
|
269
|
+
orderId: null,
|
|
270
|
+
userErrors: [NETWORK_ERROR]
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
if (result.userErrors == null) console.error("[payment.placeOrder] gatewayCallback resolved with no userErrors", orderGroup, attemptId);
|
|
274
|
+
return {
|
|
275
|
+
orderId: result.orderId ?? null,
|
|
276
|
+
userErrors: result.userErrors ?? [NETWORK_ERROR]
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* T176 (FR-2, FR-3): what the caller reads from a `gatewayCallback` that reported `userErrors`, on both
|
|
281
|
+
* paths that issue it. The gateway already accepted the card POST by then, so a result made only of this
|
|
282
|
+
* package's own collapse — `PAYMENT_NETWORK_ERROR_CODE`, which `usePaymentActions` and
|
|
283
|
+
* `invokeGatewayCallback` resolve for every thrown, lost or shapeless response — says nothing about
|
|
284
|
+
* whether VTEX processed the order. It becomes PLACE_ORDER_UNCONFIRMED rather than a "try again" that
|
|
285
|
+
* invites a second payment. A code the server sent is an answer, and comes back unchanged (INV-10).
|
|
286
|
+
*
|
|
287
|
+
* Total, and never throws: a non-array, or an entry with no readable string `code`, carries no server
|
|
288
|
+
* code, so it reads as unconfirmed too. A server code elsewhere in the same list still wins, whatever
|
|
289
|
+
* order the entries come in.
|
|
290
|
+
*/
|
|
291
|
+
function reconcileCallbackUserErrors(userErrors) {
|
|
292
|
+
try {
|
|
293
|
+
if (!Array.isArray(userErrors)) return [PLACE_ORDER_UNCONFIRMED_ERROR];
|
|
294
|
+
if (userErrors.length === 0) return userErrors;
|
|
295
|
+
return userErrors.some((userError) => {
|
|
296
|
+
try {
|
|
297
|
+
const code = userError.code;
|
|
298
|
+
return typeof code === "string" && code !== "NETWORK_ERROR";
|
|
299
|
+
} catch {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
}) ? userErrors : [PLACE_ORDER_UNCONFIRMED_ERROR];
|
|
303
|
+
} catch {
|
|
304
|
+
return [PLACE_ORDER_UNCONFIRMED_ERROR];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* `crypto.randomUUID()` is secure-context-only (fix pass, Low finding) — on a plain-HTTP preview or
|
|
309
|
+
* staging origin the method does not exist on `crypto` at all, so calling it throws, and every click
|
|
310
|
+
* collapses to a generic `NETWORK_ERROR` with no diagnostic. `localhost` is always a secure context,
|
|
311
|
+
* so dev never reproduces it. Falls back to a timestamp+random string, which only needs to be unique
|
|
312
|
+
* for one payment root's lifetime (the staleness guard below is its one reader) — not a real UUID.
|
|
313
|
+
*/
|
|
314
|
+
function generateAttemptId() {
|
|
315
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
316
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Fix pass (High finding, race #2): guards every write past an attempt's genesis against a stale,
|
|
320
|
+
* out-of-order completion clobbering a newer outcome. `run()`/`resume()` mint one write *before* their
|
|
321
|
+
* own first await (safe by construction — nothing can have superseded an attempt this call itself just
|
|
322
|
+
* created) and one or more writes *after* an await, once `startTransaction`/the continuation/
|
|
323
|
+
* `gatewayCallback` resolve. Because the writing closure keeps running after its own CTA unmounts
|
|
324
|
+
* (NFR-3), that later write cannot assume the shared attempt is still the one it started with — a
|
|
325
|
+
* remounted or sibling instance may have already resumed it to a newer phase in the meantime. Using
|
|
326
|
+
* the setter's own functional-update form (`SetPlaceOrderAttempt`) checks against whatever the shared
|
|
327
|
+
* attempt actually is *when the write is applied*, not the value this closure captured before its
|
|
328
|
+
* await — mirrors `selectionToken`'s own precedent (`matter/payment/use-payment-method-selection.ts`)
|
|
329
|
+
* for the identical shape of problem, keyed on the attempt's own id instead of a second counter.
|
|
330
|
+
* `settled` is additionally excluded because INV-6 forbids any transition away from it.
|
|
331
|
+
*/
|
|
332
|
+
function writeAttemptIfCurrent(setAttempt, attemptId, next) {
|
|
333
|
+
setAttempt?.((prev) => prev !== null && prev.attemptId === attemptId && prev.phase !== "settled" ? next : prev);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* T29 (Decision 3, FR-3): the only path back into a `sent`/`unresolved` attempt — `gatewayCallback` is
|
|
337
|
+
* the sole call it ever issues. Module scope, not a `useCallback` inside the hook (fix pass, Low
|
|
338
|
+
* finding): the previous version of this doc comment claimed `startTransaction`, the submission
|
|
339
|
+
* channel, and `PaymentGatewayTransaction` were "not in scope," so a careless future edit could not
|
|
340
|
+
* make a resume re-pay — true at runtime today (verified by identifier sweep) but false as a
|
|
341
|
+
* structural guarantee, since all three were merely unreferenced identifiers a nested closure could
|
|
342
|
+
* still have read. Hoisting here makes the claim actually true: none of those three is even
|
|
343
|
+
* *importable* from a bare module-level function that takes only `gatewayCallback`/`setAttempt`/
|
|
344
|
+
* `setPlacing`/`current` as arguments — a resume is safe by construction, not by convention.
|
|
345
|
+
*/
|
|
346
|
+
async function resume(gatewayCallback, setAttempt, setPlacing, current) {
|
|
347
|
+
if (!gatewayCallback || current.orderGroup === null) {
|
|
348
|
+
console.error("[payment.placeOrder] cannot resume an attempt with no order group");
|
|
349
|
+
return {
|
|
350
|
+
orderId: null,
|
|
351
|
+
userErrors: [NETWORK_ERROR]
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
writeAttemptIfCurrent(setAttempt, current.attemptId, {
|
|
355
|
+
...current,
|
|
356
|
+
phase: "finalizing"
|
|
357
|
+
});
|
|
358
|
+
setPlacing(true);
|
|
359
|
+
const callback = await invokeGatewayCallback(gatewayCallback, current.orderGroup, current.attemptId);
|
|
360
|
+
const failed = callback.userErrors.length > 0;
|
|
361
|
+
const result = failed ? {
|
|
362
|
+
orderId: null,
|
|
363
|
+
userErrors: reconcileCallbackUserErrors(callback.userErrors)
|
|
364
|
+
} : {
|
|
365
|
+
orderId: callback.orderId,
|
|
366
|
+
userErrors: []
|
|
367
|
+
};
|
|
368
|
+
try {
|
|
369
|
+
writeAttemptIfCurrent(setAttempt, current.attemptId, failed ? {
|
|
370
|
+
...current,
|
|
371
|
+
phase: "unresolved"
|
|
372
|
+
} : {
|
|
373
|
+
...current,
|
|
374
|
+
phase: "settled",
|
|
375
|
+
orderId: callback.orderId
|
|
376
|
+
});
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.error("[payment.placeOrder] the resume record write threw", current.attemptId, loggableErrorName(error));
|
|
379
|
+
}
|
|
380
|
+
return result;
|
|
381
|
+
}
|
|
382
|
+
function usePlaceOrder({ $data, $actions, attempt, setAttempt, billingAddressDifferent, submitGate }) {
|
|
383
|
+
const { path, resolve, resolvePreflight } = usePaymentSubmission();
|
|
384
|
+
const [placing, setPlacing] = useState(false);
|
|
385
|
+
const [userErrors, setUserErrors] = useState([]);
|
|
386
|
+
const [orderId, setOrderId] = useState(null);
|
|
387
|
+
const inFlight = useRef(null);
|
|
388
|
+
const startTransaction = $actions?.payment?.startTransaction;
|
|
389
|
+
const gatewayCallback = $actions?.payment?.gatewayCallback;
|
|
390
|
+
const track = $actions?.analytics?.track;
|
|
391
|
+
const selectedMethodId = $data?.selectedMethodId ?? null;
|
|
392
|
+
const methods = $data?.methods;
|
|
393
|
+
const selectedInstallment = $data?.selectedInstallment ?? null;
|
|
394
|
+
const selectedMethod = useMemo(() => methods?.find((method) => method.id === selectedMethodId) ?? null, [methods, selectedMethodId]);
|
|
395
|
+
const run = useCallback(async () => {
|
|
396
|
+
if (!startTransaction || !gatewayCallback) {
|
|
397
|
+
console.error("[payment.placeOrder] the payment action namespace is not injected");
|
|
398
|
+
return {
|
|
399
|
+
orderId: null,
|
|
400
|
+
userErrors: [NETWORK_ERROR]
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
const submit = path === null ? null : resolve(path);
|
|
404
|
+
if (submit === null) {
|
|
405
|
+
console.error("[payment.placeOrder] no submission is registered for the entry path", path ?? "none");
|
|
406
|
+
return {
|
|
407
|
+
orderId: null,
|
|
408
|
+
userErrors: [MISSING_SUBMISSION_ERROR]
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
if (selectedMethod === null) {
|
|
412
|
+
console.error("[payment.placeOrder] no payment method is selected");
|
|
413
|
+
return {
|
|
414
|
+
orderId: null,
|
|
415
|
+
userErrors: [MISSING_METHOD_ERROR]
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
if (submitGate) {
|
|
419
|
+
console.error("[payment.placeOrder] a client-side gate refused the submission", submitGate.code);
|
|
420
|
+
return {
|
|
421
|
+
orderId: null,
|
|
422
|
+
userErrors: [submitGate]
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
const preflight = path === null ? null : resolvePreflight(path);
|
|
426
|
+
if (preflight) {
|
|
427
|
+
const refusals = runPreflight(preflight);
|
|
428
|
+
if (refusals.length > 0) {
|
|
429
|
+
console.error("[payment.placeOrder] the registered pre-flight refused the submission", refusals[0]?.code);
|
|
430
|
+
return {
|
|
431
|
+
orderId: null,
|
|
432
|
+
userErrors: refusals
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
let handedOver = false;
|
|
437
|
+
let settledOrderGroup = null;
|
|
438
|
+
let placedAttempt = null;
|
|
439
|
+
let outcome = null;
|
|
440
|
+
const attemptId = generateAttemptId();
|
|
441
|
+
setAttempt?.({
|
|
442
|
+
attemptId,
|
|
443
|
+
phase: "opening",
|
|
444
|
+
orderGroup: null,
|
|
445
|
+
orderId: null
|
|
446
|
+
});
|
|
447
|
+
try {
|
|
448
|
+
setPlacing(true);
|
|
449
|
+
const payload = await invokeStartTransaction(startTransaction);
|
|
450
|
+
if (payload.userErrors.length > 0) {
|
|
451
|
+
writeAttemptIfCurrent(setAttempt, attemptId, {
|
|
452
|
+
attemptId,
|
|
453
|
+
phase: "refused",
|
|
454
|
+
orderGroup: null,
|
|
455
|
+
orderId: null
|
|
456
|
+
});
|
|
457
|
+
return {
|
|
458
|
+
orderId: null,
|
|
459
|
+
userErrors: payload.userErrors
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
const settled = settleTransaction(payload);
|
|
463
|
+
if (settled === null) {
|
|
464
|
+
writeAttemptIfCurrent(setAttempt, attemptId, {
|
|
465
|
+
attemptId,
|
|
466
|
+
phase: "refused",
|
|
467
|
+
orderGroup: null,
|
|
468
|
+
orderId: null
|
|
469
|
+
});
|
|
470
|
+
return {
|
|
471
|
+
orderId: null,
|
|
472
|
+
userErrors: [INVALID_TRANSACTION_ERROR]
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
settledOrderGroup = settled.orderGroup;
|
|
476
|
+
writeAttemptIfCurrent(setAttempt, attemptId, {
|
|
477
|
+
attemptId,
|
|
478
|
+
phase: "opening",
|
|
479
|
+
orderGroup: settled.orderGroup,
|
|
480
|
+
orderId: null
|
|
481
|
+
});
|
|
482
|
+
const transaction = {
|
|
483
|
+
gatewayUrl: settled.gatewayUrl,
|
|
484
|
+
transactionId: settled.transactionId,
|
|
485
|
+
merchantName: settled.merchantName,
|
|
486
|
+
orderGroup: settled.orderGroup,
|
|
487
|
+
userProfileId: settled.userProfileId,
|
|
488
|
+
paymentSystem: selectedMethod.id,
|
|
489
|
+
groupName: selectedMethod.groupName,
|
|
490
|
+
currencyCode: settled.value.currencyCode,
|
|
491
|
+
valueInCents: settled.valueInCents,
|
|
492
|
+
referenceValueInCents: settled.referenceValueInCents,
|
|
493
|
+
installments: selectedInstallment?.count ?? 1,
|
|
494
|
+
installmentsValueInCents: selectedInstallment && selectedInstallment.count !== 1 ? toMinorUnits(selectedInstallment.value.amount) : settled.valueInCents,
|
|
495
|
+
interestRate: selectedInstallment?.interestRate ?? 0,
|
|
496
|
+
...billingAddressDifferent != null ? { billingAddressDifferent } : {}
|
|
497
|
+
};
|
|
498
|
+
handedOver = true;
|
|
499
|
+
const submissionErrors = await invokeSubmission(submit, transaction);
|
|
500
|
+
if (submissionErrors.length > 0) {
|
|
501
|
+
outcome = {
|
|
502
|
+
orderId: null,
|
|
503
|
+
userErrors: submissionErrors
|
|
504
|
+
};
|
|
505
|
+
writeAttemptIfCurrent(setAttempt, attemptId, {
|
|
506
|
+
attemptId,
|
|
507
|
+
phase: "unresolved",
|
|
508
|
+
orderGroup: settled.orderGroup,
|
|
509
|
+
orderId: null
|
|
510
|
+
});
|
|
511
|
+
return outcome;
|
|
512
|
+
}
|
|
513
|
+
writeAttemptIfCurrent(setAttempt, attemptId, {
|
|
514
|
+
attemptId,
|
|
515
|
+
phase: "finalizing",
|
|
516
|
+
orderGroup: settled.orderGroup,
|
|
517
|
+
orderId: null
|
|
518
|
+
});
|
|
519
|
+
const callback = await invokeGatewayCallback(gatewayCallback, settled.orderGroup, attemptId);
|
|
520
|
+
if (callback.userErrors.length > 0) {
|
|
521
|
+
outcome = {
|
|
522
|
+
orderId: null,
|
|
523
|
+
userErrors: reconcileCallbackUserErrors(callback.userErrors)
|
|
524
|
+
};
|
|
525
|
+
writeAttemptIfCurrent(setAttempt, attemptId, {
|
|
526
|
+
attemptId,
|
|
527
|
+
phase: "unresolved",
|
|
528
|
+
orderGroup: settled.orderGroup,
|
|
529
|
+
orderId: null
|
|
530
|
+
});
|
|
531
|
+
return outcome;
|
|
532
|
+
}
|
|
533
|
+
placedAttempt = {
|
|
534
|
+
attemptId,
|
|
535
|
+
phase: "settled",
|
|
536
|
+
orderGroup: settled.orderGroup,
|
|
537
|
+
orderId: callback.orderId
|
|
538
|
+
};
|
|
539
|
+
outcome = {
|
|
540
|
+
orderId: callback.orderId,
|
|
541
|
+
userErrors: []
|
|
542
|
+
};
|
|
543
|
+
track?.({
|
|
544
|
+
name: "purchase",
|
|
545
|
+
transactionId: callback.orderId ?? settled.transactionId,
|
|
546
|
+
value: {
|
|
547
|
+
amount: Number(settled.value.amount),
|
|
548
|
+
currencyCode: settled.value.currencyCode
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
writeAttemptIfCurrent(setAttempt, attemptId, placedAttempt);
|
|
552
|
+
return outcome;
|
|
553
|
+
} catch (error) {
|
|
554
|
+
console.error("[payment.placeOrder] the place-order sequence threw", attemptId, loggableErrorName(error));
|
|
555
|
+
try {
|
|
556
|
+
writeAttemptIfCurrent(setAttempt, attemptId, placedAttempt ?? (handedOver ? {
|
|
557
|
+
attemptId,
|
|
558
|
+
phase: "unresolved",
|
|
559
|
+
orderGroup: settledOrderGroup,
|
|
560
|
+
orderId: null
|
|
561
|
+
} : {
|
|
562
|
+
attemptId,
|
|
563
|
+
phase: "refused",
|
|
564
|
+
orderGroup: null,
|
|
565
|
+
orderId: null
|
|
566
|
+
}));
|
|
567
|
+
} catch (writeError) {
|
|
568
|
+
console.error("[payment.placeOrder] the place-order recovery write threw", attemptId, loggableErrorName(writeError));
|
|
569
|
+
}
|
|
570
|
+
return outcome ?? {
|
|
571
|
+
orderId: null,
|
|
572
|
+
userErrors: [NETWORK_ERROR]
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
}, [
|
|
576
|
+
startTransaction,
|
|
577
|
+
gatewayCallback,
|
|
578
|
+
track,
|
|
579
|
+
path,
|
|
580
|
+
resolve,
|
|
581
|
+
resolvePreflight,
|
|
582
|
+
selectedMethod,
|
|
583
|
+
selectedInstallment,
|
|
584
|
+
billingAddressDifferent,
|
|
585
|
+
submitGate,
|
|
586
|
+
setAttempt
|
|
587
|
+
]);
|
|
588
|
+
const placeOrder = useCallback(() => {
|
|
589
|
+
if (inFlight.current !== null) return inFlight.current;
|
|
590
|
+
const settleSequence = (sequence) => {
|
|
591
|
+
setUserErrors([]);
|
|
592
|
+
setOrderId(null);
|
|
593
|
+
const settle = (result) => {
|
|
594
|
+
inFlight.current = null;
|
|
595
|
+
setPlacing(false);
|
|
596
|
+
setUserErrors(result.userErrors);
|
|
597
|
+
setOrderId(result.orderId);
|
|
598
|
+
return result;
|
|
599
|
+
};
|
|
600
|
+
const outcome = sequence.then(settle, () => settle({
|
|
601
|
+
orderId: null,
|
|
602
|
+
userErrors: [NETWORK_ERROR]
|
|
603
|
+
}));
|
|
604
|
+
inFlight.current = outcome;
|
|
605
|
+
return outcome;
|
|
606
|
+
};
|
|
607
|
+
if (attempt) {
|
|
608
|
+
if (attempt.phase === "settled") return Promise.resolve({
|
|
609
|
+
orderId: attempt.orderId,
|
|
610
|
+
userErrors: []
|
|
611
|
+
});
|
|
612
|
+
if (attempt.phase === "opening" || attempt.phase === "finalizing") return settleSequence(Promise.resolve({
|
|
613
|
+
orderId: attempt.orderId,
|
|
614
|
+
userErrors: [PLACE_ORDER_IN_FLIGHT_ERROR]
|
|
615
|
+
}));
|
|
616
|
+
if (attempt.phase === "sent" || attempt.phase === "unresolved") return settleSequence(resume(gatewayCallback, setAttempt, setPlacing, attempt));
|
|
617
|
+
}
|
|
618
|
+
return settleSequence(run());
|
|
619
|
+
}, [
|
|
620
|
+
run,
|
|
621
|
+
attempt,
|
|
622
|
+
gatewayCallback,
|
|
623
|
+
setAttempt
|
|
624
|
+
]);
|
|
625
|
+
return useMemo(() => ({
|
|
626
|
+
placeOrder,
|
|
627
|
+
placing,
|
|
628
|
+
userErrors,
|
|
629
|
+
orderId
|
|
630
|
+
}), [
|
|
631
|
+
placeOrder,
|
|
632
|
+
placing,
|
|
633
|
+
userErrors,
|
|
634
|
+
orderId
|
|
635
|
+
]);
|
|
636
|
+
}
|
|
637
|
+
//#endregion
|
|
638
|
+
export { PAYMENT_INVALID_TRANSACTION_CODE, PAYMENT_MISSING_METHOD_CODE, PAYMENT_MISSING_SUBMISSION_CODE, PLACE_ORDER_IN_FLIGHT_CODE, PLACE_ORDER_UNCONFIRMED_CODE, usePlaceOrder };
|
|
639
|
+
|
|
640
|
+
//# sourceMappingURL=use-place-order.js.map
|