@vtex/faststore 0.0.11 → 0.0.12
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 +13 -0
- package/dist/_virtual/_rolldown/runtime.js +4 -0
- package/dist/domain-logic/dist/checkout/rules.js +33 -0
- package/dist/domain-logic/dist/checkout/rules.js.map +1 -0
- package/dist/sdk/payment/bin-format.js +3 -2
- package/dist/sdk/payment/bin-format.js.map +1 -1
- package/dist/sdk/payment/use-payment-actions.js +2 -3
- package/dist/sdk/payment/use-payment-actions.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ Every published version of `@vtex/faststore` has a section here. From `0.0.2` on
|
|
|
4
4
|
written when its release pull request opens, and reviewed alongside the API delta that justifies the
|
|
5
5
|
version.
|
|
6
6
|
|
|
7
|
+
## 0.0.12
|
|
8
|
+
|
|
9
|
+
**Fixed: a store on 0.0.11 failed to build.** 0.0.11 shipped two payment modules that import
|
|
10
|
+
`@vtex/fs-domain-logic/checkout/rules`, a private package no store can install. The generated app
|
|
11
|
+
reaches them through `./internal/client`, so its build stopped with "Module not found". The card BIN
|
|
12
|
+
rule is now bundled into the package, as it was meant to be.
|
|
13
|
+
|
|
14
|
+
No change to `.`, `./client`, `./components`, `./graphql` or `./cli`: the public type surface is the
|
|
15
|
+
same as 0.0.11. A store on 0.0.11 only needs to move to this version.
|
|
16
|
+
|
|
17
|
+
The release build now fails when a workspace import does not resolve, and the install smoke rejects
|
|
18
|
+
a `dist/` that imports a private workspace package.
|
|
19
|
+
|
|
7
20
|
## 0.0.11
|
|
8
21
|
|
|
9
22
|
**Breaking in `./client`: no `react-intl`.** `FormattedMessage`, `useIntl`, `MessageId` and `Messages`
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { __commonJSMin } from "../../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
//#region ../domain-logic/dist/checkout/rules.js
|
|
3
|
+
var require_rules = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.INVALID_BIN_MESSAGE = void 0;
|
|
6
|
+
exports.isAcceptedCardBin = isAcceptedCardBin;
|
|
7
|
+
/**
|
|
8
|
+
* The text a refused `bin` carries, in the API's `userErrors` and in the SDK's
|
|
9
|
+
* `payment.setMethod` refusal. Fixed on purpose: the caller's value must never
|
|
10
|
+
* come back in it.
|
|
11
|
+
*/
|
|
12
|
+
exports.INVALID_BIN_MESSAGE = "bin is not in an accepted format";
|
|
13
|
+
/** A newly typed card: the full BIN, exactly eight ASCII digits. */
|
|
14
|
+
const NEW_CARD_BIN = /^\d{8}$/;
|
|
15
|
+
/** A saved card: its own stored BIN restated, which can be shorter. Only the cap stays. */
|
|
16
|
+
const SAVED_CARD_BIN = /^\d{1,8}$/;
|
|
17
|
+
/**
|
|
18
|
+
* The one rule `paymentMethodSelect` applies to `bin`, shared by the API and by
|
|
19
|
+
* the SDK that keeps the value from leaving the browser. A new card (no
|
|
20
|
+
* `accountId`, or an empty one) accepts exactly eight ASCII digits; a saved card
|
|
21
|
+
* accepts one to eight, because the caller has already compared the value to the
|
|
22
|
+
* card's own stored BIN. An absent value is the caller's business: this helper
|
|
23
|
+
* is only reached with a string.
|
|
24
|
+
*/
|
|
25
|
+
function isAcceptedCardBin(bin, savedCard) {
|
|
26
|
+
return (savedCard ? SAVED_CARD_BIN : NEW_CARD_BIN).test(bin);
|
|
27
|
+
}
|
|
28
|
+
}));
|
|
29
|
+
//#endregion
|
|
30
|
+
export default require_rules();
|
|
31
|
+
export { require_rules };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","names":[],"sources":["../../../../../domain-logic/dist/checkout/rules.js"],"sourcesContent":["\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.INVALID_BIN_MESSAGE = void 0;\nexports.mapAppliedCoupons = mapAppliedCoupons;\nexports.parseCouponSet = parseCouponSet;\nexports.isAcceptedCardBin = isAcceptedCardBin;\nexports.detectCouponRejection = detectCouponRejection;\nconst COUPON_MATCH_KEY = 'couponCode@Marketing';\n// The only two coupon message codes the backend emits (both HTTP 200 warnings).\n// We do not recognize others (min-value, usage-limit, etc.).\nconst COUPON_MESSAGE_TO_REJECTION = {\n couponNotFound: 'COUPON_NOT_FOUND',\n couponExpired: 'COUPON_EXPIRED',\n};\n/**\n * Splits the comma-separated `marketingData.coupon` string into distinct codes\n * (trimmed, blanks dropped, deduped) and flags each with whether its promotion\n * conditions matched. Mirrors fastcheckout's `coupon.factory`: a code is matched\n * (isUnmatchedCondition: false) when some rateAndBenefitsIdentifier references it\n * via matchedParameters['couponCode@Marketing']. The match is compared\n * case-insensitively, consistent with the backend's case-insensitive RnB match,\n * while the code itself is preserved verbatim. Returns [] when no coupon is set.\n */\nfunction mapAppliedCoupons(marketingData, ratesAndBenefitsData) {\n const codes = parseCouponSet(marketingData?.coupon ?? null);\n if (codes.length === 0)\n return [];\n const matchedCodes = new Set((ratesAndBenefitsData?.rateAndBenefitsIdentifiers ?? [])\n .map((identifier) => identifier.matchedParameters?.[COUPON_MATCH_KEY])\n .filter((code) => code != null)\n .map((code) => code.trim().toLowerCase()));\n return codes.map((code) => ({\n code,\n isUnmatchedCondition: !matchedCodes.has(code.toLowerCase()),\n }));\n}\n/**\n * Parses the comma-separated coupon field into distinct codes: split on `,`,\n * trim, drop blanks, dedupe (first occurrence wins, original order kept).\n */\nfunction parseCouponSet(coupon) {\n if (!coupon)\n return [];\n const seen = new Set();\n const codes = [];\n for (const raw of coupon.split(',')) {\n const code = raw.trim();\n if (code.length === 0 || seen.has(code))\n continue;\n seen.add(code);\n codes.push(code);\n }\n return codes;\n}\n/**\n * The text a refused `bin` carries, in the API's `userErrors` and in the SDK's\n * `payment.setMethod` refusal. Fixed on purpose: the caller's value must never\n * come back in it.\n */\nexports.INVALID_BIN_MESSAGE = 'bin is not in an accepted format';\n/** A newly typed card: the full BIN, exactly eight ASCII digits. */\nconst NEW_CARD_BIN = /^\\d{8}$/;\n/** A saved card: its own stored BIN restated, which can be shorter. Only the cap stays. */\nconst SAVED_CARD_BIN = /^\\d{1,8}$/;\n/**\n * The one rule `paymentMethodSelect` applies to `bin`, shared by the API and by\n * the SDK that keeps the value from leaving the browser. A new card (no\n * `accountId`, or an empty one) accepts exactly eight ASCII digits; a saved card\n * accepts one to eight, because the caller has already compared the value to the\n * card's own stored BIN. An absent value is the caller's business: this helper\n * is only reached with a string.\n */\nfunction isAcceptedCardBin(bin, savedCard) {\n return (savedCard ? SAVED_CARD_BIN : NEW_CARD_BIN).test(bin);\n}\n/**\n * Scans an order form's messages for a coupon rejection (couponNotFound /\n * couponExpired), or null when none is present. Rejection is signalled in\n * messages, not HTTP status; the first coupon message wins.\n */\nfunction detectCouponRejection(orderForm) {\n for (const message of orderForm.messages) {\n const rejection = message.code ? COUPON_MESSAGE_TO_REJECTION[message.code] : undefined;\n if (rejection)\n return rejection;\n }\n return null;\n}\n//# sourceMappingURL=rules.js.map"],"mappings":";;;CACA,OAAO,eAAe,SAAS,cAAc,EAAE,OAAO,KAAK,CAAC;CAC5D,QAAQ,sBAAsB,KAAK;CAGnC,QAAQ,oBAAoB;;;;;;CAsD5B,QAAQ,sBAAsB;;CAE9B,MAAM,eAAe;;CAErB,MAAM,iBAAiB;;;;;;;;;CASvB,SAAS,kBAAkB,KAAK,WAAW;EACvC,QAAQ,YAAY,iBAAiB,aAAA,CAAc,KAAK,GAAG;CAC/D"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { require_rules } from "../../domain-logic/dist/checkout/rules.js";
|
|
2
2
|
//#region src/sdk/payment/bin-format.ts
|
|
3
|
+
var import_rules = require_rules();
|
|
3
4
|
/**
|
|
4
5
|
* The browser-side use of the rule `paymentMethodSelect` applies to `bin`, so a malformed value never leaves
|
|
5
6
|
* through `payment.setMethod`. The rule itself lives in `@vtex/fs-domain-logic/checkout/rules`, shared with the
|
|
@@ -12,7 +13,7 @@ import { isAcceptedCardBin } from "@vtex/fs-domain-logic/checkout/rules";
|
|
|
12
13
|
function isAcceptedBin({ bin, accountId }) {
|
|
13
14
|
if (bin === void 0 || bin === null || bin === "") return true;
|
|
14
15
|
if (typeof bin !== "string") return false;
|
|
15
|
-
return isAcceptedCardBin(bin, Boolean(accountId));
|
|
16
|
+
return (0, import_rules.isAcceptedCardBin)(bin, Boolean(accountId));
|
|
16
17
|
}
|
|
17
18
|
//#endregion
|
|
18
19
|
export { isAcceptedBin };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin-format.js","names":[],"sources":["../../../src/sdk/payment/bin-format.ts"],"sourcesContent":["import { isAcceptedCardBin } from '@vtex/fs-domain-logic/checkout/rules';\n\n/**\n * The browser-side use of the rule `paymentMethodSelect` applies to `bin`, so a malformed value never leaves\n * through `payment.setMethod`. The rule itself lives in `@vtex/fs-domain-logic/checkout/rules`, shared with the\n * server, so the two can no longer drift; the fixed refusal text lives there too. A new card (no `accountId`,\n * or an empty one) carries nothing or exactly eight digits; a saved card carries nothing or one to eight,\n * because the server compares it to the card's own stored BIN. `null` counts as nothing, like the server; any\n * other value that is not a string is refused. Reads the value and returns a boolean: it never stores, logs or\n * interpolates it.\n */\nexport function isAcceptedBin({ bin, accountId }: Readonly<{ bin?: string | null; accountId?: string }>): boolean {\n if (bin === undefined || bin === null || bin === '') return true;\n if (typeof bin !== 'string') return false;\n return isAcceptedCardBin(bin, Boolean(accountId));\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"bin-format.js","names":["isAcceptedCardBin"],"sources":["../../../src/sdk/payment/bin-format.ts"],"sourcesContent":["import { isAcceptedCardBin } from '@vtex/fs-domain-logic/checkout/rules';\n\n/**\n * The browser-side use of the rule `paymentMethodSelect` applies to `bin`, so a malformed value never leaves\n * through `payment.setMethod`. The rule itself lives in `@vtex/fs-domain-logic/checkout/rules`, shared with the\n * server, so the two can no longer drift; the fixed refusal text lives there too. A new card (no `accountId`,\n * or an empty one) carries nothing or exactly eight digits; a saved card carries nothing or one to eight,\n * because the server compares it to the card's own stored BIN. `null` counts as nothing, like the server; any\n * other value that is not a string is refused. Reads the value and returns a boolean: it never stores, logs or\n * interpolates it.\n */\nexport function isAcceptedBin({ bin, accountId }: Readonly<{ bin?: string | null; accountId?: string }>): boolean {\n if (bin === undefined || bin === null || bin === '') return true;\n if (typeof bin !== 'string') return false;\n return isAcceptedCardBin(bin, Boolean(accountId));\n}\n"],"mappings":";;;;;;;;;;;;AAWA,SAAgB,cAAc,EAAE,KAAK,aAA6E;CAChH,IAAI,QAAQ,KAAA,KAAa,QAAQ,QAAQ,QAAQ,IAAI,OAAO;CAC5D,IAAI,OAAO,QAAQ,UAAU,OAAO;CACpC,QAAA,GAAOA,aAAAA,kBAAAA,CAAkB,KAAK,QAAQ,SAAS,CAAC;AAClD"}
|
|
@@ -4,15 +4,14 @@ import "../../core/dist/index.js";
|
|
|
4
4
|
import { loggableErrorName } from "../../utils/loggable-error-name.js";
|
|
5
5
|
import { PAYMENT_BILLING_ADDRESS_UPDATE, PAYMENT_GATEWAY_CALLBACK, PAYMENT_INSTALLMENT_SELECT, PAYMENT_METHOD_SELECT, PAYMENT_NETWORK_ERROR_CODE, PAYMENT_START_TRANSACTION } from "./operations.js";
|
|
6
6
|
import { CLIENT_PREFERENCES_UPDATE } from "../identity/operations.js";
|
|
7
|
+
import { require_rules } from "../../domain-logic/dist/checkout/rules.js";
|
|
7
8
|
import { isAcceptedBin } from "./bin-format.js";
|
|
8
9
|
import { PAYMENT_GATEWAY_USER_ERROR, postPaymentsToGateway } from "./gateway.js";
|
|
9
10
|
import { useMutation } from "@apollo/client/react";
|
|
10
11
|
import { useCallback, useMemo } from "react";
|
|
11
|
-
import { INVALID_BIN_MESSAGE } from "@vtex/fs-domain-logic/checkout/rules";
|
|
12
|
-
//#region src/sdk/payment/use-payment-actions.ts
|
|
13
12
|
const INVALID_BIN_ERROR = {
|
|
14
13
|
code: "INVALID_BIN_FORMAT",
|
|
15
|
-
message: INVALID_BIN_MESSAGE
|
|
14
|
+
message: require_rules().INVALID_BIN_MESSAGE
|
|
16
15
|
};
|
|
17
16
|
const NETWORK_ERROR = {
|
|
18
17
|
code: PAYMENT_NETWORK_ERROR_CODE,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-payment-actions.js","names":[],"sources":["../../../src/sdk/payment/use-payment-actions.ts"],"sourcesContent":["'use client';\n\nimport { useMutation } from '@apollo/client/react';\nimport { defineActions } from '@vtex/fs-core';\nimport { INVALID_BIN_MESSAGE } from '@vtex/fs-domain-logic/checkout/rules';\nimport { useCallback, useMemo } from 'react';\nimport { loggableErrorName } from '../../utils/loggable-error-name.js';\nimport { CLIENT_PREFERENCES_UPDATE } from '../identity/operations.js';\nimport { isAcceptedBin } from './bin-format.js';\nimport {\n PAYMENT_GATEWAY_USER_ERROR,\n type PaymentGatewayResult,\n postPaymentsToGateway,\n type SendPaymentsRequest,\n} from './gateway.js';\nimport {\n PAYMENT_BILLING_ADDRESS_UPDATE,\n PAYMENT_GATEWAY_CALLBACK,\n PAYMENT_INSTALLMENT_SELECT,\n PAYMENT_METHOD_SELECT,\n PAYMENT_NETWORK_ERROR_CODE,\n PAYMENT_START_TRANSACTION,\n type PaymentBillingAddressUpdateInput,\n type PaymentMethodSelectInput,\n type PaymentMoney,\n type PaymentUserError,\n} from './operations.js';\n\n/**\n * The `payment` action namespace — the sanctioned client write surface for credit-card payment\n * (specs/payment-action-namespace.md; docs/features/credit-card/rfc.md § D12;\n * docs/storefront-core/adr-0010-actions-own-the-write-side.md).\n *\n * Six mutation-backed methods run the write model T3 shipped (`checkout/schema.graphql` § 11), except\n * `saveCard`, which reuses the already-shipped `clientPreferencesUpdate` document this package owns —\n * there is no `paymentSaveCard` mutation and T3 decided there must not be one. All six are\n * async/pessimistic: the handler awaits the mutation and only the server-confirmed payload changes the\n * cache. Each is stateless (no reactive vars, no module state), resolves its payload `userErrors` (empty\n * on success), and never throws for an expected outcome; an infrastructure failure (transport or an\n * upstream GraphQLError) is caught and collapsed to one synthetic `NETWORK_ERROR` userError with no\n * per-code branching, mirroring `useShippingActions`.\n *\n * `sendPayments` is the seventh method and the odd one: a **façade** (ADR-0010's sanctioned flavor,\n * alongside `notifications`) that posts card data from the browser straight to the payment gateway,\n * touching neither our GraphQL layer nor the normalized cache. It is catalogued like any other method —\n * per D12, hiding a write route with no read-back would cost discoverability without protecting\n * anything, and the compiler wants to *see* which components reference it. What is deliberately absent\n * from the catalog is the card data itself: no `payload` entry below names a card number, CVV, expiry,\n * or holder name, so there is no schema-bindable name for the editor or the agent to map a value onto\n * (RFC D13). The card reaches the handler as a runtime argument from the card-entry component's local\n * state (T7), and `gateway.ts` — which has no Apollo import — is the only module that touches it.\n */\n\n/**\n * Everything `usePlaceOrder` (T13) needs to assemble `PaymentGatewayTransaction`, minus the five\n * fields that come from the `Payment` read model. Every field is nullable because a payload carrying\n * `userErrors` nulls all of them (T18 Decision 4), so a complete descriptor is only guaranteed once\n * `userErrors` is empty *and* the payload passes the caller's completeness check.\n */\nexport type PaymentStartTransactionResult = Readonly<{\n transactionId: string | null;\n merchantName: string | null;\n /** Gateway origin. Nullable, and never interpolated into a URL before `isTrustedGatewayOrigin`. */\n gatewayUrl: string | null;\n orderGroup: string | null;\n /**\n * The Relay Node ID of the order, for `Query.order(id:)`. Null when the transaction carried no order\n * group. Optional so a provider written before this field existed still satisfies the type.\n */\n orderNodeId?: string | null;\n userProfileId: string | null;\n /** Display decimal. The gateway body's unit is `valueInCents`. */\n value: PaymentMoney | null;\n valueInCents: number | null;\n referenceValueInCents: number | null;\n userErrors: readonly PaymentUserError[];\n}>;\n\nexport type PaymentGatewayCallbackResult = Readonly<{\n orderId: string | null;\n /** The Relay Node ID of the finalized order, for `Query.order(id:)`. Null unless the callback succeeded. */\n orderNodeId?: string | null;\n /** The bank's challenge page. Set only when the server answered a challenge with a usable URL. */\n redirectUrl?: string | null;\n userErrors: readonly PaymentUserError[];\n}>;\n\nexport type PaymentActions = Readonly<{\n /** Select a payment method, optionally identifying a new card's BIN or a saved card's account. */\n setMethod: (input: PaymentMethodSelectInput) => Promise<readonly PaymentUserError[]>;\n /** Select an installment count for the current method. The response's `cart.total` is the greater of the order form's value and the selected payment's value. */\n setInstallments: (count: number) => Promise<readonly PaymentUserError[]>;\n /** Persist the billing address, or fall back to the delivery address (`sameAsDelivery: true`). */\n setBillingAddress: (input: PaymentBillingAddressUpdateInput) => Promise<readonly PaymentUserError[]>;\n /**\n * Record the data-storage consent a saved card requires, over the existing `clientPreferencesUpdate`.\n * This does not by itself save the card: the save flag and label travel on `sendPayments`'s card\n * argument (RFC D5's two halves). The boolean passes through unchanged — whether unchecking should\n * revoke a consent an unrelated opt-in component may have granted is T10's decision, and its spec must\n * state one (Gap 4 in specs/payment-action-namespace.md).\n */\n saveCard: (enabled: boolean) => Promise<readonly PaymentUserError[]>;\n /** Open a VTEX transaction from the persisted selection. Resolves the payload the gateway call needs. */\n startTransaction: () => Promise<PaymentStartTransactionResult>;\n /**\n * Finalize the transaction after the gateway accepted the card. `acceptChallenges: true` is sent only by\n * the page the bank returns the shopper to, and only then; omitted, the server asks for a challenge\n * instead of finishing the order.\n */\n gatewayCallback: (\n orderGroup: string,\n options?: Readonly<{ acceptChallenges?: boolean }>,\n ) => Promise<PaymentGatewayCallbackResult>;\n /**\n * Façade: post the card straight to the payment gateway. Issues no GraphQL operation and writes\n * nothing to the cache. Never rejects: resolves `{ reachedGateway, userErrors }`, with one `GATEWAY_ERROR`\n * userError when the gateway rejects the call or it could not be issued. `reachedGateway` is `false`\n * only when no request was issued; every outcome it cannot classify is `true`.\n */\n sendPayments: (request: SendPaymentsRequest) => Promise<PaymentGatewayResult>;\n}>;\n\nconst INVALID_BIN_ERROR: PaymentUserError = { code: 'INVALID_BIN_FORMAT', message: INVALID_BIN_MESSAGE };\n\n// Diagnostic `message` (logs, `onResult`); each reader words `NETWORK_ERROR` from its own content.\nconst NETWORK_ERROR: PaymentUserError = {\n code: PAYMENT_NETWORK_ERROR_CODE,\n message: 'Unable to complete the request. Please try again.',\n};\n\n/** Mirrors the resolver's own nulled-fields constant, so one added field is nulled in one place. */\nconst EMPTY_START_TRANSACTION = {\n transactionId: null,\n merchantName: null,\n gatewayUrl: null,\n orderGroup: null,\n orderNodeId: null,\n userProfileId: null,\n value: null,\n valueInCents: null,\n referenceValueInCents: null,\n} as const;\n\n/**\n * The `payment` namespace hook. Registered with the first-party action registry via `paymentActions`\n * below; the compiler-emitted `PaymentActionProvider` calls it and threads the narrowed methods into\n * each consumer's `$actions.payment`. Writes persist purely through the normalized-cache merge of each\n * mutation response — no refetch, no imperative cache write.\n */\nexport function usePaymentActions(): PaymentActions {\n const [methodSelectMutation] = useMutation(PAYMENT_METHOD_SELECT);\n const [installmentSelectMutation] = useMutation(PAYMENT_INSTALLMENT_SELECT);\n const [billingAddressMutation] = useMutation(PAYMENT_BILLING_ADDRESS_UPDATE);\n const [preferencesMutation] = useMutation(CLIENT_PREFERENCES_UPDATE);\n const [startTransactionMutation] = useMutation(PAYMENT_START_TRANSACTION);\n const [gatewayCallbackMutation] = useMutation(PAYMENT_GATEWAY_CALLBACK);\n\n const setMethod = useCallback<PaymentActions['setMethod']>(\n async (input) => {\n // Refused here, before the value can reach the network, the GraphQL variables or the Apollo cache.\n // Logs nothing: the input carries the value being refused.\n if (!isAcceptedBin(input)) return [INVALID_BIN_ERROR];\n try {\n const result = await methodSelectMutation({ variables: { input } });\n return result.data?.paymentMethodSelect.userErrors ?? [];\n } catch (error) {\n // Log only the error name — the input carries the card's BIN, and a variable-coercion error's\n // message echoes the offending value (`got invalid value … at \"input.bin\"`).\n console.error('[payment.setMethod] paymentMethodSelect failed', loggableErrorName(error));\n return [NETWORK_ERROR];\n }\n },\n [methodSelectMutation],\n );\n\n const setInstallments = useCallback<PaymentActions['setInstallments']>(\n async (count) => {\n try {\n const result = await installmentSelectMutation({ variables: { input: { count } } });\n return result.data?.paymentInstallmentSelect.userErrors ?? [];\n } catch (error) {\n console.error('[payment.setInstallments] paymentInstallmentSelect failed', loggableErrorName(error));\n return [NETWORK_ERROR];\n }\n },\n [installmentSelectMutation],\n );\n\n const setBillingAddress = useCallback<PaymentActions['setBillingAddress']>(\n async (input) => {\n try {\n const result = await billingAddressMutation({ variables: { input } });\n return result.data?.paymentBillingAddressUpdate.userErrors ?? [];\n } catch (error) {\n // Log only the error name — the message can echo the submitted address (PII).\n console.error('[payment.setBillingAddress] paymentBillingAddressUpdate failed', loggableErrorName(error));\n return [NETWORK_ERROR];\n }\n },\n [billingAddressMutation],\n );\n\n const saveCard = useCallback<PaymentActions['saveCard']>(\n async (enabled) => {\n try {\n const result = await preferencesMutation({ variables: { input: { savePersonalData: enabled } } });\n return result.data?.clientPreferencesUpdate?.userErrors ?? [];\n } catch (error) {\n console.error('[payment.saveCard] clientPreferencesUpdate failed', loggableErrorName(error));\n return [NETWORK_ERROR];\n }\n },\n [preferencesMutation],\n );\n\n const startTransaction = useCallback<PaymentActions['startTransaction']>(async () => {\n try {\n const result = await startTransactionMutation();\n const payload = result.data?.paymentStartTransaction;\n return {\n transactionId: payload?.transactionId ?? null,\n merchantName: payload?.merchantName ?? null,\n gatewayUrl: payload?.gatewayUrl ?? null,\n orderGroup: payload?.orderGroup ?? null,\n orderNodeId: payload?.orderNodeId ?? null,\n userProfileId: payload?.userProfileId ?? null,\n value: payload?.value ?? null,\n valueInCents: payload?.valueInCents ?? null,\n referenceValueInCents: payload?.referenceValueInCents ?? null,\n userErrors: payload?.userErrors ?? [],\n };\n } catch (error) {\n console.error('[payment.startTransaction] paymentStartTransaction failed', loggableErrorName(error));\n return { ...EMPTY_START_TRANSACTION, userErrors: [NETWORK_ERROR] };\n }\n }, [startTransactionMutation]);\n\n const gatewayCallback = useCallback<PaymentActions['gatewayCallback']>(\n async (orderGroup, options) => {\n try {\n const input = options?.acceptChallenges === true ? { orderGroup, acceptChallenges: true } : { orderGroup };\n const result = await gatewayCallbackMutation({ variables: { input } });\n const payload = result.data?.paymentGatewayCallback;\n return {\n orderId: payload?.orderId ?? null,\n orderNodeId: payload?.orderNodeId ?? null,\n ...(payload?.redirectUrl ? { redirectUrl: payload.redirectUrl } : {}),\n userErrors: payload?.userErrors ?? [],\n };\n } catch (error) {\n console.error('[payment.gatewayCallback] paymentGatewayCallback failed', loggableErrorName(error));\n return { orderId: null, orderNodeId: null, userErrors: [NETWORK_ERROR] };\n }\n },\n [gatewayCallbackMutation],\n );\n\n const sendPayments = useCallback<PaymentActions['sendPayments']>(async (request) => {\n try {\n return await postPaymentsToGateway(request);\n } catch (error) {\n // Log the error NAME ONLY. The request carries a card number and CVV, and a thrown fetch error's\n // message can echo the URL or the body — nothing on this path is ever serialized to a log.\n console.error('[payment.sendPayments] gateway request failed', loggableErrorName(error));\n // A rejected request is unclassified — it may have been processed — so it counts as reached.\n return { reachedGateway: true, userErrors: [PAYMENT_GATEWAY_USER_ERROR] };\n }\n }, []);\n\n return useMemo(\n () => ({\n setMethod,\n setInstallments,\n setBillingAddress,\n saveCard,\n startTransaction,\n gatewayCallback,\n sendPayments,\n }),\n [setMethod, setInstallments, setBillingAddress, saveCard, startTransaction, gatewayCallback, sendPayments],\n );\n}\n\n/**\n * Declares the `payment` action namespace — the single source of truth for its method/payload metadata,\n * runtime provider, and `use` hook (mirrors `cartActions`, `shippingActions`). The native catalog\n * builder (`scripts/build-catalog.mjs`) statically reads this call to emit `actions.json`; at runtime\n * `PaymentActionProvider` calls `usePaymentActions` directly.\n *\n * `sendPayments`'s `payload` lists only the non-sensitive transaction descriptor's identifiers.\n * `ActionPayloadField` is the surface a schema-bindable name would come from, so the card fields'\n * absence here is the mechanical form of RFC D13: card number and CVV are never expressed as a declared\n * action payload. The `SendPaymentsRequest` type in `gateway.ts` is the source of truth for the real\n * call shape.\n */\nexport const paymentActions = defineActions({\n namespace: 'payment',\n description:\n 'Mutation-backed payment writes against the VTEX orderForm paymentData/invoiceData, plus a façade that posts card data directly to the payment gateway (never through our GraphQL layer or cache).',\n provider: { name: 'PaymentActionProvider', importPath: '@vtex/faststore/internal/client' },\n methods: {\n setMethod: {\n description:\n 'Select a payment method, optionally identifying a new card by BIN or a saved card by account (async/pessimistic). Resolves the payload userErrors.',\n payload: [\n { name: 'methodId', type: 'string' },\n { name: 'bin', type: 'string', optional: true },\n { name: 'accountId', type: 'string', optional: true },\n ],\n },\n setInstallments: {\n description:\n 'Select an installment count for the current method (async/pessimistic). The response carries the cart, whose total is the greater of the order form value and the selected payment value, so it includes the interest of the picked tier. Resolves the payload userErrors.',\n payload: [{ name: 'count', type: 'number' }],\n },\n setBillingAddress: {\n description:\n 'Persist the billing address, or fall back to the delivery address with sameAsDelivery (async/pessimistic). Resolves the payload userErrors.',\n payload: [\n { name: 'sameAsDelivery', type: 'boolean' },\n { name: 'addressType', type: 'string', optional: true },\n { name: 'postalCode', type: 'string', optional: true },\n { name: 'country', type: 'string', optional: true },\n { name: 'street', type: 'string', optional: true },\n { name: 'number', type: 'string', optional: true },\n { name: 'complement', type: 'string', optional: true },\n { name: 'neighborhood', type: 'string', optional: true },\n { name: 'city', type: 'string', optional: true },\n { name: 'state', type: 'string', optional: true },\n { name: 'reference', type: 'string', optional: true },\n ],\n },\n saveCard: {\n description:\n 'Record the data-storage consent a saved card requires, reusing the existing clientPreferencesUpdate write. Does not itself save the card — the save flag and label travel on sendPayments.',\n payload: [{ name: 'enabled', type: 'boolean' }],\n },\n startTransaction: {\n description:\n 'Open a VTEX transaction from the persisted payment selection. Takes no arguments; resolves the transaction identifiers the gateway call needs.',\n payload: [],\n },\n gatewayCallback: {\n description: 'Finalize the transaction after the gateway accepted the card. Resolves the payload userErrors.',\n payload: [\n { name: 'orderGroup', type: 'string' },\n { name: 'acceptChallenges', type: 'boolean', optional: true },\n ],\n },\n sendPayments: {\n description:\n 'Façade: post the card directly from the browser to the payment gateway. Issues no GraphQL operation and writes nothing to the cache. Resolves { reachedGateway, userErrors }: reachedGateway is false only when no request was issued. The card number and CVV are runtime-only arguments and are deliberately absent from this payload — they are never a bindable field (RFC D13).',\n payload: [\n { name: 'gatewayUrl', type: 'string' },\n { name: 'transactionId', type: 'string' },\n { name: 'merchantName', type: 'string' },\n { name: 'orderGroup', type: 'string' },\n { name: 'userProfileId', type: 'string' },\n ],\n },\n },\n use: usePaymentActions,\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-payment-actions.js","names":["INVALID_BIN_MESSAGE"],"sources":["../../../src/sdk/payment/use-payment-actions.ts"],"sourcesContent":["'use client';\n\nimport { useMutation } from '@apollo/client/react';\nimport { defineActions } from '@vtex/fs-core';\nimport { INVALID_BIN_MESSAGE } from '@vtex/fs-domain-logic/checkout/rules';\nimport { useCallback, useMemo } from 'react';\nimport { loggableErrorName } from '../../utils/loggable-error-name.js';\nimport { CLIENT_PREFERENCES_UPDATE } from '../identity/operations.js';\nimport { isAcceptedBin } from './bin-format.js';\nimport {\n PAYMENT_GATEWAY_USER_ERROR,\n type PaymentGatewayResult,\n postPaymentsToGateway,\n type SendPaymentsRequest,\n} from './gateway.js';\nimport {\n PAYMENT_BILLING_ADDRESS_UPDATE,\n PAYMENT_GATEWAY_CALLBACK,\n PAYMENT_INSTALLMENT_SELECT,\n PAYMENT_METHOD_SELECT,\n PAYMENT_NETWORK_ERROR_CODE,\n PAYMENT_START_TRANSACTION,\n type PaymentBillingAddressUpdateInput,\n type PaymentMethodSelectInput,\n type PaymentMoney,\n type PaymentUserError,\n} from './operations.js';\n\n/**\n * The `payment` action namespace — the sanctioned client write surface for credit-card payment\n * (specs/payment-action-namespace.md; docs/features/credit-card/rfc.md § D12;\n * docs/storefront-core/adr-0010-actions-own-the-write-side.md).\n *\n * Six mutation-backed methods run the write model T3 shipped (`checkout/schema.graphql` § 11), except\n * `saveCard`, which reuses the already-shipped `clientPreferencesUpdate` document this package owns —\n * there is no `paymentSaveCard` mutation and T3 decided there must not be one. All six are\n * async/pessimistic: the handler awaits the mutation and only the server-confirmed payload changes the\n * cache. Each is stateless (no reactive vars, no module state), resolves its payload `userErrors` (empty\n * on success), and never throws for an expected outcome; an infrastructure failure (transport or an\n * upstream GraphQLError) is caught and collapsed to one synthetic `NETWORK_ERROR` userError with no\n * per-code branching, mirroring `useShippingActions`.\n *\n * `sendPayments` is the seventh method and the odd one: a **façade** (ADR-0010's sanctioned flavor,\n * alongside `notifications`) that posts card data from the browser straight to the payment gateway,\n * touching neither our GraphQL layer nor the normalized cache. It is catalogued like any other method —\n * per D12, hiding a write route with no read-back would cost discoverability without protecting\n * anything, and the compiler wants to *see* which components reference it. What is deliberately absent\n * from the catalog is the card data itself: no `payload` entry below names a card number, CVV, expiry,\n * or holder name, so there is no schema-bindable name for the editor or the agent to map a value onto\n * (RFC D13). The card reaches the handler as a runtime argument from the card-entry component's local\n * state (T7), and `gateway.ts` — which has no Apollo import — is the only module that touches it.\n */\n\n/**\n * Everything `usePlaceOrder` (T13) needs to assemble `PaymentGatewayTransaction`, minus the five\n * fields that come from the `Payment` read model. Every field is nullable because a payload carrying\n * `userErrors` nulls all of them (T18 Decision 4), so a complete descriptor is only guaranteed once\n * `userErrors` is empty *and* the payload passes the caller's completeness check.\n */\nexport type PaymentStartTransactionResult = Readonly<{\n transactionId: string | null;\n merchantName: string | null;\n /** Gateway origin. Nullable, and never interpolated into a URL before `isTrustedGatewayOrigin`. */\n gatewayUrl: string | null;\n orderGroup: string | null;\n /**\n * The Relay Node ID of the order, for `Query.order(id:)`. Null when the transaction carried no order\n * group. Optional so a provider written before this field existed still satisfies the type.\n */\n orderNodeId?: string | null;\n userProfileId: string | null;\n /** Display decimal. The gateway body's unit is `valueInCents`. */\n value: PaymentMoney | null;\n valueInCents: number | null;\n referenceValueInCents: number | null;\n userErrors: readonly PaymentUserError[];\n}>;\n\nexport type PaymentGatewayCallbackResult = Readonly<{\n orderId: string | null;\n /** The Relay Node ID of the finalized order, for `Query.order(id:)`. Null unless the callback succeeded. */\n orderNodeId?: string | null;\n /** The bank's challenge page. Set only when the server answered a challenge with a usable URL. */\n redirectUrl?: string | null;\n userErrors: readonly PaymentUserError[];\n}>;\n\nexport type PaymentActions = Readonly<{\n /** Select a payment method, optionally identifying a new card's BIN or a saved card's account. */\n setMethod: (input: PaymentMethodSelectInput) => Promise<readonly PaymentUserError[]>;\n /** Select an installment count for the current method. The response's `cart.total` is the greater of the order form's value and the selected payment's value. */\n setInstallments: (count: number) => Promise<readonly PaymentUserError[]>;\n /** Persist the billing address, or fall back to the delivery address (`sameAsDelivery: true`). */\n setBillingAddress: (input: PaymentBillingAddressUpdateInput) => Promise<readonly PaymentUserError[]>;\n /**\n * Record the data-storage consent a saved card requires, over the existing `clientPreferencesUpdate`.\n * This does not by itself save the card: the save flag and label travel on `sendPayments`'s card\n * argument (RFC D5's two halves). The boolean passes through unchanged — whether unchecking should\n * revoke a consent an unrelated opt-in component may have granted is T10's decision, and its spec must\n * state one (Gap 4 in specs/payment-action-namespace.md).\n */\n saveCard: (enabled: boolean) => Promise<readonly PaymentUserError[]>;\n /** Open a VTEX transaction from the persisted selection. Resolves the payload the gateway call needs. */\n startTransaction: () => Promise<PaymentStartTransactionResult>;\n /**\n * Finalize the transaction after the gateway accepted the card. `acceptChallenges: true` is sent only by\n * the page the bank returns the shopper to, and only then; omitted, the server asks for a challenge\n * instead of finishing the order.\n */\n gatewayCallback: (\n orderGroup: string,\n options?: Readonly<{ acceptChallenges?: boolean }>,\n ) => Promise<PaymentGatewayCallbackResult>;\n /**\n * Façade: post the card straight to the payment gateway. Issues no GraphQL operation and writes\n * nothing to the cache. Never rejects: resolves `{ reachedGateway, userErrors }`, with one `GATEWAY_ERROR`\n * userError when the gateway rejects the call or it could not be issued. `reachedGateway` is `false`\n * only when no request was issued; every outcome it cannot classify is `true`.\n */\n sendPayments: (request: SendPaymentsRequest) => Promise<PaymentGatewayResult>;\n}>;\n\nconst INVALID_BIN_ERROR: PaymentUserError = { code: 'INVALID_BIN_FORMAT', message: INVALID_BIN_MESSAGE };\n\n// Diagnostic `message` (logs, `onResult`); each reader words `NETWORK_ERROR` from its own content.\nconst NETWORK_ERROR: PaymentUserError = {\n code: PAYMENT_NETWORK_ERROR_CODE,\n message: 'Unable to complete the request. Please try again.',\n};\n\n/** Mirrors the resolver's own nulled-fields constant, so one added field is nulled in one place. */\nconst EMPTY_START_TRANSACTION = {\n transactionId: null,\n merchantName: null,\n gatewayUrl: null,\n orderGroup: null,\n orderNodeId: null,\n userProfileId: null,\n value: null,\n valueInCents: null,\n referenceValueInCents: null,\n} as const;\n\n/**\n * The `payment` namespace hook. Registered with the first-party action registry via `paymentActions`\n * below; the compiler-emitted `PaymentActionProvider` calls it and threads the narrowed methods into\n * each consumer's `$actions.payment`. Writes persist purely through the normalized-cache merge of each\n * mutation response — no refetch, no imperative cache write.\n */\nexport function usePaymentActions(): PaymentActions {\n const [methodSelectMutation] = useMutation(PAYMENT_METHOD_SELECT);\n const [installmentSelectMutation] = useMutation(PAYMENT_INSTALLMENT_SELECT);\n const [billingAddressMutation] = useMutation(PAYMENT_BILLING_ADDRESS_UPDATE);\n const [preferencesMutation] = useMutation(CLIENT_PREFERENCES_UPDATE);\n const [startTransactionMutation] = useMutation(PAYMENT_START_TRANSACTION);\n const [gatewayCallbackMutation] = useMutation(PAYMENT_GATEWAY_CALLBACK);\n\n const setMethod = useCallback<PaymentActions['setMethod']>(\n async (input) => {\n // Refused here, before the value can reach the network, the GraphQL variables or the Apollo cache.\n // Logs nothing: the input carries the value being refused.\n if (!isAcceptedBin(input)) return [INVALID_BIN_ERROR];\n try {\n const result = await methodSelectMutation({ variables: { input } });\n return result.data?.paymentMethodSelect.userErrors ?? [];\n } catch (error) {\n // Log only the error name — the input carries the card's BIN, and a variable-coercion error's\n // message echoes the offending value (`got invalid value … at \"input.bin\"`).\n console.error('[payment.setMethod] paymentMethodSelect failed', loggableErrorName(error));\n return [NETWORK_ERROR];\n }\n },\n [methodSelectMutation],\n );\n\n const setInstallments = useCallback<PaymentActions['setInstallments']>(\n async (count) => {\n try {\n const result = await installmentSelectMutation({ variables: { input: { count } } });\n return result.data?.paymentInstallmentSelect.userErrors ?? [];\n } catch (error) {\n console.error('[payment.setInstallments] paymentInstallmentSelect failed', loggableErrorName(error));\n return [NETWORK_ERROR];\n }\n },\n [installmentSelectMutation],\n );\n\n const setBillingAddress = useCallback<PaymentActions['setBillingAddress']>(\n async (input) => {\n try {\n const result = await billingAddressMutation({ variables: { input } });\n return result.data?.paymentBillingAddressUpdate.userErrors ?? [];\n } catch (error) {\n // Log only the error name — the message can echo the submitted address (PII).\n console.error('[payment.setBillingAddress] paymentBillingAddressUpdate failed', loggableErrorName(error));\n return [NETWORK_ERROR];\n }\n },\n [billingAddressMutation],\n );\n\n const saveCard = useCallback<PaymentActions['saveCard']>(\n async (enabled) => {\n try {\n const result = await preferencesMutation({ variables: { input: { savePersonalData: enabled } } });\n return result.data?.clientPreferencesUpdate?.userErrors ?? [];\n } catch (error) {\n console.error('[payment.saveCard] clientPreferencesUpdate failed', loggableErrorName(error));\n return [NETWORK_ERROR];\n }\n },\n [preferencesMutation],\n );\n\n const startTransaction = useCallback<PaymentActions['startTransaction']>(async () => {\n try {\n const result = await startTransactionMutation();\n const payload = result.data?.paymentStartTransaction;\n return {\n transactionId: payload?.transactionId ?? null,\n merchantName: payload?.merchantName ?? null,\n gatewayUrl: payload?.gatewayUrl ?? null,\n orderGroup: payload?.orderGroup ?? null,\n orderNodeId: payload?.orderNodeId ?? null,\n userProfileId: payload?.userProfileId ?? null,\n value: payload?.value ?? null,\n valueInCents: payload?.valueInCents ?? null,\n referenceValueInCents: payload?.referenceValueInCents ?? null,\n userErrors: payload?.userErrors ?? [],\n };\n } catch (error) {\n console.error('[payment.startTransaction] paymentStartTransaction failed', loggableErrorName(error));\n return { ...EMPTY_START_TRANSACTION, userErrors: [NETWORK_ERROR] };\n }\n }, [startTransactionMutation]);\n\n const gatewayCallback = useCallback<PaymentActions['gatewayCallback']>(\n async (orderGroup, options) => {\n try {\n const input = options?.acceptChallenges === true ? { orderGroup, acceptChallenges: true } : { orderGroup };\n const result = await gatewayCallbackMutation({ variables: { input } });\n const payload = result.data?.paymentGatewayCallback;\n return {\n orderId: payload?.orderId ?? null,\n orderNodeId: payload?.orderNodeId ?? null,\n ...(payload?.redirectUrl ? { redirectUrl: payload.redirectUrl } : {}),\n userErrors: payload?.userErrors ?? [],\n };\n } catch (error) {\n console.error('[payment.gatewayCallback] paymentGatewayCallback failed', loggableErrorName(error));\n return { orderId: null, orderNodeId: null, userErrors: [NETWORK_ERROR] };\n }\n },\n [gatewayCallbackMutation],\n );\n\n const sendPayments = useCallback<PaymentActions['sendPayments']>(async (request) => {\n try {\n return await postPaymentsToGateway(request);\n } catch (error) {\n // Log the error NAME ONLY. The request carries a card number and CVV, and a thrown fetch error's\n // message can echo the URL or the body — nothing on this path is ever serialized to a log.\n console.error('[payment.sendPayments] gateway request failed', loggableErrorName(error));\n // A rejected request is unclassified — it may have been processed — so it counts as reached.\n return { reachedGateway: true, userErrors: [PAYMENT_GATEWAY_USER_ERROR] };\n }\n }, []);\n\n return useMemo(\n () => ({\n setMethod,\n setInstallments,\n setBillingAddress,\n saveCard,\n startTransaction,\n gatewayCallback,\n sendPayments,\n }),\n [setMethod, setInstallments, setBillingAddress, saveCard, startTransaction, gatewayCallback, sendPayments],\n );\n}\n\n/**\n * Declares the `payment` action namespace — the single source of truth for its method/payload metadata,\n * runtime provider, and `use` hook (mirrors `cartActions`, `shippingActions`). The native catalog\n * builder (`scripts/build-catalog.mjs`) statically reads this call to emit `actions.json`; at runtime\n * `PaymentActionProvider` calls `usePaymentActions` directly.\n *\n * `sendPayments`'s `payload` lists only the non-sensitive transaction descriptor's identifiers.\n * `ActionPayloadField` is the surface a schema-bindable name would come from, so the card fields'\n * absence here is the mechanical form of RFC D13: card number and CVV are never expressed as a declared\n * action payload. The `SendPaymentsRequest` type in `gateway.ts` is the source of truth for the real\n * call shape.\n */\nexport const paymentActions = defineActions({\n namespace: 'payment',\n description:\n 'Mutation-backed payment writes against the VTEX orderForm paymentData/invoiceData, plus a façade that posts card data directly to the payment gateway (never through our GraphQL layer or cache).',\n provider: { name: 'PaymentActionProvider', importPath: '@vtex/faststore/internal/client' },\n methods: {\n setMethod: {\n description:\n 'Select a payment method, optionally identifying a new card by BIN or a saved card by account (async/pessimistic). Resolves the payload userErrors.',\n payload: [\n { name: 'methodId', type: 'string' },\n { name: 'bin', type: 'string', optional: true },\n { name: 'accountId', type: 'string', optional: true },\n ],\n },\n setInstallments: {\n description:\n 'Select an installment count for the current method (async/pessimistic). The response carries the cart, whose total is the greater of the order form value and the selected payment value, so it includes the interest of the picked tier. Resolves the payload userErrors.',\n payload: [{ name: 'count', type: 'number' }],\n },\n setBillingAddress: {\n description:\n 'Persist the billing address, or fall back to the delivery address with sameAsDelivery (async/pessimistic). Resolves the payload userErrors.',\n payload: [\n { name: 'sameAsDelivery', type: 'boolean' },\n { name: 'addressType', type: 'string', optional: true },\n { name: 'postalCode', type: 'string', optional: true },\n { name: 'country', type: 'string', optional: true },\n { name: 'street', type: 'string', optional: true },\n { name: 'number', type: 'string', optional: true },\n { name: 'complement', type: 'string', optional: true },\n { name: 'neighborhood', type: 'string', optional: true },\n { name: 'city', type: 'string', optional: true },\n { name: 'state', type: 'string', optional: true },\n { name: 'reference', type: 'string', optional: true },\n ],\n },\n saveCard: {\n description:\n 'Record the data-storage consent a saved card requires, reusing the existing clientPreferencesUpdate write. Does not itself save the card — the save flag and label travel on sendPayments.',\n payload: [{ name: 'enabled', type: 'boolean' }],\n },\n startTransaction: {\n description:\n 'Open a VTEX transaction from the persisted payment selection. Takes no arguments; resolves the transaction identifiers the gateway call needs.',\n payload: [],\n },\n gatewayCallback: {\n description: 'Finalize the transaction after the gateway accepted the card. Resolves the payload userErrors.',\n payload: [\n { name: 'orderGroup', type: 'string' },\n { name: 'acceptChallenges', type: 'boolean', optional: true },\n ],\n },\n sendPayments: {\n description:\n 'Façade: post the card directly from the browser to the payment gateway. Issues no GraphQL operation and writes nothing to the cache. Resolves { reachedGateway, userErrors }: reachedGateway is false only when no request was issued. The card number and CVV are runtime-only arguments and are deliberately absent from this payload — they are never a bindable field (RFC D13).',\n payload: [\n { name: 'gatewayUrl', type: 'string' },\n { name: 'transactionId', type: 'string' },\n { name: 'merchantName', type: 'string' },\n { name: 'orderGroup', type: 'string' },\n { name: 'userProfileId', type: 'string' },\n ],\n },\n },\n use: usePaymentActions,\n});\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtex/faststore",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"description": "FastStore AI Native component library built on Base UI with AI-editable styles",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://developers.vtex.com/docs/guides/faststore",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@formatjs/icu-messageformat-parser": "^3.5.17",
|
|
59
59
|
"@graphql-typed-document-node/core": "^3.2.0",
|
|
60
60
|
"@vtex/country-forms": "1.14.0",
|
|
61
|
-
"@vtex/faststore-catalog": "0.0.
|
|
61
|
+
"@vtex/faststore-catalog": "0.0.12",
|
|
62
62
|
"esbuild": "~0.27.3",
|
|
63
63
|
"graphql": "^16.13.1",
|
|
64
64
|
"intl-messageformat": "^11.2.14",
|