@vtex/faststore 0.0.5 → 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 +45 -0
- package/dist/actions.json +181 -0
- package/dist/catalog.json +1543 -495
- package/dist/client.d.ts +2 -1
- package/dist/client.js +2 -1
- package/dist/components.d.ts +28 -7
- package/dist/components.js +31 -12
- package/dist/docs/access-code-sign-in.md +1 -1
- 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/card-entry.md +250 -0
- package/dist/docs/cardholder-document.md +142 -0
- 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/shipping-address.md +110 -258
- 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/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/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/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 +4 -4
- 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 +480 -791
- package/dist/matter/shipping-address/shipping-address.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/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/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/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/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/cart/use-cart-actions.js +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 +36 -0
- 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.js +7 -3
- package/dist/sdk/data/reactive-data-provider.js.map +1 -1
- package/dist/sdk/data/reactive-list-item-data-provider.js +12 -4
- 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 +5 -1
- 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/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/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/shipping/use-shipping-actions.js +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/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
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
//#region src/sdk/i18n/messages/en-US.json
|
|
2
2
|
var en_US_default = {
|
|
3
3
|
"fs/a11y.close": "Close",
|
|
4
|
+
"fs/form.clear": "Clear",
|
|
5
|
+
"fs/form.reset": "Reset",
|
|
6
|
+
"fs/form.submit": "Continue",
|
|
4
7
|
"fs/region.error.invalid-postal-code": "Enter a valid postal code.",
|
|
5
8
|
"fs/region.error.network": "Unable to set your region. Please try again.",
|
|
6
9
|
"fs/region.error.unserved-postal-code": "We do not deliver to this postal code yet.",
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"fs/a11y.close": "Close",
|
|
3
|
+
"fs/form.clear": "Clear",
|
|
4
|
+
"fs/form.reset": "Reset",
|
|
5
|
+
"fs/form.submit": "Continue",
|
|
3
6
|
"fs/region.error.invalid-postal-code": "Enter a valid postal code.",
|
|
4
7
|
"fs/region.error.network": "Unable to set your region. Please try again.",
|
|
5
8
|
"fs/region.error.unserved-postal-code": "We do not deliver to this postal code yet.",
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
//#region src/sdk/i18n/messages/en-US.json.d.ts
|
|
2
2
|
declare const _exports: {
|
|
3
3
|
"fs/a11y.close": string;
|
|
4
|
+
"fs/form.clear": string;
|
|
5
|
+
"fs/form.reset": string;
|
|
6
|
+
"fs/form.submit": string;
|
|
4
7
|
"fs/region.error.invalid-postal-code": string;
|
|
5
8
|
"fs/region.error.network": string;
|
|
6
9
|
"fs/region.error.unserved-postal-code": string;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
//#region src/sdk/i18n/messages/es-ES.json
|
|
2
2
|
var es_ES_default = {
|
|
3
3
|
"fs/a11y.close": "Cerrar",
|
|
4
|
+
"fs/form.clear": "Limpiar",
|
|
5
|
+
"fs/form.reset": "Restablecer",
|
|
6
|
+
"fs/form.submit": "Continuar",
|
|
4
7
|
"fs/region.error.invalid-postal-code": "Introduce un código postal válido.",
|
|
5
8
|
"fs/region.error.network": "No se pudo definir tu región. Inténtalo de nuevo.",
|
|
6
9
|
"fs/region.error.unserved-postal-code": "Todavía no hacemos entregas en este código postal.",
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"fs/a11y.close": "Cerrar",
|
|
3
|
+
"fs/form.clear": "Limpiar",
|
|
4
|
+
"fs/form.reset": "Restablecer",
|
|
5
|
+
"fs/form.submit": "Continuar",
|
|
3
6
|
"fs/region.error.invalid-postal-code": "Introduce un código postal válido.",
|
|
4
7
|
"fs/region.error.network": "No se pudo definir tu región. Inténtalo de nuevo.",
|
|
5
8
|
"fs/region.error.unserved-postal-code": "Todavía no hacemos entregas en este código postal.",
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
//#region src/sdk/i18n/messages/pt-BR.json
|
|
2
2
|
var pt_BR_default = {
|
|
3
3
|
"fs/a11y.close": "Fechar",
|
|
4
|
+
"fs/form.clear": "Limpar",
|
|
5
|
+
"fs/form.reset": "Redefinir",
|
|
6
|
+
"fs/form.submit": "Continuar",
|
|
4
7
|
"fs/region.error.invalid-postal-code": "Informe um CEP válido.",
|
|
5
8
|
"fs/region.error.network": "Não foi possível definir sua região. Tente novamente.",
|
|
6
9
|
"fs/region.error.unserved-postal-code": "Ainda não entregamos neste CEP.",
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"fs/a11y.close": "Fechar",
|
|
3
|
+
"fs/form.clear": "Limpar",
|
|
4
|
+
"fs/form.reset": "Redefinir",
|
|
5
|
+
"fs/form.submit": "Continuar",
|
|
3
6
|
"fs/region.error.invalid-postal-code": "Informe um CEP válido.",
|
|
4
7
|
"fs/region.error.network": "Não foi possível definir sua região. Tente novamente.",
|
|
5
8
|
"fs/region.error.unserved-postal-code": "Ainda não entregamos neste CEP.",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { defineActions } from "../../core/dist/actions/registry.js";
|
|
3
3
|
import "../../core/dist/index.js";
|
|
4
|
+
import { loggableErrorName } from "../../utils/loggable-error-name.js";
|
|
4
5
|
import { CLIENT_PREFERENCES_UPDATE, CLIENT_PROFILE_UPDATE, CUSTOMER_PROFILE_UPDATE } from "./operations.js";
|
|
5
6
|
import { useMutation } from "@apollo/client/react";
|
|
6
7
|
import { useCallback, useMemo } from "react";
|
|
@@ -46,7 +47,7 @@ function useIdentityActions() {
|
|
|
46
47
|
try {
|
|
47
48
|
return (await profileMutation({ variables: { input: payload } })).data?.clientProfileUpdate?.userErrors ?? [];
|
|
48
49
|
} catch (error) {
|
|
49
|
-
console.error("[identity.clientProfileUpdate] clientProfileUpdate failed", error
|
|
50
|
+
console.error("[identity.clientProfileUpdate] clientProfileUpdate failed", loggableErrorName(error));
|
|
50
51
|
return [NETWORK_PROFILE_ERROR];
|
|
51
52
|
}
|
|
52
53
|
}, [profileMutation]);
|
|
@@ -54,7 +55,7 @@ function useIdentityActions() {
|
|
|
54
55
|
try {
|
|
55
56
|
return (await preferencesMutation({ variables: { input: payload } })).data?.clientPreferencesUpdate?.userErrors ?? [];
|
|
56
57
|
} catch (error) {
|
|
57
|
-
console.error("[identity.clientPreferencesUpdate] clientPreferencesUpdate failed", error
|
|
58
|
+
console.error("[identity.clientPreferencesUpdate] clientPreferencesUpdate failed", loggableErrorName(error));
|
|
58
59
|
return [NETWORK_PREFERENCE_ERROR];
|
|
59
60
|
}
|
|
60
61
|
}, [preferencesMutation]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-identity-actions.js","names":[],"sources":["../../../src/sdk/identity/use-identity-actions.ts"],"sourcesContent":["'use client';\n\nimport { CombinedGraphQLErrors } from '@apollo/client/errors';\nimport { useMutation } from '@apollo/client/react';\nimport { defineActions } from '@vtex/fs-core';\nimport { useCallback, useMemo } from 'react';\nimport {\n CLIENT_PREFERENCES_UPDATE,\n CLIENT_PROFILE_UPDATE,\n type ClientPreferenceKey,\n type ClientProfileUserError,\n CUSTOMER_PROFILE_UPDATE,\n type CustomerProfileUpdateInput,\n type CustomerProfileUserError,\n} from './operations.js';\n\n/**\n * The `identity` action namespace — the context-free owner of the shopper-profile,\n * consent-preference, and persistent-profile writes\n * (specs/fspg-859-email-field.md, specs/consent-opt-in-component.md,\n * docs/storefront-core/reference-free-particle-model.md § Write side — actions,\n * docs/storefront-core/adr-0010-actions-own-the-write-side.md).\n *\n * It mirrors `useCartActions`: a component anywhere in the tree dispatches the write through the\n * compiler-injected `$actions.identity.*` prop, and the handler calls the corresponding mutation.\n * Every write is **async / pessimistic** (Decision 1): the caller awaits the persisted result. Each\n * call returns its `userErrors` (empty on success) so the caller can surface server-side failures\n * inline; the returned entity self-updates the normalized cache and every reactive subscriber.\n *\n * The first two writes target the cart's sibling identity entities; `customerProfileUpdate` targets\n * the shopper's account profile, which needs no cart at all.\n */\n\n/**\n * Payload for the profile write. Callers persist only the fields they own; other fields are\n * untouched. The payload is passed through to `ClientProfileUpdateInput` unmapped, so this type\n * mirrors that input verbatim — a stacked field only needs a field to already exist here to reach\n * the wire.\n */\nexport type ClientProfileUpdatePayload = Readonly<{\n email?: string;\n firstName?: string;\n lastName?: string;\n phone?: string;\n document?: string;\n}>;\n\n/** Payload for the preferences write — one opt-in flag; the other flag is left untouched. */\nexport type ClientPreferencesUpdatePayload = Readonly<Partial<Record<ClientPreferenceKey, boolean>>>;\n\n/**\n * Payload for the persistent-profile write. Mirrors `CustomerProfileUpdateInput` verbatim and is\n * forwarded unmapped, so an omitted field stays untouched upstream and an empty string erases.\n */\nexport type CustomerProfileUpdatePayload = CustomerProfileUpdateInput;\n\nexport type IdentityActions = Readonly<{\n /**\n * Persist the provided profile fields (partial) to the active cart's `clientProfileData`.\n * Async — awaits the mutation before resolving. Returns the resulting `userErrors` (empty on\n * success).\n */\n clientProfileUpdate: (payload: ClientProfileUpdatePayload) => Promise<readonly ClientProfileUserError[]>;\n /**\n * Persist the provided consent opt-in flag(s) (partial) to the shopper's `ClientPreferences`\n * entity (order-form-backed). Async — awaits the mutation before resolving. Writes ONLY the\n * requested field. Returns the resulting `userErrors` (empty on success).\n */\n clientPreferencesUpdate: (payload: ClientPreferencesUpdatePayload) => Promise<readonly ClientProfileUserError[]>;\n /**\n * Persist the provided profile fields (partial) to the shopper's persistent, cart-independent\n * account profile. Async — awaits the mutation before resolving. Sends exactly the fields it is\n * given: an omitted field is left untouched, an empty string erases. Returns the resulting\n * `userErrors` (empty on success).\n */\n customerProfileUpdate: (payload: CustomerProfileUpdatePayload) => Promise<readonly CustomerProfileUserError[]>;\n}>;\n\nconst NETWORK_PROFILE_ERROR: ClientProfileUserError = {\n code: 'NETWORK_ERROR',\n message: 'Unable to save your details. Please try again.',\n};\n\nconst NETWORK_PREFERENCE_ERROR: ClientProfileUserError = {\n code: 'NETWORK_ERROR',\n message: 'Unable to save your preference. Please try again.',\n};\n\nconst NETWORK_CUSTOMER_PROFILE_ERROR: CustomerProfileUserError = {\n code: 'NETWORK_ERROR',\n message: 'Unable to save your profile. Please try again.',\n};\n\nconst UNAUTHORIZED_CUSTOMER_PROFILE_ERROR: CustomerProfileUserError = {\n code: 'UNAUTHORIZED',\n message: 'Your session has expired. Please sign in again.',\n};\n\n/**\n * The profile module answers an anonymous or expired session with a top-level `UNAUTHORIZED`, never\n * a `userError`. Collapsing it into `NETWORK_ERROR` would offer a retry that cannot succeed, so it\n * gets its own code and the caller can send the shopper back to sign-in.\n */\nfunction toCustomerProfileError(error: unknown): CustomerProfileUserError {\n if (CombinedGraphQLErrors.is(error)) {\n const unauthorized = error.errors.some(({ extensions }) => extensions?.code === 'UNAUTHORIZED');\n if (unauthorized) return UNAUTHORIZED_CUSTOMER_PROFILE_ERROR;\n }\n return NETWORK_CUSTOMER_PROFILE_ERROR;\n}\n\n/**\n * The `identity` namespace hook. Registered with the first-party action registry via `identityActions`\n * below; the compiler-emitted `IdentityActionProvider` calls it and threads the narrowed methods into\n * each consumer's `$actions.identity`.\n */\nexport function useIdentityActions(): IdentityActions {\n const [profileMutation] = useMutation(CLIENT_PROFILE_UPDATE);\n const [preferencesMutation] = useMutation(CLIENT_PREFERENCES_UPDATE);\n const [customerProfileMutation] = useMutation(CUSTOMER_PROFILE_UPDATE);\n\n const clientProfileUpdate = useCallback<IdentityActions['clientProfileUpdate']>(\n async (payload) => {\n try {\n const result = await profileMutation({ variables: { input: payload } });\n return result.data?.clientProfileUpdate?.userErrors ?? [];\n } catch (error) {\n // Log only the error name — the message can echo the submitted email (PII).\n console.error(\n '[identity.clientProfileUpdate] clientProfileUpdate failed',\n error instanceof Error ? error.name : 'UnknownError',\n );\n return [NETWORK_PROFILE_ERROR];\n }\n },\n [profileMutation],\n );\n\n const clientPreferencesUpdate = useCallback<IdentityActions['clientPreferencesUpdate']>(\n async (payload) => {\n try {\n const result = await preferencesMutation({ variables: { input: payload } });\n return result.data?.clientPreferencesUpdate?.userErrors ?? [];\n } catch (error) {\n // Log only the error name — mirrors the profile write's PII-safe logging.\n console.error(\n '[identity.clientPreferencesUpdate] clientPreferencesUpdate failed',\n error instanceof Error ? error.name : 'UnknownError',\n );\n return [NETWORK_PREFERENCE_ERROR];\n }\n },\n [preferencesMutation],\n );\n\n const customerProfileUpdate = useCallback<IdentityActions['customerProfileUpdate']>(\n async (payload) => {\n try {\n // Forwarded unmapped: an empty string is an explicit erase and must survive to the wire.\n const result = await customerProfileMutation({ variables: { input: payload } });\n return result.data?.customerProfileUpdate?.userErrors ?? [];\n } catch (error) {\n // Log only the error name — the message can echo a submitted profile value (PII).\n console.error(\n '[identity.customerProfileUpdate] customerProfileUpdate failed',\n error instanceof Error ? error.name : 'UnknownError',\n );\n return [toCustomerProfileError(error)];\n }\n },\n [customerProfileMutation],\n );\n\n return useMemo(\n () => ({ clientProfileUpdate, clientPreferencesUpdate, customerProfileUpdate }),\n [clientProfileUpdate, clientPreferencesUpdate, customerProfileUpdate],\n );\n}\n\n/**\n * Declares the `identity` action namespace — the single source of truth for its method/payload\n * metadata, runtime provider, and `use` hook (mirrors `cartActions`). The native catalog builder\n * (`scripts/build-catalog.mjs`) statically reads this call to emit `actions.json`; at runtime\n * `IdentityActionProvider` calls `useIdentityActions` directly.\n */\nexport const identityActions = defineActions({\n namespace: 'identity',\n description:\n 'Mutation-backed writes against the shopper identity entities (client profile, consent preferences, account profile).',\n provider: { name: 'IdentityActionProvider', importPath: '@vtex/faststore/internal/client' },\n methods: {\n clientProfileUpdate: {\n description:\n 'Persist profile fields (partial) to the active cart (async strategy). Sending email flips Session.isIdentified.',\n payload: [\n { name: 'email', type: 'string', optional: true },\n { name: 'firstName', type: 'string', optional: true },\n { name: 'lastName', type: 'string', optional: true },\n { name: 'phone', type: 'string', optional: true },\n { name: 'document', type: 'string', optional: true },\n ],\n },\n clientPreferencesUpdate: {\n description:\n 'Persist consent opt-in flags (partial) to the active cart (async strategy). Writes only the provided flag(s).',\n payload: [\n { name: 'optinNewsLetter', type: 'boolean', optional: true },\n { name: 'savePersonalData', type: 'boolean', optional: true },\n ],\n },\n customerProfileUpdate: {\n description:\n 'Persist profile fields (partial) to the persistent account profile, not the cart (async strategy). Omitted fields are untouched; an empty string erases.',\n payload: [\n { name: 'firstName', type: 'string', optional: true },\n { name: 'lastName', type: 'string', optional: true },\n { name: 'homePhone', type: 'string', optional: true },\n { name: 'mobilePhone', type: 'string', optional: true },\n { name: 'document', type: 'string', optional: true },\n { name: 'documentType', type: 'string', optional: true },\n { name: 'birthDate', type: 'string', optional: true },\n { name: 'gender', type: 'string', optional: true },\n ],\n },\n },\n use: useIdentityActions,\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-identity-actions.js","names":[],"sources":["../../../src/sdk/identity/use-identity-actions.ts"],"sourcesContent":["'use client';\n\nimport { CombinedGraphQLErrors } from '@apollo/client/errors';\nimport { useMutation } from '@apollo/client/react';\nimport { defineActions } from '@vtex/fs-core';\nimport { useCallback, useMemo } from 'react';\nimport { loggableErrorName } from '../../utils/loggable-error-name.js';\nimport {\n CLIENT_PREFERENCES_UPDATE,\n CLIENT_PROFILE_UPDATE,\n type ClientPreferenceKey,\n type ClientProfileUserError,\n CUSTOMER_PROFILE_UPDATE,\n type CustomerProfileUpdateInput,\n type CustomerProfileUserError,\n} from './operations.js';\n\n/**\n * The `identity` action namespace — the context-free owner of the shopper-profile,\n * consent-preference, and persistent-profile writes\n * (specs/fspg-859-email-field.md, specs/consent-opt-in-component.md,\n * docs/storefront-core/reference-free-particle-model.md § Write side — actions,\n * docs/storefront-core/adr-0010-actions-own-the-write-side.md).\n *\n * It mirrors `useCartActions`: a component anywhere in the tree dispatches the write through the\n * compiler-injected `$actions.identity.*` prop, and the handler calls the corresponding mutation.\n * Every write is **async / pessimistic** (Decision 1): the caller awaits the persisted result. Each\n * call returns its `userErrors` (empty on success) so the caller can surface server-side failures\n * inline; the returned entity self-updates the normalized cache and every reactive subscriber.\n *\n * The first two writes target the cart's sibling identity entities; `customerProfileUpdate` targets\n * the shopper's account profile, which needs no cart at all.\n */\n\n/**\n * Payload for the profile write. Callers persist only the fields they own; other fields are\n * untouched. The payload is passed through to `ClientProfileUpdateInput` unmapped, so this type\n * mirrors that input verbatim — a stacked field only needs a field to already exist here to reach\n * the wire.\n */\nexport type ClientProfileUpdatePayload = Readonly<{\n email?: string;\n firstName?: string;\n lastName?: string;\n phone?: string;\n document?: string;\n}>;\n\n/** Payload for the preferences write — one opt-in flag; the other flag is left untouched. */\nexport type ClientPreferencesUpdatePayload = Readonly<Partial<Record<ClientPreferenceKey, boolean>>>;\n\n/**\n * Payload for the persistent-profile write. Mirrors `CustomerProfileUpdateInput` verbatim and is\n * forwarded unmapped, so an omitted field stays untouched upstream and an empty string erases.\n */\nexport type CustomerProfileUpdatePayload = CustomerProfileUpdateInput;\n\nexport type IdentityActions = Readonly<{\n /**\n * Persist the provided profile fields (partial) to the active cart's `clientProfileData`.\n * Async — awaits the mutation before resolving. Returns the resulting `userErrors` (empty on\n * success).\n */\n clientProfileUpdate: (payload: ClientProfileUpdatePayload) => Promise<readonly ClientProfileUserError[]>;\n /**\n * Persist the provided consent opt-in flag(s) (partial) to the shopper's `ClientPreferences`\n * entity (order-form-backed). Async — awaits the mutation before resolving. Writes ONLY the\n * requested field. Returns the resulting `userErrors` (empty on success).\n */\n clientPreferencesUpdate: (payload: ClientPreferencesUpdatePayload) => Promise<readonly ClientProfileUserError[]>;\n /**\n * Persist the provided profile fields (partial) to the shopper's persistent, cart-independent\n * account profile. Async — awaits the mutation before resolving. Sends exactly the fields it is\n * given: an omitted field is left untouched, an empty string erases. Returns the resulting\n * `userErrors` (empty on success).\n */\n customerProfileUpdate: (payload: CustomerProfileUpdatePayload) => Promise<readonly CustomerProfileUserError[]>;\n}>;\n\nconst NETWORK_PROFILE_ERROR: ClientProfileUserError = {\n code: 'NETWORK_ERROR',\n message: 'Unable to save your details. Please try again.',\n};\n\nconst NETWORK_PREFERENCE_ERROR: ClientProfileUserError = {\n code: 'NETWORK_ERROR',\n message: 'Unable to save your preference. Please try again.',\n};\n\nconst NETWORK_CUSTOMER_PROFILE_ERROR: CustomerProfileUserError = {\n code: 'NETWORK_ERROR',\n message: 'Unable to save your profile. Please try again.',\n};\n\nconst UNAUTHORIZED_CUSTOMER_PROFILE_ERROR: CustomerProfileUserError = {\n code: 'UNAUTHORIZED',\n message: 'Your session has expired. Please sign in again.',\n};\n\n/**\n * The profile module answers an anonymous or expired session with a top-level `UNAUTHORIZED`, never\n * a `userError`. Collapsing it into `NETWORK_ERROR` would offer a retry that cannot succeed, so it\n * gets its own code and the caller can send the shopper back to sign-in.\n */\nfunction toCustomerProfileError(error: unknown): CustomerProfileUserError {\n if (CombinedGraphQLErrors.is(error)) {\n const unauthorized = error.errors.some(({ extensions }) => extensions?.code === 'UNAUTHORIZED');\n if (unauthorized) return UNAUTHORIZED_CUSTOMER_PROFILE_ERROR;\n }\n return NETWORK_CUSTOMER_PROFILE_ERROR;\n}\n\n/**\n * The `identity` namespace hook. Registered with the first-party action registry via `identityActions`\n * below; the compiler-emitted `IdentityActionProvider` calls it and threads the narrowed methods into\n * each consumer's `$actions.identity`.\n */\nexport function useIdentityActions(): IdentityActions {\n const [profileMutation] = useMutation(CLIENT_PROFILE_UPDATE);\n const [preferencesMutation] = useMutation(CLIENT_PREFERENCES_UPDATE);\n const [customerProfileMutation] = useMutation(CUSTOMER_PROFILE_UPDATE);\n\n const clientProfileUpdate = useCallback<IdentityActions['clientProfileUpdate']>(\n async (payload) => {\n try {\n const result = await profileMutation({ variables: { input: payload } });\n return result.data?.clientProfileUpdate?.userErrors ?? [];\n } catch (error) {\n // Log only the error name — the message can echo the submitted email (PII).\n console.error('[identity.clientProfileUpdate] clientProfileUpdate failed', loggableErrorName(error));\n return [NETWORK_PROFILE_ERROR];\n }\n },\n [profileMutation],\n );\n\n const clientPreferencesUpdate = useCallback<IdentityActions['clientPreferencesUpdate']>(\n async (payload) => {\n try {\n const result = await preferencesMutation({ variables: { input: payload } });\n return result.data?.clientPreferencesUpdate?.userErrors ?? [];\n } catch (error) {\n // Log only the error name — mirrors the profile write's PII-safe logging.\n console.error('[identity.clientPreferencesUpdate] clientPreferencesUpdate failed', loggableErrorName(error));\n return [NETWORK_PREFERENCE_ERROR];\n }\n },\n [preferencesMutation],\n );\n\n const customerProfileUpdate = useCallback<IdentityActions['customerProfileUpdate']>(\n async (payload) => {\n try {\n // Forwarded unmapped: an empty string is an explicit erase and must survive to the wire.\n const result = await customerProfileMutation({ variables: { input: payload } });\n return result.data?.customerProfileUpdate?.userErrors ?? [];\n } catch (error) {\n // Log only the error name — the message can echo a submitted profile value (PII).\n console.error(\n '[identity.customerProfileUpdate] customerProfileUpdate failed',\n error instanceof Error ? error.name : 'UnknownError',\n );\n return [toCustomerProfileError(error)];\n }\n },\n [customerProfileMutation],\n );\n\n return useMemo(\n () => ({ clientProfileUpdate, clientPreferencesUpdate, customerProfileUpdate }),\n [clientProfileUpdate, clientPreferencesUpdate, customerProfileUpdate],\n );\n}\n\n/**\n * Declares the `identity` action namespace — the single source of truth for its method/payload\n * metadata, runtime provider, and `use` hook (mirrors `cartActions`). The native catalog builder\n * (`scripts/build-catalog.mjs`) statically reads this call to emit `actions.json`; at runtime\n * `IdentityActionProvider` calls `useIdentityActions` directly.\n */\nexport const identityActions = defineActions({\n namespace: 'identity',\n description:\n 'Mutation-backed writes against the shopper identity entities (client profile, consent preferences, account profile).',\n provider: { name: 'IdentityActionProvider', importPath: '@vtex/faststore/internal/client' },\n methods: {\n clientProfileUpdate: {\n description:\n 'Persist profile fields (partial) to the active cart (async strategy). Sending email flips Session.isIdentified.',\n payload: [\n { name: 'email', type: 'string', optional: true },\n { name: 'firstName', type: 'string', optional: true },\n { name: 'lastName', type: 'string', optional: true },\n { name: 'phone', type: 'string', optional: true },\n { name: 'document', type: 'string', optional: true },\n ],\n },\n clientPreferencesUpdate: {\n description:\n 'Persist consent opt-in flags (partial) to the active cart (async strategy). Writes only the provided flag(s).',\n payload: [\n { name: 'optinNewsLetter', type: 'boolean', optional: true },\n { name: 'savePersonalData', type: 'boolean', optional: true },\n ],\n },\n customerProfileUpdate: {\n description:\n 'Persist profile fields (partial) to the persistent account profile, not the cart (async strategy). Omitted fields are untouched; an empty string erases.',\n payload: [\n { name: 'firstName', type: 'string', optional: true },\n { name: 'lastName', type: 'string', optional: true },\n { name: 'homePhone', type: 'string', optional: true },\n { name: 'mobilePhone', type: 'string', optional: true },\n { name: 'document', type: 'string', optional: true },\n { name: 'documentType', type: 'string', optional: true },\n { name: 'birthDate', type: 'string', optional: true },\n { name: 'gender', type: 'string', optional: true },\n ],\n },\n },\n use: useIdentityActions,\n});\n"],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/sdk/payment/gateway-origin.ts
|
|
2
|
+
/**
|
|
3
|
+
* The one rule deciding whether a gateway origin may be interpolated into the card POST's URL
|
|
4
|
+
* (specs/use-place-order.md, Decision 4). It lives in its own module rather than in `gateway.ts`
|
|
5
|
+
* because `usePlaceOrder` needs it too, and that hook imports nothing executable from the module that
|
|
6
|
+
* touches card data — a boundary its own tests assert against the file's source text.
|
|
7
|
+
*
|
|
8
|
+
* `PaymentStartTransactionPayload.gatewayUrl` is nullable, and `buildGatewayPaymentsRequest`
|
|
9
|
+
* concatenates it (`${gatewayUrl}/api/pub/transactions/…`). Three untrusted values are real rather
|
|
10
|
+
* than hypothetical, and each produces a URL that resolves against the storefront's own origin or
|
|
11
|
+
* sends the card in plaintext:
|
|
12
|
+
*
|
|
13
|
+
* - `null` / `undefined` / `''` — the payload's own error shape (T18 nulls every field beside
|
|
14
|
+
* `userErrors`), yielding a relative path.
|
|
15
|
+
* - the four-character string `"null"` — what `new URL('mailto:x').origin` evaluates to, so a
|
|
16
|
+
* non-special scheme upstream survives a parse-only check.
|
|
17
|
+
* - an `http:` origin — parses fine and sends the PAN and CVV unencrypted.
|
|
18
|
+
*
|
|
19
|
+
* The `.vtexpayments.com.br` host suffix is deliberately *not* re-checked here. T18 resolves and
|
|
20
|
+
* validates the origin server-side and is the authoritative source; repeating the suffix in shipped
|
|
21
|
+
* client code would add nothing against the three failures above while hard-coding a deployment
|
|
22
|
+
* assumption a sandbox or a future gateway host would break.
|
|
23
|
+
*/
|
|
24
|
+
function isTrustedGatewayOrigin(value) {
|
|
25
|
+
if (!value) return false;
|
|
26
|
+
try {
|
|
27
|
+
return new URL(value).protocol === "https:";
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { isTrustedGatewayOrigin };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=gateway-origin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-origin.js","names":[],"sources":["../../../src/sdk/payment/gateway-origin.ts"],"sourcesContent":["/**\n * The one rule deciding whether a gateway origin may be interpolated into the card POST's URL\n * (specs/use-place-order.md, Decision 4). It lives in its own module rather than in `gateway.ts`\n * because `usePlaceOrder` needs it too, and that hook imports nothing executable from the module that\n * touches card data — a boundary its own tests assert against the file's source text.\n *\n * `PaymentStartTransactionPayload.gatewayUrl` is nullable, and `buildGatewayPaymentsRequest`\n * concatenates it (`${gatewayUrl}/api/pub/transactions/…`). Three untrusted values are real rather\n * than hypothetical, and each produces a URL that resolves against the storefront's own origin or\n * sends the card in plaintext:\n *\n * - `null` / `undefined` / `''` — the payload's own error shape (T18 nulls every field beside\n * `userErrors`), yielding a relative path.\n * - the four-character string `\"null\"` — what `new URL('mailto:x').origin` evaluates to, so a\n * non-special scheme upstream survives a parse-only check.\n * - an `http:` origin — parses fine and sends the PAN and CVV unencrypted.\n *\n * The `.vtexpayments.com.br` host suffix is deliberately *not* re-checked here. T18 resolves and\n * validates the origin server-side and is the authoritative source; repeating the suffix in shipped\n * client code would add nothing against the three failures above while hard-coding a deployment\n * assumption a sandbox or a future gateway host would break.\n */\nexport function isTrustedGatewayOrigin(value: string | null | undefined): value is string {\n if (!value) return false;\n\n try {\n return new URL(value).protocol === 'https:';\n } catch {\n // Not an absolute URL — `'/api'`, `'acme.vtexpayments.com.br'`, `'not-a-url'`.\n return false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,uBAAuB,OAAmD;CACxF,IAAI,CAAC,OAAO,OAAO;CAEnB,IAAI;EACF,OAAO,IAAI,IAAI,KAAK,CAAC,CAAC,aAAa;CACrC,QAAQ;EAEN,OAAO;CACT;AACF"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import "./operations.js";
|
|
2
|
+
//#region src/sdk/payment/gateway.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Non-sensitive descriptor of the open transaction. Every field maps to a value the caller already
|
|
5
|
+
* holds — `paymentStartTransaction`'s payload (T3) or the `Payment` read model (T1) — except
|
|
6
|
+
* `gatewayUrl` and `merchantName`, which that payload does not carry today (Gaps 1 and 2 in the spec).
|
|
7
|
+
* Amounts are in the currency's **minor units**; `PaymentStartTransactionPayload.value.amount` is a
|
|
8
|
+
* decimal, so the caller converts once, visibly, rather than having this module assume an exponent.
|
|
9
|
+
*/
|
|
10
|
+
export type PaymentGatewayTransaction = Readonly<{
|
|
11
|
+
/**
|
|
12
|
+
* Gateway origin, e.g. `https://acme.vtexpayments.com.br`. Server-resolved by T18 and read off
|
|
13
|
+
* `paymentStartTransaction`'s payload; never derived here. It is nullable there and required here,
|
|
14
|
+
* so the builder re-checks it (`isTrustedGatewayOrigin`) rather than trusting the type.
|
|
15
|
+
*/
|
|
16
|
+
gatewayUrl: string;
|
|
17
|
+
/** `PaymentStartTransactionPayload.transactionId`. */
|
|
18
|
+
transactionId: string;
|
|
19
|
+
/** VTEX merchant name for this transaction. Resolved server-side by T3 but not exposed on its payload. */
|
|
20
|
+
merchantName: string;
|
|
21
|
+
/** `PaymentStartTransactionPayload.orderGroup` — the gateway's `orderId` query parameter. */
|
|
22
|
+
orderGroup: string;
|
|
23
|
+
/** `PaymentStartTransactionPayload.userProfileId`; null for a guest order, so the query parameter is left out. */
|
|
24
|
+
userProfileId: string | null;
|
|
25
|
+
/** `Payment.selectedMethodId` — the VTEX payment-system id. */
|
|
26
|
+
paymentSystem: string;
|
|
27
|
+
/** `PaymentMethod.groupName`, e.g. `creditCardPaymentGroup`. */
|
|
28
|
+
groupName: string;
|
|
29
|
+
/** `PaymentStartTransactionPayload.value.currencyCode`. */
|
|
30
|
+
currencyCode: string;
|
|
31
|
+
/** Transaction total, in minor units. */
|
|
32
|
+
valueInCents: number;
|
|
33
|
+
/** Pre-interest base (`PaymentInstallmentOption.baseValue`), in minor units. */
|
|
34
|
+
referenceValueInCents: number;
|
|
35
|
+
/** `Payment.selectedInstallment.count`; 1 when the shopper never picked a plan. */
|
|
36
|
+
installments: number;
|
|
37
|
+
/** Per-installment amount (`PaymentInstallment.value`), in minor units. */
|
|
38
|
+
installmentsValueInCents: number;
|
|
39
|
+
/** `PaymentInstallment.interestRate`; 0 for the interest-free 1x default. */
|
|
40
|
+
interestRate: number;
|
|
41
|
+
/** Absolute return URL. Phase-2 redirect mode only; omitted in v1's direct mode. */
|
|
42
|
+
callbackUrl?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Billing differs from the delivery address. Defaults to `false`, because `setBillingAddress` persists
|
|
45
|
+
* billing on the order form's `invoiceData` (T3) and the gateway reads it from there rather than from
|
|
46
|
+
* this body. The call site that would override it is T13's `usePlaceOrder`, the one place the
|
|
47
|
+
* descriptor is assembled; T11 owns the live verification of the assumption and publishes the flag to
|
|
48
|
+
* T5's `draft` for T13 to read. Confirming it never reopens this module.
|
|
49
|
+
*/
|
|
50
|
+
billingAddressDifferent?: boolean;
|
|
51
|
+
}>;
|
|
52
|
+
/** A newly entered card. Runtime argument only — never declared, never logged, never cached. */
|
|
53
|
+
export type PaymentGatewayNewCard = Readonly<{
|
|
54
|
+
kind: 'new';
|
|
55
|
+
/** Full card number. The builder strips whatever separators the method's `numberMask` inserted. */
|
|
56
|
+
number: string;
|
|
57
|
+
/** Security code (CVV). */
|
|
58
|
+
securityCode: string;
|
|
59
|
+
/** Expiry. Normalized to `MM/YY`, so a `MM/YYYY` mask is accepted. */
|
|
60
|
+
expirationDate: string;
|
|
61
|
+
holderName: string;
|
|
62
|
+
/** Shopper document (a CPF). The builder strips its punctuation. */
|
|
63
|
+
document?: string;
|
|
64
|
+
/** Shopper-assigned label, when the save-card control collected one. */
|
|
65
|
+
cardLabel?: string;
|
|
66
|
+
/** The card-save flag itself — the other half of `payment.saveCard`'s consent write (RFC D5). */
|
|
67
|
+
saveCard?: boolean;
|
|
68
|
+
}>;
|
|
69
|
+
/** A previously saved card: its account reference plus the CVV the shopper re-enters. */
|
|
70
|
+
export type PaymentGatewaySavedCard = Readonly<{
|
|
71
|
+
kind: 'saved';
|
|
72
|
+
/** `SavedCard.accountId`. */
|
|
73
|
+
accountId: string;
|
|
74
|
+
/** `SavedCard.bin` — not sensitive; the read model exposes it for installment lookup. */
|
|
75
|
+
bin: string;
|
|
76
|
+
/** Security code (CVV). */
|
|
77
|
+
securityCode: string;
|
|
78
|
+
}>;
|
|
79
|
+
export type PaymentGatewayCard = PaymentGatewayNewCard | PaymentGatewaySavedCard;
|
|
80
|
+
export type SendPaymentsRequest = Readonly<{
|
|
81
|
+
transaction: PaymentGatewayTransaction;
|
|
82
|
+
card: PaymentGatewayCard;
|
|
83
|
+
}>;
|
|
84
|
+
//#endregion
|
|
85
|
+
//# sourceMappingURL=gateway.d.ts.map
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { isTrustedGatewayOrigin } from "./gateway-origin.js";
|
|
2
|
+
//#region src/sdk/payment/gateway.ts
|
|
3
|
+
/**
|
|
4
|
+
* The direct-to-gateway card POST (specs/payment-action-namespace.md, US-6) — the PCI boundary of the
|
|
5
|
+
* credit-card feature. A new card's number and CVV, or a saved card's CVV, go from the browser straight
|
|
6
|
+
* to the VTEX payment gateway and never transit our GraphQL layer or the normalized Apollo cache
|
|
7
|
+
* (RFC D13; ADR-0010's sanctioned façade flavor).
|
|
8
|
+
*
|
|
9
|
+
* This module imports no Apollo and no GraphQL symbol on purpose: the code that holds card data has no
|
|
10
|
+
* route to the cache, and that property is checkable by reading the imports above. Card data arrives as
|
|
11
|
+
* a runtime argument from the card-entry component's local state (T7) — it is never a declared action
|
|
12
|
+
* payload, a fragment field, or a schema-bindable prop, so the AI Store Builder and the visual editor
|
|
13
|
+
* have no name to map a value onto.
|
|
14
|
+
*
|
|
15
|
+
* Nothing on this path is ever logged, serialized, or stored anywhere but the request body: the caller
|
|
16
|
+
* in `use-payment-actions.ts` logs a static message plus the error's `name` only.
|
|
17
|
+
*
|
|
18
|
+
* The request shape is behavior evidence from the legacy checkout
|
|
19
|
+
* (`fastcheckout@cd09bec3`, `packages/apps/client/src/screens/Payment/components/CardForm/services/gateway.service.ts`)
|
|
20
|
+
* — never a port. Where the legacy hardcoded a value under a `TODO` (installment count, interest rate,
|
|
21
|
+
* reference value) this builder reads it off the transaction descriptor instead.
|
|
22
|
+
*/
|
|
23
|
+
/** Client-synthesized code for a failed gateway leg — distinct from the BFF's `NETWORK_ERROR`. */
|
|
24
|
+
const PAYMENT_GATEWAY_ERROR_CODE = "GATEWAY_ERROR";
|
|
25
|
+
/** The single userError both a rejected request and a non-2xx response resolve to. */
|
|
26
|
+
const PAYMENT_GATEWAY_USER_ERROR = {
|
|
27
|
+
code: PAYMENT_GATEWAY_ERROR_CODE,
|
|
28
|
+
message: "Unable to complete the payment. Please try again."
|
|
29
|
+
};
|
|
30
|
+
/** VTEX bins the leading eight digits, not six — the legacy's own `sanitizedBin` slice. */
|
|
31
|
+
const BIN_LENGTH = 8;
|
|
32
|
+
/**
|
|
33
|
+
* Strips the separators a mask inserted. `PaymentMethodValidator.numberMask` is server-driven (T1) and the
|
|
34
|
+
* card-entry component (T7) renders the input through it, so its local state holds `4111 1111 1111 1111`
|
|
35
|
+
* rather than the digits. The legacy normalized here, in the builder, before its own BIN slice
|
|
36
|
+
* (`card.cardNumber = card.cardNumber.replace(/ /g, '')`) — the boundary is the one place the obligation
|
|
37
|
+
* cannot be forgotten, and a spaced BIN is a lookup the gateway fails with no diagnosable error.
|
|
38
|
+
*/
|
|
39
|
+
function toDigits(value) {
|
|
40
|
+
return value.replace(/\D/g, "");
|
|
41
|
+
}
|
|
42
|
+
/** `MM/YY` whatever the mask produced (`MM/YYYY`, `MMYY`) — the legacy's own two-slice normalization. */
|
|
43
|
+
function toDueDate(expirationDate) {
|
|
44
|
+
const digits = toDigits(expirationDate);
|
|
45
|
+
return `${digits.slice(0, 2)}/${digits.slice(-2)}`;
|
|
46
|
+
}
|
|
47
|
+
/** The card half of the body — the BIN and the `fields` map, both derived from one normalization pass. */
|
|
48
|
+
function toCardBody(card) {
|
|
49
|
+
if (card.kind === "saved") return {
|
|
50
|
+
bin: card.bin,
|
|
51
|
+
fields: {
|
|
52
|
+
bin: card.bin,
|
|
53
|
+
validationCode: card.securityCode
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const number = toDigits(card.number);
|
|
57
|
+
const bin = number.slice(0, BIN_LENGTH);
|
|
58
|
+
return {
|
|
59
|
+
bin,
|
|
60
|
+
fields: {
|
|
61
|
+
bin,
|
|
62
|
+
cardNumber: number,
|
|
63
|
+
document: card.document === void 0 ? null : card.document.replace(/\W/g, ""),
|
|
64
|
+
dueDate: toDueDate(card.expirationDate),
|
|
65
|
+
holderName: card.holderName,
|
|
66
|
+
validationCode: card.securityCode,
|
|
67
|
+
cardLabel: card.cardLabel,
|
|
68
|
+
savePaymentData: card.saveCard ?? false
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Builds the gateway URL and body. Billing is deliberately absent from the body: `setBillingAddress`
|
|
74
|
+
* already persisted it on the order form's `invoiceData` (T3), so the gateway reads it from there — hence
|
|
75
|
+
* the `billingAddressDifferent: false` default the two billing flags derive from. `originalPaymentIndex`
|
|
76
|
+
* is the legacy's own constant for a single-payment order.
|
|
77
|
+
*
|
|
78
|
+
* The origin check is the last line of defence, not the first: `usePlaceOrder` already refuses an
|
|
79
|
+
* untrusted origin *before* asking a holder for the card (T13, Decision 4). A builder has no userError
|
|
80
|
+
* channel, so it throws — `sendPayments` catches, collapses it to `GATEWAY_ERROR`, and logs the error's
|
|
81
|
+
* name only, so nothing about the card reaches a log. The throw happens before the body is assembled.
|
|
82
|
+
*/
|
|
83
|
+
function buildGatewayPaymentsRequest({ transaction, card }) {
|
|
84
|
+
if (!isTrustedGatewayOrigin(transaction.gatewayUrl)) throw new Error("Refusing to post card data: the transaction carries no trusted https gateway origin.");
|
|
85
|
+
const params = new URLSearchParams({ orderId: transaction.orderGroup });
|
|
86
|
+
if (transaction.userProfileId !== null) params.append("userProfileId", transaction.userProfileId);
|
|
87
|
+
if (transaction.callbackUrl !== void 0) params.append("callbackUrl", transaction.callbackUrl);
|
|
88
|
+
const url = `${transaction.gatewayUrl}/api/pub/transactions/${transaction.transactionId}/payments?${params}`;
|
|
89
|
+
const { bin, fields } = toCardBody(card);
|
|
90
|
+
const billingAddressDifferent = transaction.billingAddressDifferent ?? false;
|
|
91
|
+
return {
|
|
92
|
+
url,
|
|
93
|
+
body: [{
|
|
94
|
+
accountId: card.kind === "saved" ? card.accountId : null,
|
|
95
|
+
bin,
|
|
96
|
+
chooseToUseNewCard: card.kind === "new",
|
|
97
|
+
currencyCode: transaction.currencyCode,
|
|
98
|
+
fields,
|
|
99
|
+
groupName: transaction.groupName,
|
|
100
|
+
hasDefaultBillingAddress: !billingAddressDifferent,
|
|
101
|
+
id: transaction.merchantName,
|
|
102
|
+
installmentValue: transaction.installmentsValueInCents,
|
|
103
|
+
installments: transaction.installments,
|
|
104
|
+
installmentsInterestRate: transaction.interestRate,
|
|
105
|
+
installmentsValue: transaction.installmentsValueInCents,
|
|
106
|
+
interestRate: transaction.interestRate,
|
|
107
|
+
isBillingAddressDifferent: billingAddressDifferent,
|
|
108
|
+
originalPaymentIndex: 1,
|
|
109
|
+
paymentSystem: transaction.paymentSystem,
|
|
110
|
+
referenceValue: transaction.referenceValueInCents,
|
|
111
|
+
tokenId: null,
|
|
112
|
+
transaction: {
|
|
113
|
+
id: transaction.transactionId,
|
|
114
|
+
merchantName: transaction.merchantName
|
|
115
|
+
},
|
|
116
|
+
value: transaction.valueInCents
|
|
117
|
+
}]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* T29 (FR-6, OQ-7): this leg's own abort budget. Before this task the direct-to-gateway POST carried no
|
|
122
|
+
* `AbortSignal` and no timeout at all, so a gateway that never answered left `placing` true and the CTA
|
|
123
|
+
* dead for the life of the mount (Route C, `place-order.md`'s Known limitations). The BFF's own default
|
|
124
|
+
* is 10s (`apps/storefront-api-core/.../api-timeout.ts:9`), but a card authorization can legitimately
|
|
125
|
+
* run longer than a GraphQL round trip, and this exact number is not confirmed with payments (spec
|
|
126
|
+
* OQ-7) — a placeholder pending that answer, not a tuned production value.
|
|
127
|
+
*
|
|
128
|
+
* Exported so `gateway.test.ts`'s own abort-budget test can assert the abort fires at exactly this
|
|
129
|
+
* duration, rather than against `vi.runAllTimersAsync()`, which fires any pending timer regardless of
|
|
130
|
+
* duration and would stay green even if this were edited to `300_000`. That mechanism test alone is
|
|
131
|
+
* still self-referential about this constant's own *value* — it advances the fake clock by whatever
|
|
132
|
+
* `GATEWAY_REQUEST_TIMEOUT_MS` currently holds, so on its own it cannot catch an edit to the constant
|
|
133
|
+
* either (fix round 2, verification finding V2). `gateway.test.ts` also asserts this constant against
|
|
134
|
+
* the literal `30_000` directly, in its own dedicated test — that is what actually fails if this
|
|
135
|
+
* constant's value ever changes.
|
|
136
|
+
*/
|
|
137
|
+
const GATEWAY_REQUEST_TIMEOUT_MS = 3e4;
|
|
138
|
+
/**
|
|
139
|
+
* Posts the card straight to the gateway and resolves the gateway leg's `userErrors` (empty on success).
|
|
140
|
+
* A non-2xx response resolves one `GATEWAY_ERROR`; a rejected request — including this leg's own
|
|
141
|
+
* timeout firing, or `options.signal` aborting — propagates, and the handler in `use-payment-actions.ts`
|
|
142
|
+
* collapses it to the same one. Neither outcome reads the response body: it may echo the request, and
|
|
143
|
+
* it tells the shopper nothing more actionable than "try again".
|
|
144
|
+
*
|
|
145
|
+
* `options.signal`, when given, aborts this request in *addition to* — never instead of — this module's
|
|
146
|
+
* own timeout: a caller opts into an earlier or externally triggered abort, but cannot opt out of the
|
|
147
|
+
* budget that keeps this leg from hanging forever (FR-6, US-3). Composed with a plain `AbortController`
|
|
148
|
+
* rather than `AbortSignal.any`/`AbortSignal.timeout` so the timeout is driven by an ordinary
|
|
149
|
+
* `setTimeout`, the one implementation a test can advance deterministically with fake timers.
|
|
150
|
+
*
|
|
151
|
+
* No caller in this package passes `options.signal` today — it exists to satisfy the spec's own
|
|
152
|
+
* Technical Contract (`specs/make-an-order-attempt-survive-a.md`) ahead of a future integration. Fix
|
|
153
|
+
* pass, Low finding: the listener this composes onto `externalSignal` is now removed in the `finally`
|
|
154
|
+
* below on every exit path, not only when it fires — previously, a caller that supplied a long-lived
|
|
155
|
+
* signal (as opposed to a fresh one per call) would accumulate one listener, and this function's own
|
|
156
|
+
* dead `AbortController`, per call that resolved normally.
|
|
157
|
+
*/
|
|
158
|
+
async function postPaymentsToGateway(request, options) {
|
|
159
|
+
const { url, body } = buildGatewayPaymentsRequest(request);
|
|
160
|
+
const controller = new AbortController();
|
|
161
|
+
const timeoutId = setTimeout(() => controller.abort(new DOMException("The gateway request timed out.", "TimeoutError")), GATEWAY_REQUEST_TIMEOUT_MS);
|
|
162
|
+
const externalSignal = options?.signal;
|
|
163
|
+
const onExternalAbort = () => controller.abort(externalSignal?.reason);
|
|
164
|
+
if (externalSignal) {
|
|
165
|
+
if (externalSignal.aborted) controller.abort(externalSignal.reason);
|
|
166
|
+
else externalSignal.addEventListener("abort", onExternalAbort, { once: true });
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
return (await fetch(url, {
|
|
170
|
+
method: "POST",
|
|
171
|
+
headers: { "Content-Type": "application/json" },
|
|
172
|
+
body: JSON.stringify(body),
|
|
173
|
+
signal: controller.signal
|
|
174
|
+
})).ok ? [] : [PAYMENT_GATEWAY_USER_ERROR];
|
|
175
|
+
} finally {
|
|
176
|
+
clearTimeout(timeoutId);
|
|
177
|
+
externalSignal?.removeEventListener("abort", onExternalAbort);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
181
|
+
export { GATEWAY_REQUEST_TIMEOUT_MS, PAYMENT_GATEWAY_ERROR_CODE, PAYMENT_GATEWAY_USER_ERROR, buildGatewayPaymentsRequest, postPaymentsToGateway };
|
|
182
|
+
|
|
183
|
+
//# sourceMappingURL=gateway.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway.js","names":[],"sources":["../../../src/sdk/payment/gateway.ts"],"sourcesContent":["/**\n * The direct-to-gateway card POST (specs/payment-action-namespace.md, US-6) — the PCI boundary of the\n * credit-card feature. A new card's number and CVV, or a saved card's CVV, go from the browser straight\n * to the VTEX payment gateway and never transit our GraphQL layer or the normalized Apollo cache\n * (RFC D13; ADR-0010's sanctioned façade flavor).\n *\n * This module imports no Apollo and no GraphQL symbol on purpose: the code that holds card data has no\n * route to the cache, and that property is checkable by reading the imports above. Card data arrives as\n * a runtime argument from the card-entry component's local state (T7) — it is never a declared action\n * payload, a fragment field, or a schema-bindable prop, so the AI Store Builder and the visual editor\n * have no name to map a value onto.\n *\n * Nothing on this path is ever logged, serialized, or stored anywhere but the request body: the caller\n * in `use-payment-actions.ts` logs a static message plus the error's `name` only.\n *\n * The request shape is behavior evidence from the legacy checkout\n * (`fastcheckout@cd09bec3`, `packages/apps/client/src/screens/Payment/components/CardForm/services/gateway.service.ts`)\n * — never a port. Where the legacy hardcoded a value under a `TODO` (installment count, interest rate,\n * reference value) this builder reads it off the transaction descriptor instead.\n */\n\nimport { isTrustedGatewayOrigin } from './gateway-origin.js';\nimport type { PaymentUserError } from './operations.js';\n\n/** Client-synthesized code for a failed gateway leg — distinct from the BFF's `NETWORK_ERROR`. */\nexport const PAYMENT_GATEWAY_ERROR_CODE = 'GATEWAY_ERROR';\n\n/** The single userError both a rejected request and a non-2xx response resolve to. */\nexport const PAYMENT_GATEWAY_USER_ERROR: PaymentUserError = {\n code: PAYMENT_GATEWAY_ERROR_CODE,\n message: 'Unable to complete the payment. Please try again.',\n};\n\n/**\n * Non-sensitive descriptor of the open transaction. Every field maps to a value the caller already\n * holds — `paymentStartTransaction`'s payload (T3) or the `Payment` read model (T1) — except\n * `gatewayUrl` and `merchantName`, which that payload does not carry today (Gaps 1 and 2 in the spec).\n * Amounts are in the currency's **minor units**; `PaymentStartTransactionPayload.value.amount` is a\n * decimal, so the caller converts once, visibly, rather than having this module assume an exponent.\n */\nexport type PaymentGatewayTransaction = Readonly<{\n /**\n * Gateway origin, e.g. `https://acme.vtexpayments.com.br`. Server-resolved by T18 and read off\n * `paymentStartTransaction`'s payload; never derived here. It is nullable there and required here,\n * so the builder re-checks it (`isTrustedGatewayOrigin`) rather than trusting the type.\n */\n gatewayUrl: string;\n /** `PaymentStartTransactionPayload.transactionId`. */\n transactionId: string;\n /** VTEX merchant name for this transaction. Resolved server-side by T3 but not exposed on its payload. */\n merchantName: string;\n /** `PaymentStartTransactionPayload.orderGroup` — the gateway's `orderId` query parameter. */\n orderGroup: string;\n /** `PaymentStartTransactionPayload.userProfileId`; null for a guest order, so the query parameter is left out. */\n userProfileId: string | null;\n /** `Payment.selectedMethodId` — the VTEX payment-system id. */\n paymentSystem: string;\n /** `PaymentMethod.groupName`, e.g. `creditCardPaymentGroup`. */\n groupName: string;\n /** `PaymentStartTransactionPayload.value.currencyCode`. */\n currencyCode: string;\n /** Transaction total, in minor units. */\n valueInCents: number;\n /** Pre-interest base (`PaymentInstallmentOption.baseValue`), in minor units. */\n referenceValueInCents: number;\n /** `Payment.selectedInstallment.count`; 1 when the shopper never picked a plan. */\n installments: number;\n /** Per-installment amount (`PaymentInstallment.value`), in minor units. */\n installmentsValueInCents: number;\n /** `PaymentInstallment.interestRate`; 0 for the interest-free 1x default. */\n interestRate: number;\n /** Absolute return URL. Phase-2 redirect mode only; omitted in v1's direct mode. */\n callbackUrl?: string;\n /**\n * Billing differs from the delivery address. Defaults to `false`, because `setBillingAddress` persists\n * billing on the order form's `invoiceData` (T3) and the gateway reads it from there rather than from\n * this body. The call site that would override it is T13's `usePlaceOrder`, the one place the\n * descriptor is assembled; T11 owns the live verification of the assumption and publishes the flag to\n * T5's `draft` for T13 to read. Confirming it never reopens this module.\n */\n billingAddressDifferent?: boolean;\n}>;\n\n/** A newly entered card. Runtime argument only — never declared, never logged, never cached. */\nexport type PaymentGatewayNewCard = Readonly<{\n kind: 'new';\n /** Full card number. The builder strips whatever separators the method's `numberMask` inserted. */\n number: string;\n /** Security code (CVV). */\n securityCode: string;\n /** Expiry. Normalized to `MM/YY`, so a `MM/YYYY` mask is accepted. */\n expirationDate: string;\n holderName: string;\n /** Shopper document (a CPF). The builder strips its punctuation. */\n document?: string;\n /** Shopper-assigned label, when the save-card control collected one. */\n cardLabel?: string;\n /** The card-save flag itself — the other half of `payment.saveCard`'s consent write (RFC D5). */\n saveCard?: boolean;\n}>;\n\n/** A previously saved card: its account reference plus the CVV the shopper re-enters. */\nexport type PaymentGatewaySavedCard = Readonly<{\n kind: 'saved';\n /** `SavedCard.accountId`. */\n accountId: string;\n /** `SavedCard.bin` — not sensitive; the read model exposes it for installment lookup. */\n bin: string;\n /** Security code (CVV). */\n securityCode: string;\n}>;\n\nexport type PaymentGatewayCard = PaymentGatewayNewCard | PaymentGatewaySavedCard;\n\nexport type SendPaymentsRequest = Readonly<{\n transaction: PaymentGatewayTransaction;\n card: PaymentGatewayCard;\n}>;\n\n/** The assembled request. Exported so the body shape is unit-testable without issuing a POST. */\nexport type GatewayPaymentsRequest = Readonly<{ url: string; body: readonly unknown[] }>;\n\n/** VTEX bins the leading eight digits, not six — the legacy's own `sanitizedBin` slice. */\nconst BIN_LENGTH = 8;\n\n/**\n * Strips the separators a mask inserted. `PaymentMethodValidator.numberMask` is server-driven (T1) and the\n * card-entry component (T7) renders the input through it, so its local state holds `4111 1111 1111 1111`\n * rather than the digits. The legacy normalized here, in the builder, before its own BIN slice\n * (`card.cardNumber = card.cardNumber.replace(/ /g, '')`) — the boundary is the one place the obligation\n * cannot be forgotten, and a spaced BIN is a lookup the gateway fails with no diagnosable error.\n */\nfunction toDigits(value: string): string {\n return value.replace(/\\D/g, '');\n}\n\n/** `MM/YY` whatever the mask produced (`MM/YYYY`, `MMYY`) — the legacy's own two-slice normalization. */\nfunction toDueDate(expirationDate: string): string {\n const digits = toDigits(expirationDate);\n return `${digits.slice(0, 2)}/${digits.slice(-2)}`;\n}\n\n/** The card half of the body — the BIN and the `fields` map, both derived from one normalization pass. */\nfunction toCardBody(card: PaymentGatewayCard): { bin: string; fields: Record<string, unknown> } {\n if (card.kind === 'saved') {\n return { bin: card.bin, fields: { bin: card.bin, validationCode: card.securityCode } };\n }\n\n const number = toDigits(card.number);\n const bin = number.slice(0, BIN_LENGTH);\n\n return {\n bin,\n fields: {\n bin,\n cardNumber: number,\n // The legacy's own `\\W` strip, so a CPF typed `123.456.789-00` reaches the gateway unpunctuated.\n document: card.document === undefined ? null : card.document.replace(/\\W/g, ''),\n dueDate: toDueDate(card.expirationDate),\n holderName: card.holderName,\n validationCode: card.securityCode,\n cardLabel: card.cardLabel,\n savePaymentData: card.saveCard ?? false,\n },\n };\n}\n\n/**\n * Builds the gateway URL and body. Billing is deliberately absent from the body: `setBillingAddress`\n * already persisted it on the order form's `invoiceData` (T3), so the gateway reads it from there — hence\n * the `billingAddressDifferent: false` default the two billing flags derive from. `originalPaymentIndex`\n * is the legacy's own constant for a single-payment order.\n *\n * The origin check is the last line of defence, not the first: `usePlaceOrder` already refuses an\n * untrusted origin *before* asking a holder for the card (T13, Decision 4). A builder has no userError\n * channel, so it throws — `sendPayments` catches, collapses it to `GATEWAY_ERROR`, and logs the error's\n * name only, so nothing about the card reaches a log. The throw happens before the body is assembled.\n */\nexport function buildGatewayPaymentsRequest({ transaction, card }: SendPaymentsRequest): GatewayPaymentsRequest {\n if (!isTrustedGatewayOrigin(transaction.gatewayUrl)) {\n throw new Error('Refusing to post card data: the transaction carries no trusted https gateway origin.');\n }\n\n const params = new URLSearchParams({ orderId: transaction.orderGroup });\n if (transaction.userProfileId !== null) params.append('userProfileId', transaction.userProfileId);\n if (transaction.callbackUrl !== undefined) params.append('callbackUrl', transaction.callbackUrl);\n\n const url = `${transaction.gatewayUrl}/api/pub/transactions/${transaction.transactionId}/payments?${params}`;\n\n const { bin, fields } = toCardBody(card);\n const billingAddressDifferent = transaction.billingAddressDifferent ?? false;\n\n const payment = {\n accountId: card.kind === 'saved' ? card.accountId : null,\n bin,\n chooseToUseNewCard: card.kind === 'new',\n currencyCode: transaction.currencyCode,\n fields,\n groupName: transaction.groupName,\n hasDefaultBillingAddress: !billingAddressDifferent,\n id: transaction.merchantName,\n installmentValue: transaction.installmentsValueInCents,\n installments: transaction.installments,\n installmentsInterestRate: transaction.interestRate,\n installmentsValue: transaction.installmentsValueInCents,\n interestRate: transaction.interestRate,\n isBillingAddressDifferent: billingAddressDifferent,\n originalPaymentIndex: 1,\n paymentSystem: transaction.paymentSystem,\n referenceValue: transaction.referenceValueInCents,\n tokenId: null,\n transaction: { id: transaction.transactionId, merchantName: transaction.merchantName },\n value: transaction.valueInCents,\n };\n\n return { url, body: [payment] };\n}\n\n/**\n * T29 (FR-6, OQ-7): this leg's own abort budget. Before this task the direct-to-gateway POST carried no\n * `AbortSignal` and no timeout at all, so a gateway that never answered left `placing` true and the CTA\n * dead for the life of the mount (Route C, `place-order.md`'s Known limitations). The BFF's own default\n * is 10s (`apps/storefront-api-core/.../api-timeout.ts:9`), but a card authorization can legitimately\n * run longer than a GraphQL round trip, and this exact number is not confirmed with payments (spec\n * OQ-7) — a placeholder pending that answer, not a tuned production value.\n *\n * Exported so `gateway.test.ts`'s own abort-budget test can assert the abort fires at exactly this\n * duration, rather than against `vi.runAllTimersAsync()`, which fires any pending timer regardless of\n * duration and would stay green even if this were edited to `300_000`. That mechanism test alone is\n * still self-referential about this constant's own *value* — it advances the fake clock by whatever\n * `GATEWAY_REQUEST_TIMEOUT_MS` currently holds, so on its own it cannot catch an edit to the constant\n * either (fix round 2, verification finding V2). `gateway.test.ts` also asserts this constant against\n * the literal `30_000` directly, in its own dedicated test — that is what actually fails if this\n * constant's value ever changes.\n */\nexport const GATEWAY_REQUEST_TIMEOUT_MS = 30_000;\n\n/**\n * Posts the card straight to the gateway and resolves the gateway leg's `userErrors` (empty on success).\n * A non-2xx response resolves one `GATEWAY_ERROR`; a rejected request — including this leg's own\n * timeout firing, or `options.signal` aborting — propagates, and the handler in `use-payment-actions.ts`\n * collapses it to the same one. Neither outcome reads the response body: it may echo the request, and\n * it tells the shopper nothing more actionable than \"try again\".\n *\n * `options.signal`, when given, aborts this request in *addition to* — never instead of — this module's\n * own timeout: a caller opts into an earlier or externally triggered abort, but cannot opt out of the\n * budget that keeps this leg from hanging forever (FR-6, US-3). Composed with a plain `AbortController`\n * rather than `AbortSignal.any`/`AbortSignal.timeout` so the timeout is driven by an ordinary\n * `setTimeout`, the one implementation a test can advance deterministically with fake timers.\n *\n * No caller in this package passes `options.signal` today — it exists to satisfy the spec's own\n * Technical Contract (`specs/make-an-order-attempt-survive-a.md`) ahead of a future integration. Fix\n * pass, Low finding: the listener this composes onto `externalSignal` is now removed in the `finally`\n * below on every exit path, not only when it fires — previously, a caller that supplied a long-lived\n * signal (as opposed to a fresh one per call) would accumulate one listener, and this function's own\n * dead `AbortController`, per call that resolved normally.\n */\nexport async function postPaymentsToGateway(\n request: SendPaymentsRequest,\n options?: { signal?: AbortSignal },\n): Promise<readonly PaymentUserError[]> {\n const { url, body } = buildGatewayPaymentsRequest(request);\n\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(new DOMException('The gateway request timed out.', 'TimeoutError')),\n GATEWAY_REQUEST_TIMEOUT_MS,\n );\n const externalSignal = options?.signal;\n const onExternalAbort = () => controller.abort(externalSignal?.reason);\n if (externalSignal) {\n if (externalSignal.aborted) {\n controller.abort(externalSignal.reason);\n } else {\n externalSignal.addEventListener('abort', onExternalAbort, { once: true });\n }\n }\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n return response.ok ? [] : [PAYMENT_GATEWAY_USER_ERROR];\n } finally {\n clearTimeout(timeoutId);\n externalSignal?.removeEventListener('abort', onExternalAbort);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import "../cart/operations.js";
|
|
2
|
+
import { TypedDocumentNode } from "@apollo/client";
|
|
3
|
+
//#region src/sdk/payment/operations.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Payment write-model GraphQL operations (specs/payment-action-namespace.md, T4) — the five mutations
|
|
6
|
+
* `api-module:PaymentWriteModel` shipped (T3, `checkout/schema.graphql` § 11). Nothing here is
|
|
7
|
+
* invented: every mutation, input field, and payload field is pinned to that schema.
|
|
8
|
+
*
|
|
9
|
+
* Authored with the hand `gql` tag — the codegen `graphql()` factory is reserved for the reactive read
|
|
10
|
+
* path — and excluded from the codegen `documents` glob (see `codegen.ts`): the `gql` tag alone is
|
|
11
|
+
* still plucked by `graphql-codegen`, which would duplicate these hand-written types in
|
|
12
|
+
* `sdk/generated/`. Mirrors `shipping/option-select-operations.ts` and `identity/operations.ts`.
|
|
13
|
+
*
|
|
14
|
+
* No field at any nesting depth carries a card number or CVV — the read model guarantees it (RFC D13),
|
|
15
|
+
* and the one call that does carry them never touches GraphQL at all (see `gateway.ts`).
|
|
16
|
+
*
|
|
17
|
+
* Failures follow ADR-00007's two-path contract: domain outcomes arrive as payload `userErrors`;
|
|
18
|
+
* transport and top-level GraphQL failures are infrastructure, collapsed by the handlers in
|
|
19
|
+
* `use-payment-actions.ts` to the one client-synthesized code, `PAYMENT_NETWORK_ERROR_CODE`.
|
|
20
|
+
*/
|
|
21
|
+
/** A payment domain error. Every mutation's error type is `{ code, message }`; codes stay strings. */
|
|
22
|
+
export type PaymentUserError = Readonly<{
|
|
23
|
+
code: string;
|
|
24
|
+
message: string;
|
|
25
|
+
}>;
|
|
26
|
+
export type PaymentMoney = Readonly<{
|
|
27
|
+
__typename?: 'Money';
|
|
28
|
+
amount: number;
|
|
29
|
+
currencyCode: string;
|
|
30
|
+
}>;
|
|
31
|
+
/** A `bin` with an `accountId` is validated against the saved card's own BIN (MISMATCHED_BIN_FOR_SAVED_CARD). */
|
|
32
|
+
export type PaymentMethodSelectInput = Readonly<{
|
|
33
|
+
methodId: string;
|
|
34
|
+
bin?: string;
|
|
35
|
+
accountId?: string;
|
|
36
|
+
}>;
|
|
37
|
+
/** With `sameAsDelivery: true` every other field is ignored and the server performs no upstream write. */
|
|
38
|
+
export type PaymentBillingAddressUpdateInput = Readonly<{
|
|
39
|
+
sameAsDelivery: boolean;
|
|
40
|
+
addressType?: string;
|
|
41
|
+
postalCode?: string;
|
|
42
|
+
country?: string;
|
|
43
|
+
street?: string;
|
|
44
|
+
number?: string;
|
|
45
|
+
complement?: string;
|
|
46
|
+
neighborhood?: string;
|
|
47
|
+
city?: string;
|
|
48
|
+
state?: string;
|
|
49
|
+
reference?: string;
|
|
50
|
+
}>;
|
|
51
|
+
//#endregion
|
|
52
|
+
//# sourceMappingURL=operations.d.ts.map
|