@usethrottle/payment-methods 0.1.10 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/{chunk-3MLDHCKP.js → chunk-U5VCJZPP.js} +1 -1
- package/dist/chunk-U5VCJZPP.js.map +1 -0
- package/dist/index.cjs +34 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +35 -7
- package/dist/index.js.map +1 -1
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -1
- package/dist/server.d.ts +2 -1
- package/dist/server.js +1 -1
- package/dist/{types-DTnYe0Y8.d.cts → types-CuRlkq4t.d.cts} +4 -1
- package/dist/{types-DTnYe0Y8.d.ts → types-CuRlkq4t.d.ts} +4 -1
- package/package.json +3 -2
- package/dist/chunk-3MLDHCKP.js.map +0 -1
package/README.md
CHANGED
|
@@ -243,6 +243,12 @@ Common error codes:
|
|
|
243
243
|
|
|
244
244
|
---
|
|
245
245
|
|
|
246
|
+
## Money
|
|
247
|
+
|
|
248
|
+
This package re-exports `@usethrottle/money` as `money` and `formatMoney` by
|
|
249
|
+
name, plus the `CurrencyCode` and `MoneyValue` types. Format any amount with
|
|
250
|
+
`formatMoney(amount, currency)` using the record's own currency.
|
|
251
|
+
|
|
246
252
|
## `SavedCard` shape
|
|
247
253
|
|
|
248
254
|
```ts
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/types.ts","../src/request.ts"],"sourcesContent":["import { ThrottleError, type ThrottleErrorArgs } from '@usethrottle/errors';\n\nexport class ThrottlePaymentMethodsError extends ThrottleError {\n constructor(args: {\n code: string;\n message: string;\n statusCode: number;\n details?: unknown;\n responseHeaders?: ThrottleErrorArgs['responseHeaders'];\n }) {\n super(args);\n this.name = 'ThrottlePaymentMethodsError';\n }\n}\n\nexport { ThrottleError, isThrottleError } from '@usethrottle/errors';\n","import type { CurrencyCode } from '@usethrottle/money';\n\nexport interface SavedCard {\n id: string;\n methodType: string;\n cardBrand: string | null;\n cardLastFour: string | null;\n cardExpMonth: number | null;\n cardExpYear: number | null;\n isDefault: boolean;\n}\n\nexport interface EmbedPayload {\n embedToken: string;\n checkoutSessionId: string;\n amount: number;\n /** The verification hold's currency. */\n currency: CurrencyCode;\n processorInstanceId: string;\n merchantAccountId?: string;\n environment: 'sandbox' | 'production';\n /**\n * The buyer the embed JWT is pinned to. The add-only vault embed is a\n * recurring/store flow, so Gr4vy asserts the `<Gr4vyEmbed buyerExternalIdentifier>`\n * prop equals the JWT's value — the component MUST forward this or the embed\n * renders no card fields. Always present on the add-only embed payload.\n */\n buyerExternalIdentifier?: string;\n /** ISO country code the embed session was minted for. The Gr4vy embed requires\n * a `country` string at init (\"undefined must be a string ISO country code\").\n * Defaults to 'US' in the component when absent. */\n country?: string;\n hostedUrl?: string;\n embedUrl?: string;\n}\n\nexport interface MintedClientToken {\n token: string;\n expiresAt: string;\n}\n\nexport interface PaymentMethodsTheme {\n colorPrimary?: string;\n colorText?: string;\n colorBorder?: string;\n borderRadius?: string;\n}\n\nexport const DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n","import { ThrottlePaymentMethodsError } from './errors';\n\nexport async function request(input: {\n method: string;\n path: string;\n baseUrl: string;\n headers?: Record<string, string>;\n body?: unknown;\n}): Promise<any> {\n const res = await fetch(`${input.baseUrl}${input.path}`, {\n method: input.method,\n headers: {\n ...(input.body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(input.headers ?? {}),\n },\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body (e.g. an HTML 502/503 gateway page). Leave parsed as {} so\n // the non-2xx branch below still throws a typed ThrottlePaymentMethodsError\n // (code 'request_failed') rather than leaking a raw SyntaxError.\n }\n if (!res.ok) {\n const e = parsed?.error ?? {};\n throw new ThrottlePaymentMethodsError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n details: e.details,\n responseHeaders: res.headers,\n });\n }\n return parsed;\n}\n"],"mappings":";AAAA,SAAS,qBAA6C;AAetD,SAAS,iBAAAA,gBAAe,uBAAuB;AAbxC,IAAM,8BAAN,cAA0C,cAAc;AAAA,EAC7D,YAAY,MAMT;AACD,UAAM,IAAI;AACV,SAAK,OAAO;AAAA,EACd;AACF;;;ACmCO,IAAM,mBAAmB;;;AC9ChC,eAAsB,QAAQ,OAMb;AACf,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,GAAG,MAAM,IAAI,IAAI;AAAA,IACvD,QAAQ,MAAM;AAAA,IACd,SAAS;AAAA,MACP,GAAI,MAAM,SAAS,SAAY,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,MACzE,GAAI,MAAM,WAAW,CAAC;AAAA,IACxB;AAAA,IACA,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,KAAK,UAAU,MAAM,IAAI,EAAE,IAAI,CAAC;AAAA,EACzE,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,SAAc,CAAC;AACnB,MAAI;AACF,aAAS,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC;AAAA,EACtC,QAAQ;AAAA,EAIR;AACA,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,QAAQ,SAAS,CAAC;AAC5B,UAAM,IAAI,4BAA4B;AAAA,MACpC,MAAM,EAAE,QAAQ;AAAA,MAChB,SAAS,EAAE,WAAW,uBAAuB,IAAI,MAAM;AAAA,MACvD,YAAY,IAAI;AAAA,MAChB,SAAS,EAAE;AAAA,MACX,iBAAiB,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AACA,SAAO;AACT;","names":["ThrottleError"]}
|
package/dist/index.cjs
CHANGED
|
@@ -163,12 +163,23 @@ var init_Gr4vyAddCard = __esm({
|
|
|
163
163
|
// src/index.tsx
|
|
164
164
|
var index_exports = {};
|
|
165
165
|
__export(index_exports, {
|
|
166
|
+
CURRENCIES: () => import_money.CURRENCIES,
|
|
166
167
|
DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
|
|
167
168
|
PaymentMethods: () => PaymentMethods,
|
|
168
169
|
ThrottleError: () => import_errors2.ThrottleError,
|
|
169
170
|
ThrottlePaymentMethodsError: () => ThrottlePaymentMethodsError,
|
|
171
|
+
UnknownCurrencyError: () => import_money.UnknownCurrencyError,
|
|
170
172
|
createPaymentMethods: () => createPaymentMethods,
|
|
173
|
+
currencySymbol: () => import_money.currencySymbol,
|
|
174
|
+
formatMoney: () => import_money.formatMoney,
|
|
175
|
+
isKnownCurrency: () => import_money.isKnownCurrency,
|
|
171
176
|
isThrottleError: () => import_errors2.isThrottleError,
|
|
177
|
+
minorUnitsOf: () => import_money.minorUnitsOf,
|
|
178
|
+
money: () => money,
|
|
179
|
+
moneyValue: () => import_money.moneyValue,
|
|
180
|
+
parseMoney: () => import_money.parseMoney,
|
|
181
|
+
toDecimalString: () => import_money.toDecimalString,
|
|
182
|
+
toMinor: () => import_money.toMinor,
|
|
172
183
|
usePaymentMethods: () => usePaymentMethods
|
|
173
184
|
});
|
|
174
185
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -243,12 +254,13 @@ function createPaymentMethods(opts) {
|
|
|
243
254
|
});
|
|
244
255
|
return parsed.data;
|
|
245
256
|
},
|
|
246
|
-
async startAddCard() {
|
|
257
|
+
async startAddCard(startOpts) {
|
|
247
258
|
const parsed = await request({
|
|
248
259
|
method: "POST",
|
|
249
260
|
path: "/api/v1/me/payment-methods/embed-token",
|
|
250
261
|
baseUrl,
|
|
251
|
-
headers
|
|
262
|
+
headers,
|
|
263
|
+
...startOpts?.currency ? { body: { currency: startOpts.currency } } : {}
|
|
252
264
|
});
|
|
253
265
|
return parsed.data;
|
|
254
266
|
},
|
|
@@ -273,7 +285,7 @@ function toPaymentMethodsError(err) {
|
|
|
273
285
|
return new ThrottlePaymentMethodsError({ code: "unknown_error", message, statusCode: 0 });
|
|
274
286
|
}
|
|
275
287
|
function usePaymentMethods(opts) {
|
|
276
|
-
const { clientToken, baseUrl } = opts;
|
|
288
|
+
const { clientToken, baseUrl, currency } = opts;
|
|
277
289
|
const client = (0, import_react.useMemo)(
|
|
278
290
|
() => createPaymentMethods({ clientToken, baseUrl }),
|
|
279
291
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -338,7 +350,7 @@ function usePaymentMethods(opts) {
|
|
|
338
350
|
}
|
|
339
351
|
};
|
|
340
352
|
const addCard = async () => {
|
|
341
|
-
return client.startAddCard();
|
|
353
|
+
return currency ? client.startAddCard({ currency }) : client.startAddCard();
|
|
342
354
|
};
|
|
343
355
|
const confirmAddCard = async (transactionId) => {
|
|
344
356
|
try {
|
|
@@ -379,10 +391,11 @@ function toReadableMessage(err) {
|
|
|
379
391
|
if (typeof err === "string" && err) return err;
|
|
380
392
|
return "We couldn't save your card. Please try another payment method.";
|
|
381
393
|
}
|
|
382
|
-
function PaymentMethods({ clientToken, baseUrl, theme, onChange }) {
|
|
394
|
+
function PaymentMethods({ clientToken, baseUrl, theme, onChange, currency }) {
|
|
383
395
|
const { cards, isLoading, error, setDefault, remove, addCard, confirmAddCard, refetch } = usePaymentMethods({
|
|
384
396
|
clientToken,
|
|
385
|
-
baseUrl
|
|
397
|
+
baseUrl,
|
|
398
|
+
currency
|
|
386
399
|
});
|
|
387
400
|
const [busyId, setBusyId] = (0, import_react3.useState)(null);
|
|
388
401
|
const [addCardPayload, setAddCardPayload] = (0, import_react3.useState)(null);
|
|
@@ -566,14 +579,29 @@ function PaymentMethods({ clientToken, baseUrl, theme, onChange }) {
|
|
|
566
579
|
)
|
|
567
580
|
] });
|
|
568
581
|
}
|
|
582
|
+
|
|
583
|
+
// src/index.tsx
|
|
584
|
+
var money = __toESM(require("@usethrottle/money"), 1);
|
|
585
|
+
var import_money = require("@usethrottle/money");
|
|
569
586
|
// Annotate the CommonJS export names for ESM import in node:
|
|
570
587
|
0 && (module.exports = {
|
|
588
|
+
CURRENCIES,
|
|
571
589
|
DEFAULT_BASE_URL,
|
|
572
590
|
PaymentMethods,
|
|
573
591
|
ThrottleError,
|
|
574
592
|
ThrottlePaymentMethodsError,
|
|
593
|
+
UnknownCurrencyError,
|
|
575
594
|
createPaymentMethods,
|
|
595
|
+
currencySymbol,
|
|
596
|
+
formatMoney,
|
|
597
|
+
isKnownCurrency,
|
|
576
598
|
isThrottleError,
|
|
599
|
+
minorUnitsOf,
|
|
600
|
+
money,
|
|
601
|
+
moneyValue,
|
|
602
|
+
parseMoney,
|
|
603
|
+
toDecimalString,
|
|
604
|
+
toMinor,
|
|
577
605
|
usePaymentMethods
|
|
578
606
|
});
|
|
579
607
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Gr4vyAddCard.tsx","../src/index.tsx","../src/errors.ts","../src/types.ts","../src/request.ts","../src/client.ts","../src/usePaymentMethods.ts","../src/PaymentMethods.tsx"],"sourcesContent":["/**\n * Gr4vyAddCard — lazy-loaded vault-only embed wrapper.\n *\n * THIS FILE MUST ONLY BE LOADED VIA React.lazy(() => import('./Gr4vyAddCard'))\n * from PaymentMethods.tsx. Never import it at the module top-level of any file\n * that is part of the server bundle — @gr4vy/embed-react touches `window` at\n * module evaluation time and will throw `ReferenceError: window is not defined`\n * in SSR contexts.\n */\nimport React, { useRef, useState, useEffect } from 'react';\nimport Gr4vyEmbedImport, { type EmbedInstance } from '@gr4vy/embed-react';\nimport type { EmbedPayload, PaymentMethodsTheme } from './types';\n\n/**\n * `@gr4vy/embed-react` is a CommonJS package (no `module`/`exports`/`type`\n * fields), so a consumer's bundler resolves `import Gr4vy from '@gr4vy/embed-react'`\n * differently depending on its CJS↔ESM interop: some hand back the component\n * directly, others hand back the module namespace `{ default: <component> }`.\n * Rendering the namespace object throws \"Element type is invalid … got: object\".\n * Unwrap `.default` until we reach an actual React element type (a function\n * component, or a forwardRef/memo object carrying `$$typeof`).\n */\nexport function resolveComponent(mod: unknown): React.ComponentType<Record<string, unknown>> {\n let x: any = mod;\n while (x && typeof x === 'object' && !x.$$typeof && x.default) x = x.default;\n return x as React.ComponentType<Record<string, unknown>>;\n}\nconst Gr4vyEmbed = resolveComponent(Gr4vyEmbedImport);\n\ninterface Gr4vyTransaction {\n id: string;\n status: string;\n paymentMethod?: { id?: string };\n}\n\n/**\n * Message for a `transactionFailed` event.\n *\n * `transactionFailed` means the transaction WAS created and the processor\n * refused it — so this is never a data-entry problem, and the copy must not\n * tell the buyer to re-check details they typed correctly (a `do_not_honor`\n * decline reaches us with `cvvResponseCode: 'match'`). Prefer the processor's\n * own sentence when it sends one; otherwise say what actually happened and\n * what to do about it. Deliberately carries no decline code — those are\n * processor vocabulary, not buyer-facing.\n */\nexport function declineMessage(data: unknown): string {\n if (data && typeof data === 'object') {\n const message = (data as Record<string, unknown>).message;\n if (typeof message === 'string' && message.trim()) return message.trim();\n }\n return 'Your card was declined by your bank. Try another card, or contact your bank to approve the charge.';\n}\n\nexport interface Gr4vyAddCardProps {\n payload: EmbedPayload;\n theme?: PaymentMethodsTheme;\n /** Fired when the embed completes the authorization + vault. Carries the\n * Gr4vy transaction id (used to record the card server-side) + the vaulted\n * payment-method id. */\n onAdded: (result: { transactionId?: string; paymentMethodId?: string }) => void;\n onCancel: () => void;\n onError: (err: unknown) => void;\n}\n\nexport function Gr4vyAddCard({ payload, theme, onAdded, onCancel, onError }: Gr4vyAddCardProps) {\n const embedRef = useRef<EmbedInstance>(null);\n // The Gr4vy embed loads its secure fields asynchronously (and is sometimes\n // slow). Don't let the buyer click \"Save card\" until the form is ready, or\n // the submit goes out with no card data and Gr4vy returns \"Request failed\n // validation\". `optionsLoaded` is the embed's \"form is ready\" signal.\n const [formReady, setFormReady] = useState(false);\n\n useEffect(() => {\n if (formReady) return;\n const t = setTimeout(() => {\n if (!formReady) {\n onError(\n new Error(\n \"The card form is taking longer than usual to load. Please close this and tap “Add card” again.\",\n ),\n );\n }\n }, 20000);\n return () => clearTimeout(t);\n }, [formReady, onError]);\n\n const colors = theme\n ? {\n ...(theme.colorPrimary !== undefined ? { primary: theme.colorPrimary } : {}),\n ...(theme.colorText !== undefined ? { text: theme.colorText } : {}),\n ...(theme.colorBorder !== undefined ? { inputBorder: theme.colorBorder } : {}),\n }\n : {};\n const gr4vyTheme = Object.keys(colors).length ? { colors } : undefined;\n\n return (\n <div>\n <Gr4vyEmbed\n ref={embedRef}\n gr4vyId={payload.processorInstanceId}\n environment={payload.environment}\n token={payload.embedToken}\n amount={payload.amount}\n currency={payload.currency}\n country={payload.country ?? 'US'}\n merchantAccountId={payload.merchantAccountId}\n intent=\"authorize\"\n paymentSource=\"recurring\"\n store={true}\n display=\"addOnly\"\n {...(payload.buyerExternalIdentifier\n ? { buyerExternalIdentifier: payload.buyerExternalIdentifier }\n : {})}\n {...(gr4vyTheme !== undefined ? { theme: gr4vyTheme } : {})}\n onComplete={(txn: Gr4vyTransaction) => {\n onAdded({ transactionId: txn.id, paymentMethodId: txn.paymentMethod?.id });\n }}\n onEvent={(eventName: string, data: unknown) => {\n // The form's fields are loaded and interactive.\n if (eventName === 'optionsLoaded' || eventName === 'formUpdate') {\n setFormReady(true);\n }\n if (eventName === 'transactionFailed') {\n onError(new Error(declineMessage(data)));\n }\n }}\n />\n\n <button\n type=\"button\"\n disabled={!formReady}\n onClick={() => {\n if (!formReady) return;\n embedRef.current?.submit();\n }}\n style={{\n marginTop: '12px',\n padding: '8px 16px',\n backgroundColor: formReady ? theme?.colorPrimary ?? '#1D56E8' : '#9CA3AF',\n color: '#fff',\n border: 'none',\n borderRadius: '8px',\n fontSize: '14px',\n fontWeight: 600,\n cursor: formReady ? 'pointer' : 'not-allowed',\n width: '100%',\n }}\n >\n {formReady ? 'Save card' : 'Loading card form…'}\n </button>\n\n <button\n type=\"button\"\n onClick={onCancel}\n style={{\n marginTop: '8px',\n padding: '8px 16px',\n backgroundColor: 'transparent',\n color: theme?.colorText ?? '#111827',\n border: `1px solid ${theme?.colorBorder ?? '#E5E7EB'}`,\n borderRadius: '8px',\n fontSize: '14px',\n cursor: 'pointer',\n width: '100%',\n }}\n >\n Cancel\n </button>\n </div>\n );\n}\n\nexport default Gr4vyAddCard;\n","export { ThrottlePaymentMethodsError, ThrottleError, isThrottleError } from './errors';\nexport type { SavedCard, EmbedPayload, MintedClientToken, PaymentMethodsTheme } from './types';\nexport { DEFAULT_BASE_URL } from './types';\nexport { createPaymentMethods } from './client';\nexport type { PaymentMethodsClient } from './client';\nexport { usePaymentMethods } from './usePaymentMethods';\nexport type { UsePaymentMethodsOpts, UsePaymentMethodsResult } from './usePaymentMethods';\nexport { PaymentMethods } from './PaymentMethods';\nexport type { PaymentMethodsProps } from './PaymentMethods';\n","import { ThrottleError, type ThrottleErrorArgs } from '@usethrottle/errors';\n\nexport class ThrottlePaymentMethodsError extends ThrottleError {\n constructor(args: {\n code: string;\n message: string;\n statusCode: number;\n details?: unknown;\n responseHeaders?: ThrottleErrorArgs['responseHeaders'];\n }) {\n super(args);\n this.name = 'ThrottlePaymentMethodsError';\n }\n}\n\nexport { ThrottleError, isThrottleError } from '@usethrottle/errors';\n","export interface SavedCard {\n id: string;\n methodType: string;\n cardBrand: string | null;\n cardLastFour: string | null;\n cardExpMonth: number | null;\n cardExpYear: number | null;\n isDefault: boolean;\n}\n\nexport interface EmbedPayload {\n embedToken: string;\n checkoutSessionId: string;\n amount: number;\n currency: string;\n processorInstanceId: string;\n merchantAccountId?: string;\n environment: 'sandbox' | 'production';\n /**\n * The buyer the embed JWT is pinned to. The add-only vault embed is a\n * recurring/store flow, so Gr4vy asserts the `<Gr4vyEmbed buyerExternalIdentifier>`\n * prop equals the JWT's value — the component MUST forward this or the embed\n * renders no card fields. Always present on the add-only embed payload.\n */\n buyerExternalIdentifier?: string;\n /** ISO country code the embed session was minted for. The Gr4vy embed requires\n * a `country` string at init (\"undefined must be a string ISO country code\").\n * Defaults to 'US' in the component when absent. */\n country?: string;\n hostedUrl?: string;\n embedUrl?: string;\n}\n\nexport interface MintedClientToken {\n token: string;\n expiresAt: string;\n}\n\nexport interface PaymentMethodsTheme {\n colorPrimary?: string;\n colorText?: string;\n colorBorder?: string;\n borderRadius?: string;\n}\n\nexport const DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n","import { ThrottlePaymentMethodsError } from './errors';\n\nexport async function request(input: {\n method: string;\n path: string;\n baseUrl: string;\n headers?: Record<string, string>;\n body?: unknown;\n}): Promise<any> {\n const res = await fetch(`${input.baseUrl}${input.path}`, {\n method: input.method,\n headers: {\n ...(input.body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(input.headers ?? {}),\n },\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body (e.g. an HTML 502/503 gateway page). Leave parsed as {} so\n // the non-2xx branch below still throws a typed ThrottlePaymentMethodsError\n // (code 'request_failed') rather than leaking a raw SyntaxError.\n }\n if (!res.ok) {\n const e = parsed?.error ?? {};\n throw new ThrottlePaymentMethodsError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n details: e.details,\n responseHeaders: res.headers,\n });\n }\n return parsed;\n}\n","import { request } from './request';\nimport { DEFAULT_BASE_URL } from './types';\nimport type { SavedCard, EmbedPayload } from './types';\n\nexport interface PaymentMethodsClient {\n list(): Promise<SavedCard[]>;\n setDefault(id: string): Promise<SavedCard>;\n remove(id: string): Promise<{ id: string }>;\n startAddCard(): Promise<EmbedPayload>;\n /** Record a card vaulted via the add-card embed, given the embed's transaction id. */\n confirmAddCard(transactionId: string): Promise<SavedCard>;\n}\n\nexport function createPaymentMethods(opts: {\n clientToken: string;\n baseUrl?: string;\n}): PaymentMethodsClient {\n const baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;\n const headers = { 'X-Throttle-PM-Token': opts.clientToken };\n\n return {\n async list() {\n const parsed = await request({ method: 'GET', path: '/api/v1/me/payment-methods', baseUrl, headers });\n return parsed.data as SavedCard[];\n },\n\n async setDefault(id: string) {\n const parsed = await request({\n method: 'PATCH',\n path: `/api/v1/me/payment-methods/${encodeURIComponent(id)}`,\n baseUrl,\n headers,\n body: { isDefault: true },\n });\n return parsed.data as SavedCard;\n },\n\n async remove(id: string) {\n const parsed = await request({\n method: 'DELETE',\n path: `/api/v1/me/payment-methods/${encodeURIComponent(id)}`,\n baseUrl,\n headers,\n });\n return parsed.data as { id: string };\n },\n\n async startAddCard() {\n const parsed = await request({\n method: 'POST',\n path: '/api/v1/me/payment-methods/embed-token',\n baseUrl,\n headers,\n });\n return parsed.data as EmbedPayload;\n },\n\n async confirmAddCard(transactionId: string) {\n const parsed = await request({\n method: 'POST',\n path: '/api/v1/me/payment-methods/confirm',\n baseUrl,\n headers,\n body: { transactionId },\n });\n return parsed.data as SavedCard;\n },\n };\n}\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { createPaymentMethods } from './client';\nimport { ThrottlePaymentMethodsError } from './errors';\nimport type { EmbedPayload, SavedCard } from './types';\n\nexport interface UsePaymentMethodsOpts {\n clientToken: string;\n baseUrl?: string;\n}\n\nexport interface UsePaymentMethodsResult {\n cards: SavedCard[];\n isLoading: boolean;\n error: ThrottlePaymentMethodsError | null;\n setDefault(id: string): Promise<void>;\n remove(id: string): Promise<void>;\n addCard(): Promise<EmbedPayload>;\n /** Record a card vaulted by the add-card embed (given its transaction id), then refresh the list. */\n confirmAddCard(transactionId: string): Promise<void>;\n refetch(): Promise<void>;\n}\n\nfunction toPaymentMethodsError(err: unknown): ThrottlePaymentMethodsError {\n if (err instanceof ThrottlePaymentMethodsError) return err;\n const message = err instanceof Error ? err.message : String(err);\n return new ThrottlePaymentMethodsError({ code: 'unknown_error', message, statusCode: 0 });\n}\n\nexport function usePaymentMethods(opts: UsePaymentMethodsOpts): UsePaymentMethodsResult {\n const { clientToken, baseUrl } = opts;\n\n const client = useMemo(\n () => createPaymentMethods({ clientToken, baseUrl }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clientToken, baseUrl],\n );\n\n const [cards, setCards] = useState<SavedCard[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<ThrottlePaymentMethodsError | null>(null);\n const genRef = useRef(0);\n\n const refetch = useMemo(\n () => async () => {\n const myGen = genRef.current;\n try {\n const data = await client.list();\n if (genRef.current !== myGen) return;\n setCards(data);\n setError(null);\n } catch (err) {\n if (genRef.current !== myGen) return;\n setError(toPaymentMethodsError(err));\n }\n },\n [client],\n );\n\n useEffect(() => {\n const myGen = ++genRef.current;\n\n (async () => {\n setIsLoading(true);\n try {\n const data = await client.list();\n if (genRef.current !== myGen) return;\n setCards(data);\n setError(null);\n } catch (err) {\n if (genRef.current !== myGen) return;\n setError(toPaymentMethodsError(err));\n } finally {\n if (genRef.current === myGen) {\n setIsLoading(false);\n }\n }\n })();\n }, [client]);\n\n const setDefault = async (id: string): Promise<void> => {\n try {\n await client.setDefault(id);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n const remove = async (id: string): Promise<void> => {\n try {\n await client.remove(id);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n const addCard = async (): Promise<EmbedPayload> => {\n return client.startAddCard();\n };\n\n const confirmAddCard = async (transactionId: string): Promise<void> => {\n try {\n await client.confirmAddCard(transactionId);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n return { cards, isLoading, error, setDefault, remove, addCard, confirmAddCard, refetch };\n}\n","import React, { Suspense, useEffect, useRef, useState } from 'react';\nimport { usePaymentMethods } from './usePaymentMethods';\nimport type { EmbedPayload, SavedCard, PaymentMethodsTheme } from './types';\n\n// Lazy import — keeps @gr4vy/embed-react out of the eager bundle (SSR-safe).\n// Do NOT hoist this to a top-level import; React.lazy is the only safe path.\nconst LazyGr4vyAddCard = React.lazy(() =>\n import('./Gr4vyAddCard').then((m) => ({ default: m.Gr4vyAddCard })),\n);\n\nexport interface PaymentMethodsProps {\n clientToken: string;\n baseUrl?: string;\n theme?: PaymentMethodsTheme;\n onChange?: (cards: SavedCard[]) => void;\n}\n\nfunction formatCard(card: SavedCard): string {\n const brand = card.cardBrand ? card.cardBrand.toUpperCase() : 'Card';\n const last4 = card.cardLastFour ? `•••• ${card.cardLastFour}` : '••••';\n const brandPart = `${brand} ${last4}`;\n\n if (card.cardExpMonth != null && card.cardExpYear != null) {\n const mm = String(card.cardExpMonth).padStart(2, '0');\n const yy = String(card.cardExpYear).slice(-2);\n return `${brandPart} · ${mm}/${yy}`;\n }\n return brandPart;\n}\n\n/**\n * Extract a human-readable message from anything thrown/emitted. The Gr4vy embed\n * emits a plain object on `transactionFailed` (e.g. { code, message }), not an\n * Error — `String(obj)` would render \"[object Object]\", so pull `.message`.\n *\n * When no message survives, the fallback attributes NO cause. It used to say\n * \"double-check the details\", which is actively wrong for the most common\n * failure: an issuer decline, where the details were entered correctly and\n * re-typing them changes nothing. Processor codes are deliberately not\n * surfaced — they are processor vocabulary, not buyer-facing copy.\n */\nexport function toReadableMessage(err: unknown): string {\n if (err instanceof Error && err.message) return err.message;\n if (err && typeof err === 'object') {\n const o = err as Record<string, unknown>;\n if (typeof o.message === 'string' && o.message) return o.message;\n }\n if (typeof err === 'string' && err) return err;\n return \"We couldn't save your card. Please try another payment method.\";\n}\n\nexport function PaymentMethods({ clientToken, baseUrl, theme, onChange }: PaymentMethodsProps) {\n const { cards, isLoading, error, setDefault, remove, addCard, confirmAddCard, refetch } = usePaymentMethods({\n clientToken,\n baseUrl,\n });\n\n const [busyId, setBusyId] = useState<string | null>(null);\n const [addCardPayload, setAddCardPayload] = useState<EmbedPayload | null>(null);\n const [externalError, setExternalError] = useState<Error | null>(null);\n\n const onChangeRef = useRef(onChange);\n useEffect(() => { onChangeRef.current = onChange; });\n useEffect(() => { if (!isLoading) onChangeRef.current?.(cards); }, [cards, isLoading]);\n\n const primary = theme?.colorPrimary ?? '#1D56E8';\n const colorText = theme?.colorText ?? '#111827';\n const colorBorder = theme?.colorBorder ?? '#E5E7EB';\n const borderRadius = theme?.borderRadius ?? '8px';\n\n const handleSetDefault = async (id: string) => {\n setBusyId(id);\n try {\n await setDefault(id);\n } catch {\n // error is already set in the hook\n } finally {\n setBusyId(null);\n }\n };\n\n const handleRemove = async (id: string) => {\n setBusyId(id);\n try {\n await remove(id);\n } catch {\n // error is already set in the hook\n } finally {\n setBusyId(null);\n }\n };\n\n const handleAddCard = async () => {\n try {\n const payload = await addCard();\n setExternalError(null);\n setAddCardPayload(payload);\n } catch (err) {\n setExternalError(new Error(toReadableMessage(err)));\n }\n };\n\n if (isLoading) {\n return (\n <div role=\"status\" aria-label=\"Loading payment methods\" style={{ color: colorText }}>\n Loading…\n </div>\n );\n }\n\n const displayError = externalError ?? error;\n\n return (\n <div style={{ color: colorText, fontFamily: 'inherit' }}>\n {displayError && (\n <div\n role=\"alert\"\n style={{\n color: '#DC2626',\n marginBottom: '12px',\n padding: '8px 12px',\n border: `1px solid #FCA5A5`,\n borderRadius,\n fontSize: '14px',\n }}\n >\n {displayError.message}\n </div>\n )}\n\n <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>\n {cards.map((card) => {\n const isBusy = busyId === card.id;\n return (\n <li\n key={card.id}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n padding: '12px',\n border: `1px solid ${colorBorder}`,\n borderRadius,\n marginBottom: '8px',\n }}\n >\n <span style={{ flex: 1, fontSize: '14px' }}>{formatCard(card)}</span>\n\n {card.isDefault ? (\n <span\n style={{\n backgroundColor: primary,\n color: '#fff',\n fontSize: '12px',\n fontWeight: 600,\n padding: '2px 8px',\n borderRadius: '999px',\n }}\n >\n Default\n </span>\n ) : (\n <>\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => handleSetDefault(card.id)}\n style={{\n fontSize: '13px',\n padding: '4px 10px',\n border: `1px solid ${colorBorder}`,\n borderRadius,\n background: 'transparent',\n cursor: isBusy ? 'not-allowed' : 'pointer',\n opacity: isBusy ? 0.5 : 1,\n }}\n >\n Make default\n </button>\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => handleRemove(card.id)}\n style={{\n fontSize: '13px',\n padding: '4px 10px',\n border: `1px solid #FCA5A5`,\n borderRadius,\n background: 'transparent',\n color: '#DC2626',\n cursor: isBusy ? 'not-allowed' : 'pointer',\n opacity: isBusy ? 0.5 : 1,\n }}\n >\n Remove\n </button>\n </>\n )}\n </li>\n );\n })}\n </ul>\n\n {addCardPayload ? (\n <Suspense\n fallback={\n <div role=\"status\" style={{ color: colorText, marginTop: '12px' }}>\n Loading…\n </div>\n }\n >\n <LazyGr4vyAddCard\n payload={addCardPayload}\n theme={theme}\n onAdded={async ({ transactionId }) => {\n // Record the vaulted card in Throttle (so it shows in the wallet),\n // then refresh. confirmAddCard refetches on success.\n try {\n if (transactionId) await confirmAddCard(transactionId);\n else await refetch();\n setExternalError(null);\n } catch (e) {\n setExternalError(new Error(toReadableMessage(e)));\n } finally {\n setAddCardPayload(null);\n }\n }}\n onCancel={() => setAddCardPayload(null)}\n onError={(err) => {\n // Show the real reason and KEEP the embed open so the buyer can\n // fix the card and retry without re-opening the form.\n setExternalError(new Error(toReadableMessage(err)));\n }}\n />\n </Suspense>\n ) : (\n <button\n type=\"button\"\n onClick={handleAddCard}\n style={{\n marginTop: '12px',\n padding: '8px 16px',\n backgroundColor: primary,\n color: '#fff',\n border: 'none',\n borderRadius,\n fontSize: '14px',\n fontWeight: 600,\n cursor: 'pointer',\n }}\n >\n + Add card\n </button>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","Gr4vyEmbedImport","import_errors","import_react","import_jsx_runtime","React"]}
|
|
1
|
+
{"version":3,"sources":["../src/Gr4vyAddCard.tsx","../src/index.tsx","../src/errors.ts","../src/types.ts","../src/request.ts","../src/client.ts","../src/usePaymentMethods.ts","../src/PaymentMethods.tsx"],"sourcesContent":["/**\n * Gr4vyAddCard — lazy-loaded vault-only embed wrapper.\n *\n * THIS FILE MUST ONLY BE LOADED VIA React.lazy(() => import('./Gr4vyAddCard'))\n * from PaymentMethods.tsx. Never import it at the module top-level of any file\n * that is part of the server bundle — @gr4vy/embed-react touches `window` at\n * module evaluation time and will throw `ReferenceError: window is not defined`\n * in SSR contexts.\n */\nimport React, { useRef, useState, useEffect } from 'react';\nimport Gr4vyEmbedImport, { type EmbedInstance } from '@gr4vy/embed-react';\nimport type { EmbedPayload, PaymentMethodsTheme } from './types';\n\n/**\n * `@gr4vy/embed-react` is a CommonJS package (no `module`/`exports`/`type`\n * fields), so a consumer's bundler resolves `import Gr4vy from '@gr4vy/embed-react'`\n * differently depending on its CJS↔ESM interop: some hand back the component\n * directly, others hand back the module namespace `{ default: <component> }`.\n * Rendering the namespace object throws \"Element type is invalid … got: object\".\n * Unwrap `.default` until we reach an actual React element type (a function\n * component, or a forwardRef/memo object carrying `$$typeof`).\n */\nexport function resolveComponent(mod: unknown): React.ComponentType<Record<string, unknown>> {\n let x: any = mod;\n while (x && typeof x === 'object' && !x.$$typeof && x.default) x = x.default;\n return x as React.ComponentType<Record<string, unknown>>;\n}\nconst Gr4vyEmbed = resolveComponent(Gr4vyEmbedImport);\n\ninterface Gr4vyTransaction {\n id: string;\n status: string;\n paymentMethod?: { id?: string };\n}\n\n/**\n * Message for a `transactionFailed` event.\n *\n * `transactionFailed` means the transaction WAS created and the processor\n * refused it — so this is never a data-entry problem, and the copy must not\n * tell the buyer to re-check details they typed correctly (a `do_not_honor`\n * decline reaches us with `cvvResponseCode: 'match'`). Prefer the processor's\n * own sentence when it sends one; otherwise say what actually happened and\n * what to do about it. Deliberately carries no decline code — those are\n * processor vocabulary, not buyer-facing.\n */\nexport function declineMessage(data: unknown): string {\n if (data && typeof data === 'object') {\n const message = (data as Record<string, unknown>).message;\n if (typeof message === 'string' && message.trim()) return message.trim();\n }\n return 'Your card was declined by your bank. Try another card, or contact your bank to approve the charge.';\n}\n\nexport interface Gr4vyAddCardProps {\n payload: EmbedPayload;\n theme?: PaymentMethodsTheme;\n /** Fired when the embed completes the authorization + vault. Carries the\n * Gr4vy transaction id (used to record the card server-side) + the vaulted\n * payment-method id. */\n onAdded: (result: { transactionId?: string; paymentMethodId?: string }) => void;\n onCancel: () => void;\n onError: (err: unknown) => void;\n}\n\nexport function Gr4vyAddCard({ payload, theme, onAdded, onCancel, onError }: Gr4vyAddCardProps) {\n const embedRef = useRef<EmbedInstance>(null);\n // The Gr4vy embed loads its secure fields asynchronously (and is sometimes\n // slow). Don't let the buyer click \"Save card\" until the form is ready, or\n // the submit goes out with no card data and Gr4vy returns \"Request failed\n // validation\". `optionsLoaded` is the embed's \"form is ready\" signal.\n const [formReady, setFormReady] = useState(false);\n\n useEffect(() => {\n if (formReady) return;\n const t = setTimeout(() => {\n if (!formReady) {\n onError(\n new Error(\n \"The card form is taking longer than usual to load. Please close this and tap “Add card” again.\",\n ),\n );\n }\n }, 20000);\n return () => clearTimeout(t);\n }, [formReady, onError]);\n\n const colors = theme\n ? {\n ...(theme.colorPrimary !== undefined ? { primary: theme.colorPrimary } : {}),\n ...(theme.colorText !== undefined ? { text: theme.colorText } : {}),\n ...(theme.colorBorder !== undefined ? { inputBorder: theme.colorBorder } : {}),\n }\n : {};\n const gr4vyTheme = Object.keys(colors).length ? { colors } : undefined;\n\n return (\n <div>\n <Gr4vyEmbed\n ref={embedRef}\n gr4vyId={payload.processorInstanceId}\n environment={payload.environment}\n token={payload.embedToken}\n amount={payload.amount}\n currency={payload.currency}\n country={payload.country ?? 'US'}\n merchantAccountId={payload.merchantAccountId}\n intent=\"authorize\"\n paymentSource=\"recurring\"\n store={true}\n display=\"addOnly\"\n {...(payload.buyerExternalIdentifier\n ? { buyerExternalIdentifier: payload.buyerExternalIdentifier }\n : {})}\n {...(gr4vyTheme !== undefined ? { theme: gr4vyTheme } : {})}\n onComplete={(txn: Gr4vyTransaction) => {\n onAdded({ transactionId: txn.id, paymentMethodId: txn.paymentMethod?.id });\n }}\n onEvent={(eventName: string, data: unknown) => {\n // The form's fields are loaded and interactive.\n if (eventName === 'optionsLoaded' || eventName === 'formUpdate') {\n setFormReady(true);\n }\n if (eventName === 'transactionFailed') {\n onError(new Error(declineMessage(data)));\n }\n }}\n />\n\n <button\n type=\"button\"\n disabled={!formReady}\n onClick={() => {\n if (!formReady) return;\n embedRef.current?.submit();\n }}\n style={{\n marginTop: '12px',\n padding: '8px 16px',\n backgroundColor: formReady ? theme?.colorPrimary ?? '#1D56E8' : '#9CA3AF',\n color: '#fff',\n border: 'none',\n borderRadius: '8px',\n fontSize: '14px',\n fontWeight: 600,\n cursor: formReady ? 'pointer' : 'not-allowed',\n width: '100%',\n }}\n >\n {formReady ? 'Save card' : 'Loading card form…'}\n </button>\n\n <button\n type=\"button\"\n onClick={onCancel}\n style={{\n marginTop: '8px',\n padding: '8px 16px',\n backgroundColor: 'transparent',\n color: theme?.colorText ?? '#111827',\n border: `1px solid ${theme?.colorBorder ?? '#E5E7EB'}`,\n borderRadius: '8px',\n fontSize: '14px',\n cursor: 'pointer',\n width: '100%',\n }}\n >\n Cancel\n </button>\n </div>\n );\n}\n\nexport default Gr4vyAddCard;\n","export { ThrottlePaymentMethodsError, ThrottleError, isThrottleError } from './errors';\nexport type { SavedCard, EmbedPayload, MintedClientToken, PaymentMethodsTheme } from './types';\nexport { DEFAULT_BASE_URL } from './types';\nexport { createPaymentMethods } from './client';\nexport type { PaymentMethodsClient } from './client';\nexport { usePaymentMethods } from './usePaymentMethods';\nexport type { UsePaymentMethodsOpts, UsePaymentMethodsResult } from './usePaymentMethods';\nexport { PaymentMethods } from './PaymentMethods';\nexport type { PaymentMethodsProps } from './PaymentMethods';\n// Money helpers (MC2-R34): amounts are integer minor units of their `currency`.\nexport * as money from '@usethrottle/money';\nexport {\n formatMoney,\n currencySymbol,\n minorUnitsOf,\n toDecimalString,\n toMinor,\n parseMoney,\n moneyValue,\n CURRENCIES,\n isKnownCurrency,\n UnknownCurrencyError,\n} from '@usethrottle/money';\nexport type { CurrencyCode, MoneyValue } from '@usethrottle/money';\n","import { ThrottleError, type ThrottleErrorArgs } from '@usethrottle/errors';\n\nexport class ThrottlePaymentMethodsError extends ThrottleError {\n constructor(args: {\n code: string;\n message: string;\n statusCode: number;\n details?: unknown;\n responseHeaders?: ThrottleErrorArgs['responseHeaders'];\n }) {\n super(args);\n this.name = 'ThrottlePaymentMethodsError';\n }\n}\n\nexport { ThrottleError, isThrottleError } from '@usethrottle/errors';\n","import type { CurrencyCode } from '@usethrottle/money';\n\nexport interface SavedCard {\n id: string;\n methodType: string;\n cardBrand: string | null;\n cardLastFour: string | null;\n cardExpMonth: number | null;\n cardExpYear: number | null;\n isDefault: boolean;\n}\n\nexport interface EmbedPayload {\n embedToken: string;\n checkoutSessionId: string;\n amount: number;\n /** The verification hold's currency. */\n currency: CurrencyCode;\n processorInstanceId: string;\n merchantAccountId?: string;\n environment: 'sandbox' | 'production';\n /**\n * The buyer the embed JWT is pinned to. The add-only vault embed is a\n * recurring/store flow, so Gr4vy asserts the `<Gr4vyEmbed buyerExternalIdentifier>`\n * prop equals the JWT's value — the component MUST forward this or the embed\n * renders no card fields. Always present on the add-only embed payload.\n */\n buyerExternalIdentifier?: string;\n /** ISO country code the embed session was minted for. The Gr4vy embed requires\n * a `country` string at init (\"undefined must be a string ISO country code\").\n * Defaults to 'US' in the component when absent. */\n country?: string;\n hostedUrl?: string;\n embedUrl?: string;\n}\n\nexport interface MintedClientToken {\n token: string;\n expiresAt: string;\n}\n\nexport interface PaymentMethodsTheme {\n colorPrimary?: string;\n colorText?: string;\n colorBorder?: string;\n borderRadius?: string;\n}\n\nexport const DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n","import { ThrottlePaymentMethodsError } from './errors';\n\nexport async function request(input: {\n method: string;\n path: string;\n baseUrl: string;\n headers?: Record<string, string>;\n body?: unknown;\n}): Promise<any> {\n const res = await fetch(`${input.baseUrl}${input.path}`, {\n method: input.method,\n headers: {\n ...(input.body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(input.headers ?? {}),\n },\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body (e.g. an HTML 502/503 gateway page). Leave parsed as {} so\n // the non-2xx branch below still throws a typed ThrottlePaymentMethodsError\n // (code 'request_failed') rather than leaking a raw SyntaxError.\n }\n if (!res.ok) {\n const e = parsed?.error ?? {};\n throw new ThrottlePaymentMethodsError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n details: e.details,\n responseHeaders: res.headers,\n });\n }\n return parsed;\n}\n","import { request } from './request';\nimport { DEFAULT_BASE_URL } from './types';\nimport type { SavedCard, EmbedPayload } from './types';\n\nexport interface PaymentMethodsClient {\n list(): Promise<SavedCard[]>;\n setDefault(id: string): Promise<SavedCard>;\n remove(id: string): Promise<{ id: string }>;\n /**\n * Mint the add-card embed. `currency` is the verification hold's currency;\n * omitted, the store's main currency (or its first enabled payable one).\n */\n startAddCard(opts?: { currency?: string }): Promise<EmbedPayload>;\n /** Record a card vaulted via the add-card embed, given the embed's transaction id. */\n confirmAddCard(transactionId: string): Promise<SavedCard>;\n}\n\nexport function createPaymentMethods(opts: {\n clientToken: string;\n baseUrl?: string;\n}): PaymentMethodsClient {\n const baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;\n const headers = { 'X-Throttle-PM-Token': opts.clientToken };\n\n return {\n async list() {\n const parsed = await request({ method: 'GET', path: '/api/v1/me/payment-methods', baseUrl, headers });\n return parsed.data as SavedCard[];\n },\n\n async setDefault(id: string) {\n const parsed = await request({\n method: 'PATCH',\n path: `/api/v1/me/payment-methods/${encodeURIComponent(id)}`,\n baseUrl,\n headers,\n body: { isDefault: true },\n });\n return parsed.data as SavedCard;\n },\n\n async remove(id: string) {\n const parsed = await request({\n method: 'DELETE',\n path: `/api/v1/me/payment-methods/${encodeURIComponent(id)}`,\n baseUrl,\n headers,\n });\n return parsed.data as { id: string };\n },\n\n async startAddCard(startOpts?: { currency?: string }) {\n const parsed = await request({\n method: 'POST',\n path: '/api/v1/me/payment-methods/embed-token',\n baseUrl,\n headers,\n ...(startOpts?.currency ? { body: { currency: startOpts.currency } } : {}),\n });\n return parsed.data as EmbedPayload;\n },\n\n async confirmAddCard(transactionId: string) {\n const parsed = await request({\n method: 'POST',\n path: '/api/v1/me/payment-methods/confirm',\n baseUrl,\n headers,\n body: { transactionId },\n });\n return parsed.data as SavedCard;\n },\n };\n}\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { createPaymentMethods } from './client';\nimport { ThrottlePaymentMethodsError } from './errors';\nimport type { EmbedPayload, SavedCard } from './types';\n\nexport interface UsePaymentMethodsOpts {\n clientToken: string;\n baseUrl?: string;\n /** Currency of the add-card verification hold. Omitted: the store's main currency. */\n currency?: string;\n}\n\nexport interface UsePaymentMethodsResult {\n cards: SavedCard[];\n isLoading: boolean;\n error: ThrottlePaymentMethodsError | null;\n setDefault(id: string): Promise<void>;\n remove(id: string): Promise<void>;\n addCard(): Promise<EmbedPayload>;\n /** Record a card vaulted by the add-card embed (given its transaction id), then refresh the list. */\n confirmAddCard(transactionId: string): Promise<void>;\n refetch(): Promise<void>;\n}\n\nfunction toPaymentMethodsError(err: unknown): ThrottlePaymentMethodsError {\n if (err instanceof ThrottlePaymentMethodsError) return err;\n const message = err instanceof Error ? err.message : String(err);\n return new ThrottlePaymentMethodsError({ code: 'unknown_error', message, statusCode: 0 });\n}\n\nexport function usePaymentMethods(opts: UsePaymentMethodsOpts): UsePaymentMethodsResult {\n const { clientToken, baseUrl, currency } = opts;\n\n const client = useMemo(\n () => createPaymentMethods({ clientToken, baseUrl }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clientToken, baseUrl],\n );\n\n const [cards, setCards] = useState<SavedCard[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<ThrottlePaymentMethodsError | null>(null);\n const genRef = useRef(0);\n\n const refetch = useMemo(\n () => async () => {\n const myGen = genRef.current;\n try {\n const data = await client.list();\n if (genRef.current !== myGen) return;\n setCards(data);\n setError(null);\n } catch (err) {\n if (genRef.current !== myGen) return;\n setError(toPaymentMethodsError(err));\n }\n },\n [client],\n );\n\n useEffect(() => {\n const myGen = ++genRef.current;\n\n (async () => {\n setIsLoading(true);\n try {\n const data = await client.list();\n if (genRef.current !== myGen) return;\n setCards(data);\n setError(null);\n } catch (err) {\n if (genRef.current !== myGen) return;\n setError(toPaymentMethodsError(err));\n } finally {\n if (genRef.current === myGen) {\n setIsLoading(false);\n }\n }\n })();\n }, [client]);\n\n const setDefault = async (id: string): Promise<void> => {\n try {\n await client.setDefault(id);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n const remove = async (id: string): Promise<void> => {\n try {\n await client.remove(id);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n const addCard = async (): Promise<EmbedPayload> => {\n return currency ? client.startAddCard({ currency }) : client.startAddCard();\n };\n\n const confirmAddCard = async (transactionId: string): Promise<void> => {\n try {\n await client.confirmAddCard(transactionId);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n return { cards, isLoading, error, setDefault, remove, addCard, confirmAddCard, refetch };\n}\n","import React, { Suspense, useEffect, useRef, useState } from 'react';\nimport { usePaymentMethods } from './usePaymentMethods';\nimport type { EmbedPayload, SavedCard, PaymentMethodsTheme } from './types';\n\n// Lazy import — keeps @gr4vy/embed-react out of the eager bundle (SSR-safe).\n// Do NOT hoist this to a top-level import; React.lazy is the only safe path.\nconst LazyGr4vyAddCard = React.lazy(() =>\n import('./Gr4vyAddCard').then((m) => ({ default: m.Gr4vyAddCard })),\n);\n\nexport interface PaymentMethodsProps {\n clientToken: string;\n baseUrl?: string;\n theme?: PaymentMethodsTheme;\n onChange?: (cards: SavedCard[]) => void;\n /** Currency of the add-card verification hold. Omitted: the store's main currency. */\n currency?: string;\n}\n\nfunction formatCard(card: SavedCard): string {\n const brand = card.cardBrand ? card.cardBrand.toUpperCase() : 'Card';\n const last4 = card.cardLastFour ? `•••• ${card.cardLastFour}` : '••••';\n const brandPart = `${brand} ${last4}`;\n\n if (card.cardExpMonth != null && card.cardExpYear != null) {\n const mm = String(card.cardExpMonth).padStart(2, '0');\n const yy = String(card.cardExpYear).slice(-2);\n return `${brandPart} · ${mm}/${yy}`;\n }\n return brandPart;\n}\n\n/**\n * Extract a human-readable message from anything thrown/emitted. The Gr4vy embed\n * emits a plain object on `transactionFailed` (e.g. { code, message }), not an\n * Error — `String(obj)` would render \"[object Object]\", so pull `.message`.\n *\n * When no message survives, the fallback attributes NO cause. It used to say\n * \"double-check the details\", which is actively wrong for the most common\n * failure: an issuer decline, where the details were entered correctly and\n * re-typing them changes nothing. Processor codes are deliberately not\n * surfaced — they are processor vocabulary, not buyer-facing copy.\n */\nexport function toReadableMessage(err: unknown): string {\n if (err instanceof Error && err.message) return err.message;\n if (err && typeof err === 'object') {\n const o = err as Record<string, unknown>;\n if (typeof o.message === 'string' && o.message) return o.message;\n }\n if (typeof err === 'string' && err) return err;\n return \"We couldn't save your card. Please try another payment method.\";\n}\n\nexport function PaymentMethods({ clientToken, baseUrl, theme, onChange, currency }: PaymentMethodsProps) {\n const { cards, isLoading, error, setDefault, remove, addCard, confirmAddCard, refetch } = usePaymentMethods({\n clientToken,\n baseUrl,\n currency,\n });\n\n const [busyId, setBusyId] = useState<string | null>(null);\n const [addCardPayload, setAddCardPayload] = useState<EmbedPayload | null>(null);\n const [externalError, setExternalError] = useState<Error | null>(null);\n\n const onChangeRef = useRef(onChange);\n useEffect(() => { onChangeRef.current = onChange; });\n useEffect(() => { if (!isLoading) onChangeRef.current?.(cards); }, [cards, isLoading]);\n\n const primary = theme?.colorPrimary ?? '#1D56E8';\n const colorText = theme?.colorText ?? '#111827';\n const colorBorder = theme?.colorBorder ?? '#E5E7EB';\n const borderRadius = theme?.borderRadius ?? '8px';\n\n const handleSetDefault = async (id: string) => {\n setBusyId(id);\n try {\n await setDefault(id);\n } catch {\n // error is already set in the hook\n } finally {\n setBusyId(null);\n }\n };\n\n const handleRemove = async (id: string) => {\n setBusyId(id);\n try {\n await remove(id);\n } catch {\n // error is already set in the hook\n } finally {\n setBusyId(null);\n }\n };\n\n const handleAddCard = async () => {\n try {\n const payload = await addCard();\n setExternalError(null);\n setAddCardPayload(payload);\n } catch (err) {\n setExternalError(new Error(toReadableMessage(err)));\n }\n };\n\n if (isLoading) {\n return (\n <div role=\"status\" aria-label=\"Loading payment methods\" style={{ color: colorText }}>\n Loading…\n </div>\n );\n }\n\n const displayError = externalError ?? error;\n\n return (\n <div style={{ color: colorText, fontFamily: 'inherit' }}>\n {displayError && (\n <div\n role=\"alert\"\n style={{\n color: '#DC2626',\n marginBottom: '12px',\n padding: '8px 12px',\n border: `1px solid #FCA5A5`,\n borderRadius,\n fontSize: '14px',\n }}\n >\n {displayError.message}\n </div>\n )}\n\n <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>\n {cards.map((card) => {\n const isBusy = busyId === card.id;\n return (\n <li\n key={card.id}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n padding: '12px',\n border: `1px solid ${colorBorder}`,\n borderRadius,\n marginBottom: '8px',\n }}\n >\n <span style={{ flex: 1, fontSize: '14px' }}>{formatCard(card)}</span>\n\n {card.isDefault ? (\n <span\n style={{\n backgroundColor: primary,\n color: '#fff',\n fontSize: '12px',\n fontWeight: 600,\n padding: '2px 8px',\n borderRadius: '999px',\n }}\n >\n Default\n </span>\n ) : (\n <>\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => handleSetDefault(card.id)}\n style={{\n fontSize: '13px',\n padding: '4px 10px',\n border: `1px solid ${colorBorder}`,\n borderRadius,\n background: 'transparent',\n cursor: isBusy ? 'not-allowed' : 'pointer',\n opacity: isBusy ? 0.5 : 1,\n }}\n >\n Make default\n </button>\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => handleRemove(card.id)}\n style={{\n fontSize: '13px',\n padding: '4px 10px',\n border: `1px solid #FCA5A5`,\n borderRadius,\n background: 'transparent',\n color: '#DC2626',\n cursor: isBusy ? 'not-allowed' : 'pointer',\n opacity: isBusy ? 0.5 : 1,\n }}\n >\n Remove\n </button>\n </>\n )}\n </li>\n );\n })}\n </ul>\n\n {addCardPayload ? (\n <Suspense\n fallback={\n <div role=\"status\" style={{ color: colorText, marginTop: '12px' }}>\n Loading…\n </div>\n }\n >\n <LazyGr4vyAddCard\n payload={addCardPayload}\n theme={theme}\n onAdded={async ({ transactionId }) => {\n // Record the vaulted card in Throttle (so it shows in the wallet),\n // then refresh. confirmAddCard refetches on success.\n try {\n if (transactionId) await confirmAddCard(transactionId);\n else await refetch();\n setExternalError(null);\n } catch (e) {\n setExternalError(new Error(toReadableMessage(e)));\n } finally {\n setAddCardPayload(null);\n }\n }}\n onCancel={() => setAddCardPayload(null)}\n onError={(err) => {\n // Show the real reason and KEEP the embed open so the buyer can\n // fix the card and retry without re-opening the form.\n setExternalError(new Error(toReadableMessage(err)));\n }}\n />\n </Suspense>\n ) : (\n <button\n type=\"button\"\n onClick={handleAddCard}\n style={{\n marginTop: '12px',\n padding: '8px 16px',\n backgroundColor: primary,\n color: '#fff',\n border: 'none',\n borderRadius,\n fontSize: '14px',\n fontWeight: 600,\n cursor: 'pointer',\n }}\n >\n + Add card\n </button>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","Gr4vyEmbedImport","import_errors","import_react","import_jsx_runtime","React"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { ThrottleError, ThrottleErrorArgs } from '@usethrottle/errors';
|
|
2
2
|
export { ThrottleError, isThrottleError } from '@usethrottle/errors';
|
|
3
|
-
import { S as SavedCard, E as EmbedPayload, P as PaymentMethodsTheme } from './types-
|
|
4
|
-
export { D as DEFAULT_BASE_URL, M as MintedClientToken } from './types-
|
|
3
|
+
import { S as SavedCard, E as EmbedPayload, P as PaymentMethodsTheme } from './types-CuRlkq4t.cjs';
|
|
4
|
+
export { D as DEFAULT_BASE_URL, M as MintedClientToken } from './types-CuRlkq4t.cjs';
|
|
5
5
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
|
+
import * as money from '@usethrottle/money';
|
|
7
|
+
export { money };
|
|
8
|
+
export { CURRENCIES, CurrencyCode, MoneyValue, UnknownCurrencyError, currencySymbol, formatMoney, isKnownCurrency, minorUnitsOf, moneyValue, parseMoney, toDecimalString, toMinor } from '@usethrottle/money';
|
|
6
9
|
|
|
7
10
|
declare class ThrottlePaymentMethodsError extends ThrottleError {
|
|
8
11
|
constructor(args: {
|
|
@@ -20,7 +23,13 @@ interface PaymentMethodsClient {
|
|
|
20
23
|
remove(id: string): Promise<{
|
|
21
24
|
id: string;
|
|
22
25
|
}>;
|
|
23
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Mint the add-card embed. `currency` is the verification hold's currency;
|
|
28
|
+
* omitted, the store's main currency (or its first enabled payable one).
|
|
29
|
+
*/
|
|
30
|
+
startAddCard(opts?: {
|
|
31
|
+
currency?: string;
|
|
32
|
+
}): Promise<EmbedPayload>;
|
|
24
33
|
/** Record a card vaulted via the add-card embed, given the embed's transaction id. */
|
|
25
34
|
confirmAddCard(transactionId: string): Promise<SavedCard>;
|
|
26
35
|
}
|
|
@@ -32,6 +41,8 @@ declare function createPaymentMethods(opts: {
|
|
|
32
41
|
interface UsePaymentMethodsOpts {
|
|
33
42
|
clientToken: string;
|
|
34
43
|
baseUrl?: string;
|
|
44
|
+
/** Currency of the add-card verification hold. Omitted: the store's main currency. */
|
|
45
|
+
currency?: string;
|
|
35
46
|
}
|
|
36
47
|
interface UsePaymentMethodsResult {
|
|
37
48
|
cards: SavedCard[];
|
|
@@ -51,7 +62,9 @@ interface PaymentMethodsProps {
|
|
|
51
62
|
baseUrl?: string;
|
|
52
63
|
theme?: PaymentMethodsTheme;
|
|
53
64
|
onChange?: (cards: SavedCard[]) => void;
|
|
65
|
+
/** Currency of the add-card verification hold. Omitted: the store's main currency. */
|
|
66
|
+
currency?: string;
|
|
54
67
|
}
|
|
55
|
-
declare function PaymentMethods({ clientToken, baseUrl, theme, onChange }: PaymentMethodsProps): react_jsx_runtime.JSX.Element;
|
|
68
|
+
declare function PaymentMethods({ clientToken, baseUrl, theme, onChange, currency }: PaymentMethodsProps): react_jsx_runtime.JSX.Element;
|
|
56
69
|
|
|
57
70
|
export { EmbedPayload, PaymentMethods, type PaymentMethodsClient, type PaymentMethodsProps, PaymentMethodsTheme, SavedCard, ThrottlePaymentMethodsError, type UsePaymentMethodsOpts, type UsePaymentMethodsResult, createPaymentMethods, usePaymentMethods };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { ThrottleError, ThrottleErrorArgs } from '@usethrottle/errors';
|
|
2
2
|
export { ThrottleError, isThrottleError } from '@usethrottle/errors';
|
|
3
|
-
import { S as SavedCard, E as EmbedPayload, P as PaymentMethodsTheme } from './types-
|
|
4
|
-
export { D as DEFAULT_BASE_URL, M as MintedClientToken } from './types-
|
|
3
|
+
import { S as SavedCard, E as EmbedPayload, P as PaymentMethodsTheme } from './types-CuRlkq4t.js';
|
|
4
|
+
export { D as DEFAULT_BASE_URL, M as MintedClientToken } from './types-CuRlkq4t.js';
|
|
5
5
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
|
+
import * as money from '@usethrottle/money';
|
|
7
|
+
export { money };
|
|
8
|
+
export { CURRENCIES, CurrencyCode, MoneyValue, UnknownCurrencyError, currencySymbol, formatMoney, isKnownCurrency, minorUnitsOf, moneyValue, parseMoney, toDecimalString, toMinor } from '@usethrottle/money';
|
|
6
9
|
|
|
7
10
|
declare class ThrottlePaymentMethodsError extends ThrottleError {
|
|
8
11
|
constructor(args: {
|
|
@@ -20,7 +23,13 @@ interface PaymentMethodsClient {
|
|
|
20
23
|
remove(id: string): Promise<{
|
|
21
24
|
id: string;
|
|
22
25
|
}>;
|
|
23
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Mint the add-card embed. `currency` is the verification hold's currency;
|
|
28
|
+
* omitted, the store's main currency (or its first enabled payable one).
|
|
29
|
+
*/
|
|
30
|
+
startAddCard(opts?: {
|
|
31
|
+
currency?: string;
|
|
32
|
+
}): Promise<EmbedPayload>;
|
|
24
33
|
/** Record a card vaulted via the add-card embed, given the embed's transaction id. */
|
|
25
34
|
confirmAddCard(transactionId: string): Promise<SavedCard>;
|
|
26
35
|
}
|
|
@@ -32,6 +41,8 @@ declare function createPaymentMethods(opts: {
|
|
|
32
41
|
interface UsePaymentMethodsOpts {
|
|
33
42
|
clientToken: string;
|
|
34
43
|
baseUrl?: string;
|
|
44
|
+
/** Currency of the add-card verification hold. Omitted: the store's main currency. */
|
|
45
|
+
currency?: string;
|
|
35
46
|
}
|
|
36
47
|
interface UsePaymentMethodsResult {
|
|
37
48
|
cards: SavedCard[];
|
|
@@ -51,7 +62,9 @@ interface PaymentMethodsProps {
|
|
|
51
62
|
baseUrl?: string;
|
|
52
63
|
theme?: PaymentMethodsTheme;
|
|
53
64
|
onChange?: (cards: SavedCard[]) => void;
|
|
65
|
+
/** Currency of the add-card verification hold. Omitted: the store's main currency. */
|
|
66
|
+
currency?: string;
|
|
54
67
|
}
|
|
55
|
-
declare function PaymentMethods({ clientToken, baseUrl, theme, onChange }: PaymentMethodsProps): react_jsx_runtime.JSX.Element;
|
|
68
|
+
declare function PaymentMethods({ clientToken, baseUrl, theme, onChange, currency }: PaymentMethodsProps): react_jsx_runtime.JSX.Element;
|
|
56
69
|
|
|
57
70
|
export { EmbedPayload, PaymentMethods, type PaymentMethodsClient, type PaymentMethodsProps, PaymentMethodsTheme, SavedCard, ThrottlePaymentMethodsError, type UsePaymentMethodsOpts, type UsePaymentMethodsResult, createPaymentMethods, usePaymentMethods };
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
ThrottlePaymentMethodsError,
|
|
5
5
|
isThrottleError,
|
|
6
6
|
request
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-U5VCJZPP.js";
|
|
8
8
|
|
|
9
9
|
// src/client.ts
|
|
10
10
|
function createPaymentMethods(opts) {
|
|
@@ -34,12 +34,13 @@ function createPaymentMethods(opts) {
|
|
|
34
34
|
});
|
|
35
35
|
return parsed.data;
|
|
36
36
|
},
|
|
37
|
-
async startAddCard() {
|
|
37
|
+
async startAddCard(startOpts) {
|
|
38
38
|
const parsed = await request({
|
|
39
39
|
method: "POST",
|
|
40
40
|
path: "/api/v1/me/payment-methods/embed-token",
|
|
41
41
|
baseUrl,
|
|
42
|
-
headers
|
|
42
|
+
headers,
|
|
43
|
+
...startOpts?.currency ? { body: { currency: startOpts.currency } } : {}
|
|
43
44
|
});
|
|
44
45
|
return parsed.data;
|
|
45
46
|
},
|
|
@@ -64,7 +65,7 @@ function toPaymentMethodsError(err) {
|
|
|
64
65
|
return new ThrottlePaymentMethodsError({ code: "unknown_error", message, statusCode: 0 });
|
|
65
66
|
}
|
|
66
67
|
function usePaymentMethods(opts) {
|
|
67
|
-
const { clientToken, baseUrl } = opts;
|
|
68
|
+
const { clientToken, baseUrl, currency } = opts;
|
|
68
69
|
const client = useMemo(
|
|
69
70
|
() => createPaymentMethods({ clientToken, baseUrl }),
|
|
70
71
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -129,7 +130,7 @@ function usePaymentMethods(opts) {
|
|
|
129
130
|
}
|
|
130
131
|
};
|
|
131
132
|
const addCard = async () => {
|
|
132
|
-
return client.startAddCard();
|
|
133
|
+
return currency ? client.startAddCard({ currency }) : client.startAddCard();
|
|
133
134
|
};
|
|
134
135
|
const confirmAddCard = async (transactionId) => {
|
|
135
136
|
try {
|
|
@@ -170,10 +171,11 @@ function toReadableMessage(err) {
|
|
|
170
171
|
if (typeof err === "string" && err) return err;
|
|
171
172
|
return "We couldn't save your card. Please try another payment method.";
|
|
172
173
|
}
|
|
173
|
-
function PaymentMethods({ clientToken, baseUrl, theme, onChange }) {
|
|
174
|
+
function PaymentMethods({ clientToken, baseUrl, theme, onChange, currency }) {
|
|
174
175
|
const { cards, isLoading, error, setDefault, remove, addCard, confirmAddCard, refetch } = usePaymentMethods({
|
|
175
176
|
clientToken,
|
|
176
|
-
baseUrl
|
|
177
|
+
baseUrl,
|
|
178
|
+
currency
|
|
177
179
|
});
|
|
178
180
|
const [busyId, setBusyId] = useState2(null);
|
|
179
181
|
const [addCardPayload, setAddCardPayload] = useState2(null);
|
|
@@ -357,13 +359,39 @@ function PaymentMethods({ clientToken, baseUrl, theme, onChange }) {
|
|
|
357
359
|
)
|
|
358
360
|
] });
|
|
359
361
|
}
|
|
362
|
+
|
|
363
|
+
// src/index.tsx
|
|
364
|
+
import * as money from "@usethrottle/money";
|
|
365
|
+
import {
|
|
366
|
+
formatMoney,
|
|
367
|
+
currencySymbol,
|
|
368
|
+
minorUnitsOf,
|
|
369
|
+
toDecimalString,
|
|
370
|
+
toMinor,
|
|
371
|
+
parseMoney,
|
|
372
|
+
moneyValue,
|
|
373
|
+
CURRENCIES,
|
|
374
|
+
isKnownCurrency,
|
|
375
|
+
UnknownCurrencyError
|
|
376
|
+
} from "@usethrottle/money";
|
|
360
377
|
export {
|
|
378
|
+
CURRENCIES,
|
|
361
379
|
DEFAULT_BASE_URL,
|
|
362
380
|
PaymentMethods,
|
|
363
381
|
ThrottleError,
|
|
364
382
|
ThrottlePaymentMethodsError,
|
|
383
|
+
UnknownCurrencyError,
|
|
365
384
|
createPaymentMethods,
|
|
385
|
+
currencySymbol,
|
|
386
|
+
formatMoney,
|
|
387
|
+
isKnownCurrency,
|
|
366
388
|
isThrottleError,
|
|
389
|
+
minorUnitsOf,
|
|
390
|
+
money,
|
|
391
|
+
moneyValue,
|
|
392
|
+
parseMoney,
|
|
393
|
+
toDecimalString,
|
|
394
|
+
toMinor,
|
|
367
395
|
usePaymentMethods
|
|
368
396
|
};
|
|
369
397
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/usePaymentMethods.ts","../src/PaymentMethods.tsx"],"sourcesContent":["import { request } from './request';\nimport { DEFAULT_BASE_URL } from './types';\nimport type { SavedCard, EmbedPayload } from './types';\n\nexport interface PaymentMethodsClient {\n list(): Promise<SavedCard[]>;\n setDefault(id: string): Promise<SavedCard>;\n remove(id: string): Promise<{ id: string }>;\n startAddCard(): Promise<EmbedPayload>;\n /** Record a card vaulted via the add-card embed, given the embed's transaction id. */\n confirmAddCard(transactionId: string): Promise<SavedCard>;\n}\n\nexport function createPaymentMethods(opts: {\n clientToken: string;\n baseUrl?: string;\n}): PaymentMethodsClient {\n const baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;\n const headers = { 'X-Throttle-PM-Token': opts.clientToken };\n\n return {\n async list() {\n const parsed = await request({ method: 'GET', path: '/api/v1/me/payment-methods', baseUrl, headers });\n return parsed.data as SavedCard[];\n },\n\n async setDefault(id: string) {\n const parsed = await request({\n method: 'PATCH',\n path: `/api/v1/me/payment-methods/${encodeURIComponent(id)}`,\n baseUrl,\n headers,\n body: { isDefault: true },\n });\n return parsed.data as SavedCard;\n },\n\n async remove(id: string) {\n const parsed = await request({\n method: 'DELETE',\n path: `/api/v1/me/payment-methods/${encodeURIComponent(id)}`,\n baseUrl,\n headers,\n });\n return parsed.data as { id: string };\n },\n\n async startAddCard() {\n const parsed = await request({\n method: 'POST',\n path: '/api/v1/me/payment-methods/embed-token',\n baseUrl,\n headers,\n });\n return parsed.data as EmbedPayload;\n },\n\n async confirmAddCard(transactionId: string) {\n const parsed = await request({\n method: 'POST',\n path: '/api/v1/me/payment-methods/confirm',\n baseUrl,\n headers,\n body: { transactionId },\n });\n return parsed.data as SavedCard;\n },\n };\n}\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { createPaymentMethods } from './client';\nimport { ThrottlePaymentMethodsError } from './errors';\nimport type { EmbedPayload, SavedCard } from './types';\n\nexport interface UsePaymentMethodsOpts {\n clientToken: string;\n baseUrl?: string;\n}\n\nexport interface UsePaymentMethodsResult {\n cards: SavedCard[];\n isLoading: boolean;\n error: ThrottlePaymentMethodsError | null;\n setDefault(id: string): Promise<void>;\n remove(id: string): Promise<void>;\n addCard(): Promise<EmbedPayload>;\n /** Record a card vaulted by the add-card embed (given its transaction id), then refresh the list. */\n confirmAddCard(transactionId: string): Promise<void>;\n refetch(): Promise<void>;\n}\n\nfunction toPaymentMethodsError(err: unknown): ThrottlePaymentMethodsError {\n if (err instanceof ThrottlePaymentMethodsError) return err;\n const message = err instanceof Error ? err.message : String(err);\n return new ThrottlePaymentMethodsError({ code: 'unknown_error', message, statusCode: 0 });\n}\n\nexport function usePaymentMethods(opts: UsePaymentMethodsOpts): UsePaymentMethodsResult {\n const { clientToken, baseUrl } = opts;\n\n const client = useMemo(\n () => createPaymentMethods({ clientToken, baseUrl }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clientToken, baseUrl],\n );\n\n const [cards, setCards] = useState<SavedCard[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<ThrottlePaymentMethodsError | null>(null);\n const genRef = useRef(0);\n\n const refetch = useMemo(\n () => async () => {\n const myGen = genRef.current;\n try {\n const data = await client.list();\n if (genRef.current !== myGen) return;\n setCards(data);\n setError(null);\n } catch (err) {\n if (genRef.current !== myGen) return;\n setError(toPaymentMethodsError(err));\n }\n },\n [client],\n );\n\n useEffect(() => {\n const myGen = ++genRef.current;\n\n (async () => {\n setIsLoading(true);\n try {\n const data = await client.list();\n if (genRef.current !== myGen) return;\n setCards(data);\n setError(null);\n } catch (err) {\n if (genRef.current !== myGen) return;\n setError(toPaymentMethodsError(err));\n } finally {\n if (genRef.current === myGen) {\n setIsLoading(false);\n }\n }\n })();\n }, [client]);\n\n const setDefault = async (id: string): Promise<void> => {\n try {\n await client.setDefault(id);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n const remove = async (id: string): Promise<void> => {\n try {\n await client.remove(id);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n const addCard = async (): Promise<EmbedPayload> => {\n return client.startAddCard();\n };\n\n const confirmAddCard = async (transactionId: string): Promise<void> => {\n try {\n await client.confirmAddCard(transactionId);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n return { cards, isLoading, error, setDefault, remove, addCard, confirmAddCard, refetch };\n}\n","import React, { Suspense, useEffect, useRef, useState } from 'react';\nimport { usePaymentMethods } from './usePaymentMethods';\nimport type { EmbedPayload, SavedCard, PaymentMethodsTheme } from './types';\n\n// Lazy import — keeps @gr4vy/embed-react out of the eager bundle (SSR-safe).\n// Do NOT hoist this to a top-level import; React.lazy is the only safe path.\nconst LazyGr4vyAddCard = React.lazy(() =>\n import('./Gr4vyAddCard').then((m) => ({ default: m.Gr4vyAddCard })),\n);\n\nexport interface PaymentMethodsProps {\n clientToken: string;\n baseUrl?: string;\n theme?: PaymentMethodsTheme;\n onChange?: (cards: SavedCard[]) => void;\n}\n\nfunction formatCard(card: SavedCard): string {\n const brand = card.cardBrand ? card.cardBrand.toUpperCase() : 'Card';\n const last4 = card.cardLastFour ? `•••• ${card.cardLastFour}` : '••••';\n const brandPart = `${brand} ${last4}`;\n\n if (card.cardExpMonth != null && card.cardExpYear != null) {\n const mm = String(card.cardExpMonth).padStart(2, '0');\n const yy = String(card.cardExpYear).slice(-2);\n return `${brandPart} · ${mm}/${yy}`;\n }\n return brandPart;\n}\n\n/**\n * Extract a human-readable message from anything thrown/emitted. The Gr4vy embed\n * emits a plain object on `transactionFailed` (e.g. { code, message }), not an\n * Error — `String(obj)` would render \"[object Object]\", so pull `.message`.\n *\n * When no message survives, the fallback attributes NO cause. It used to say\n * \"double-check the details\", which is actively wrong for the most common\n * failure: an issuer decline, where the details were entered correctly and\n * re-typing them changes nothing. Processor codes are deliberately not\n * surfaced — they are processor vocabulary, not buyer-facing copy.\n */\nexport function toReadableMessage(err: unknown): string {\n if (err instanceof Error && err.message) return err.message;\n if (err && typeof err === 'object') {\n const o = err as Record<string, unknown>;\n if (typeof o.message === 'string' && o.message) return o.message;\n }\n if (typeof err === 'string' && err) return err;\n return \"We couldn't save your card. Please try another payment method.\";\n}\n\nexport function PaymentMethods({ clientToken, baseUrl, theme, onChange }: PaymentMethodsProps) {\n const { cards, isLoading, error, setDefault, remove, addCard, confirmAddCard, refetch } = usePaymentMethods({\n clientToken,\n baseUrl,\n });\n\n const [busyId, setBusyId] = useState<string | null>(null);\n const [addCardPayload, setAddCardPayload] = useState<EmbedPayload | null>(null);\n const [externalError, setExternalError] = useState<Error | null>(null);\n\n const onChangeRef = useRef(onChange);\n useEffect(() => { onChangeRef.current = onChange; });\n useEffect(() => { if (!isLoading) onChangeRef.current?.(cards); }, [cards, isLoading]);\n\n const primary = theme?.colorPrimary ?? '#1D56E8';\n const colorText = theme?.colorText ?? '#111827';\n const colorBorder = theme?.colorBorder ?? '#E5E7EB';\n const borderRadius = theme?.borderRadius ?? '8px';\n\n const handleSetDefault = async (id: string) => {\n setBusyId(id);\n try {\n await setDefault(id);\n } catch {\n // error is already set in the hook\n } finally {\n setBusyId(null);\n }\n };\n\n const handleRemove = async (id: string) => {\n setBusyId(id);\n try {\n await remove(id);\n } catch {\n // error is already set in the hook\n } finally {\n setBusyId(null);\n }\n };\n\n const handleAddCard = async () => {\n try {\n const payload = await addCard();\n setExternalError(null);\n setAddCardPayload(payload);\n } catch (err) {\n setExternalError(new Error(toReadableMessage(err)));\n }\n };\n\n if (isLoading) {\n return (\n <div role=\"status\" aria-label=\"Loading payment methods\" style={{ color: colorText }}>\n Loading…\n </div>\n );\n }\n\n const displayError = externalError ?? error;\n\n return (\n <div style={{ color: colorText, fontFamily: 'inherit' }}>\n {displayError && (\n <div\n role=\"alert\"\n style={{\n color: '#DC2626',\n marginBottom: '12px',\n padding: '8px 12px',\n border: `1px solid #FCA5A5`,\n borderRadius,\n fontSize: '14px',\n }}\n >\n {displayError.message}\n </div>\n )}\n\n <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>\n {cards.map((card) => {\n const isBusy = busyId === card.id;\n return (\n <li\n key={card.id}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n padding: '12px',\n border: `1px solid ${colorBorder}`,\n borderRadius,\n marginBottom: '8px',\n }}\n >\n <span style={{ flex: 1, fontSize: '14px' }}>{formatCard(card)}</span>\n\n {card.isDefault ? (\n <span\n style={{\n backgroundColor: primary,\n color: '#fff',\n fontSize: '12px',\n fontWeight: 600,\n padding: '2px 8px',\n borderRadius: '999px',\n }}\n >\n Default\n </span>\n ) : (\n <>\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => handleSetDefault(card.id)}\n style={{\n fontSize: '13px',\n padding: '4px 10px',\n border: `1px solid ${colorBorder}`,\n borderRadius,\n background: 'transparent',\n cursor: isBusy ? 'not-allowed' : 'pointer',\n opacity: isBusy ? 0.5 : 1,\n }}\n >\n Make default\n </button>\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => handleRemove(card.id)}\n style={{\n fontSize: '13px',\n padding: '4px 10px',\n border: `1px solid #FCA5A5`,\n borderRadius,\n background: 'transparent',\n color: '#DC2626',\n cursor: isBusy ? 'not-allowed' : 'pointer',\n opacity: isBusy ? 0.5 : 1,\n }}\n >\n Remove\n </button>\n </>\n )}\n </li>\n );\n })}\n </ul>\n\n {addCardPayload ? (\n <Suspense\n fallback={\n <div role=\"status\" style={{ color: colorText, marginTop: '12px' }}>\n Loading…\n </div>\n }\n >\n <LazyGr4vyAddCard\n payload={addCardPayload}\n theme={theme}\n onAdded={async ({ transactionId }) => {\n // Record the vaulted card in Throttle (so it shows in the wallet),\n // then refresh. confirmAddCard refetches on success.\n try {\n if (transactionId) await confirmAddCard(transactionId);\n else await refetch();\n setExternalError(null);\n } catch (e) {\n setExternalError(new Error(toReadableMessage(e)));\n } finally {\n setAddCardPayload(null);\n }\n }}\n onCancel={() => setAddCardPayload(null)}\n onError={(err) => {\n // Show the real reason and KEEP the embed open so the buyer can\n // fix the card and retry without re-opening the form.\n setExternalError(new Error(toReadableMessage(err)));\n }}\n />\n </Suspense>\n ) : (\n <button\n type=\"button\"\n onClick={handleAddCard}\n style={{\n marginTop: '12px',\n padding: '8px 16px',\n backgroundColor: primary,\n color: '#fff',\n border: 'none',\n borderRadius,\n fontSize: '14px',\n fontWeight: 600,\n cursor: 'pointer',\n }}\n >\n + Add card\n </button>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["useEffect","useRef","useState","useState","useRef","useEffect"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/usePaymentMethods.ts","../src/PaymentMethods.tsx","../src/index.tsx"],"sourcesContent":["import { request } from './request';\nimport { DEFAULT_BASE_URL } from './types';\nimport type { SavedCard, EmbedPayload } from './types';\n\nexport interface PaymentMethodsClient {\n list(): Promise<SavedCard[]>;\n setDefault(id: string): Promise<SavedCard>;\n remove(id: string): Promise<{ id: string }>;\n /**\n * Mint the add-card embed. `currency` is the verification hold's currency;\n * omitted, the store's main currency (or its first enabled payable one).\n */\n startAddCard(opts?: { currency?: string }): Promise<EmbedPayload>;\n /** Record a card vaulted via the add-card embed, given the embed's transaction id. */\n confirmAddCard(transactionId: string): Promise<SavedCard>;\n}\n\nexport function createPaymentMethods(opts: {\n clientToken: string;\n baseUrl?: string;\n}): PaymentMethodsClient {\n const baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;\n const headers = { 'X-Throttle-PM-Token': opts.clientToken };\n\n return {\n async list() {\n const parsed = await request({ method: 'GET', path: '/api/v1/me/payment-methods', baseUrl, headers });\n return parsed.data as SavedCard[];\n },\n\n async setDefault(id: string) {\n const parsed = await request({\n method: 'PATCH',\n path: `/api/v1/me/payment-methods/${encodeURIComponent(id)}`,\n baseUrl,\n headers,\n body: { isDefault: true },\n });\n return parsed.data as SavedCard;\n },\n\n async remove(id: string) {\n const parsed = await request({\n method: 'DELETE',\n path: `/api/v1/me/payment-methods/${encodeURIComponent(id)}`,\n baseUrl,\n headers,\n });\n return parsed.data as { id: string };\n },\n\n async startAddCard(startOpts?: { currency?: string }) {\n const parsed = await request({\n method: 'POST',\n path: '/api/v1/me/payment-methods/embed-token',\n baseUrl,\n headers,\n ...(startOpts?.currency ? { body: { currency: startOpts.currency } } : {}),\n });\n return parsed.data as EmbedPayload;\n },\n\n async confirmAddCard(transactionId: string) {\n const parsed = await request({\n method: 'POST',\n path: '/api/v1/me/payment-methods/confirm',\n baseUrl,\n headers,\n body: { transactionId },\n });\n return parsed.data as SavedCard;\n },\n };\n}\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { createPaymentMethods } from './client';\nimport { ThrottlePaymentMethodsError } from './errors';\nimport type { EmbedPayload, SavedCard } from './types';\n\nexport interface UsePaymentMethodsOpts {\n clientToken: string;\n baseUrl?: string;\n /** Currency of the add-card verification hold. Omitted: the store's main currency. */\n currency?: string;\n}\n\nexport interface UsePaymentMethodsResult {\n cards: SavedCard[];\n isLoading: boolean;\n error: ThrottlePaymentMethodsError | null;\n setDefault(id: string): Promise<void>;\n remove(id: string): Promise<void>;\n addCard(): Promise<EmbedPayload>;\n /** Record a card vaulted by the add-card embed (given its transaction id), then refresh the list. */\n confirmAddCard(transactionId: string): Promise<void>;\n refetch(): Promise<void>;\n}\n\nfunction toPaymentMethodsError(err: unknown): ThrottlePaymentMethodsError {\n if (err instanceof ThrottlePaymentMethodsError) return err;\n const message = err instanceof Error ? err.message : String(err);\n return new ThrottlePaymentMethodsError({ code: 'unknown_error', message, statusCode: 0 });\n}\n\nexport function usePaymentMethods(opts: UsePaymentMethodsOpts): UsePaymentMethodsResult {\n const { clientToken, baseUrl, currency } = opts;\n\n const client = useMemo(\n () => createPaymentMethods({ clientToken, baseUrl }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [clientToken, baseUrl],\n );\n\n const [cards, setCards] = useState<SavedCard[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<ThrottlePaymentMethodsError | null>(null);\n const genRef = useRef(0);\n\n const refetch = useMemo(\n () => async () => {\n const myGen = genRef.current;\n try {\n const data = await client.list();\n if (genRef.current !== myGen) return;\n setCards(data);\n setError(null);\n } catch (err) {\n if (genRef.current !== myGen) return;\n setError(toPaymentMethodsError(err));\n }\n },\n [client],\n );\n\n useEffect(() => {\n const myGen = ++genRef.current;\n\n (async () => {\n setIsLoading(true);\n try {\n const data = await client.list();\n if (genRef.current !== myGen) return;\n setCards(data);\n setError(null);\n } catch (err) {\n if (genRef.current !== myGen) return;\n setError(toPaymentMethodsError(err));\n } finally {\n if (genRef.current === myGen) {\n setIsLoading(false);\n }\n }\n })();\n }, [client]);\n\n const setDefault = async (id: string): Promise<void> => {\n try {\n await client.setDefault(id);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n const remove = async (id: string): Promise<void> => {\n try {\n await client.remove(id);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n const addCard = async (): Promise<EmbedPayload> => {\n return currency ? client.startAddCard({ currency }) : client.startAddCard();\n };\n\n const confirmAddCard = async (transactionId: string): Promise<void> => {\n try {\n await client.confirmAddCard(transactionId);\n await refetch();\n } catch (err) {\n const pmErr = toPaymentMethodsError(err);\n setError(pmErr);\n throw pmErr;\n }\n };\n\n return { cards, isLoading, error, setDefault, remove, addCard, confirmAddCard, refetch };\n}\n","import React, { Suspense, useEffect, useRef, useState } from 'react';\nimport { usePaymentMethods } from './usePaymentMethods';\nimport type { EmbedPayload, SavedCard, PaymentMethodsTheme } from './types';\n\n// Lazy import — keeps @gr4vy/embed-react out of the eager bundle (SSR-safe).\n// Do NOT hoist this to a top-level import; React.lazy is the only safe path.\nconst LazyGr4vyAddCard = React.lazy(() =>\n import('./Gr4vyAddCard').then((m) => ({ default: m.Gr4vyAddCard })),\n);\n\nexport interface PaymentMethodsProps {\n clientToken: string;\n baseUrl?: string;\n theme?: PaymentMethodsTheme;\n onChange?: (cards: SavedCard[]) => void;\n /** Currency of the add-card verification hold. Omitted: the store's main currency. */\n currency?: string;\n}\n\nfunction formatCard(card: SavedCard): string {\n const brand = card.cardBrand ? card.cardBrand.toUpperCase() : 'Card';\n const last4 = card.cardLastFour ? `•••• ${card.cardLastFour}` : '••••';\n const brandPart = `${brand} ${last4}`;\n\n if (card.cardExpMonth != null && card.cardExpYear != null) {\n const mm = String(card.cardExpMonth).padStart(2, '0');\n const yy = String(card.cardExpYear).slice(-2);\n return `${brandPart} · ${mm}/${yy}`;\n }\n return brandPart;\n}\n\n/**\n * Extract a human-readable message from anything thrown/emitted. The Gr4vy embed\n * emits a plain object on `transactionFailed` (e.g. { code, message }), not an\n * Error — `String(obj)` would render \"[object Object]\", so pull `.message`.\n *\n * When no message survives, the fallback attributes NO cause. It used to say\n * \"double-check the details\", which is actively wrong for the most common\n * failure: an issuer decline, where the details were entered correctly and\n * re-typing them changes nothing. Processor codes are deliberately not\n * surfaced — they are processor vocabulary, not buyer-facing copy.\n */\nexport function toReadableMessage(err: unknown): string {\n if (err instanceof Error && err.message) return err.message;\n if (err && typeof err === 'object') {\n const o = err as Record<string, unknown>;\n if (typeof o.message === 'string' && o.message) return o.message;\n }\n if (typeof err === 'string' && err) return err;\n return \"We couldn't save your card. Please try another payment method.\";\n}\n\nexport function PaymentMethods({ clientToken, baseUrl, theme, onChange, currency }: PaymentMethodsProps) {\n const { cards, isLoading, error, setDefault, remove, addCard, confirmAddCard, refetch } = usePaymentMethods({\n clientToken,\n baseUrl,\n currency,\n });\n\n const [busyId, setBusyId] = useState<string | null>(null);\n const [addCardPayload, setAddCardPayload] = useState<EmbedPayload | null>(null);\n const [externalError, setExternalError] = useState<Error | null>(null);\n\n const onChangeRef = useRef(onChange);\n useEffect(() => { onChangeRef.current = onChange; });\n useEffect(() => { if (!isLoading) onChangeRef.current?.(cards); }, [cards, isLoading]);\n\n const primary = theme?.colorPrimary ?? '#1D56E8';\n const colorText = theme?.colorText ?? '#111827';\n const colorBorder = theme?.colorBorder ?? '#E5E7EB';\n const borderRadius = theme?.borderRadius ?? '8px';\n\n const handleSetDefault = async (id: string) => {\n setBusyId(id);\n try {\n await setDefault(id);\n } catch {\n // error is already set in the hook\n } finally {\n setBusyId(null);\n }\n };\n\n const handleRemove = async (id: string) => {\n setBusyId(id);\n try {\n await remove(id);\n } catch {\n // error is already set in the hook\n } finally {\n setBusyId(null);\n }\n };\n\n const handleAddCard = async () => {\n try {\n const payload = await addCard();\n setExternalError(null);\n setAddCardPayload(payload);\n } catch (err) {\n setExternalError(new Error(toReadableMessage(err)));\n }\n };\n\n if (isLoading) {\n return (\n <div role=\"status\" aria-label=\"Loading payment methods\" style={{ color: colorText }}>\n Loading…\n </div>\n );\n }\n\n const displayError = externalError ?? error;\n\n return (\n <div style={{ color: colorText, fontFamily: 'inherit' }}>\n {displayError && (\n <div\n role=\"alert\"\n style={{\n color: '#DC2626',\n marginBottom: '12px',\n padding: '8px 12px',\n border: `1px solid #FCA5A5`,\n borderRadius,\n fontSize: '14px',\n }}\n >\n {displayError.message}\n </div>\n )}\n\n <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>\n {cards.map((card) => {\n const isBusy = busyId === card.id;\n return (\n <li\n key={card.id}\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '12px',\n padding: '12px',\n border: `1px solid ${colorBorder}`,\n borderRadius,\n marginBottom: '8px',\n }}\n >\n <span style={{ flex: 1, fontSize: '14px' }}>{formatCard(card)}</span>\n\n {card.isDefault ? (\n <span\n style={{\n backgroundColor: primary,\n color: '#fff',\n fontSize: '12px',\n fontWeight: 600,\n padding: '2px 8px',\n borderRadius: '999px',\n }}\n >\n Default\n </span>\n ) : (\n <>\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => handleSetDefault(card.id)}\n style={{\n fontSize: '13px',\n padding: '4px 10px',\n border: `1px solid ${colorBorder}`,\n borderRadius,\n background: 'transparent',\n cursor: isBusy ? 'not-allowed' : 'pointer',\n opacity: isBusy ? 0.5 : 1,\n }}\n >\n Make default\n </button>\n <button\n type=\"button\"\n disabled={isBusy}\n onClick={() => handleRemove(card.id)}\n style={{\n fontSize: '13px',\n padding: '4px 10px',\n border: `1px solid #FCA5A5`,\n borderRadius,\n background: 'transparent',\n color: '#DC2626',\n cursor: isBusy ? 'not-allowed' : 'pointer',\n opacity: isBusy ? 0.5 : 1,\n }}\n >\n Remove\n </button>\n </>\n )}\n </li>\n );\n })}\n </ul>\n\n {addCardPayload ? (\n <Suspense\n fallback={\n <div role=\"status\" style={{ color: colorText, marginTop: '12px' }}>\n Loading…\n </div>\n }\n >\n <LazyGr4vyAddCard\n payload={addCardPayload}\n theme={theme}\n onAdded={async ({ transactionId }) => {\n // Record the vaulted card in Throttle (so it shows in the wallet),\n // then refresh. confirmAddCard refetches on success.\n try {\n if (transactionId) await confirmAddCard(transactionId);\n else await refetch();\n setExternalError(null);\n } catch (e) {\n setExternalError(new Error(toReadableMessage(e)));\n } finally {\n setAddCardPayload(null);\n }\n }}\n onCancel={() => setAddCardPayload(null)}\n onError={(err) => {\n // Show the real reason and KEEP the embed open so the buyer can\n // fix the card and retry without re-opening the form.\n setExternalError(new Error(toReadableMessage(err)));\n }}\n />\n </Suspense>\n ) : (\n <button\n type=\"button\"\n onClick={handleAddCard}\n style={{\n marginTop: '12px',\n padding: '8px 16px',\n backgroundColor: primary,\n color: '#fff',\n border: 'none',\n borderRadius,\n fontSize: '14px',\n fontWeight: 600,\n cursor: 'pointer',\n }}\n >\n + Add card\n </button>\n )}\n </div>\n );\n}\n","export { ThrottlePaymentMethodsError, ThrottleError, isThrottleError } from './errors';\nexport type { SavedCard, EmbedPayload, MintedClientToken, PaymentMethodsTheme } from './types';\nexport { DEFAULT_BASE_URL } from './types';\nexport { createPaymentMethods } from './client';\nexport type { PaymentMethodsClient } from './client';\nexport { usePaymentMethods } from './usePaymentMethods';\nexport type { UsePaymentMethodsOpts, UsePaymentMethodsResult } from './usePaymentMethods';\nexport { PaymentMethods } from './PaymentMethods';\nexport type { PaymentMethodsProps } from './PaymentMethods';\n// Money helpers (MC2-R34): amounts are integer minor units of their `currency`.\nexport * as money from '@usethrottle/money';\nexport {\n formatMoney,\n currencySymbol,\n minorUnitsOf,\n toDecimalString,\n toMinor,\n parseMoney,\n moneyValue,\n CURRENCIES,\n isKnownCurrency,\n UnknownCurrencyError,\n} from '@usethrottle/money';\nexport type { CurrencyCode, MoneyValue } from '@usethrottle/money';\n"],"mappings":"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","names":["useEffect","useRef","useState","useState","useRef","useEffect"]}
|
package/dist/server.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts","../src/errors.ts","../src/request.ts","../src/types.ts"],"sourcesContent":["/**\n * Server-only entry for @usethrottle/payment-methods.\n *\n * NOTE: No `startAddCard` here — adding a card is inherently a browser flow.\n * The backend's role is to mint a short-lived client token via `mintClientToken`;\n * the browser then passes that token to <PaymentMethods/> (or the headless client)\n * to initiate the card-add UI. The server cannot initiate the Gr4vy embed flow.\n *\n * MUST NOT import React or @gr4vy/embed-react. Only ./request, ./types, ./errors.\n */\n\nimport { request } from './request';\nimport type { MintedClientToken, SavedCard } from './types';\nimport { DEFAULT_BASE_URL } from './types';\n\ninterface ServerOpts {\n apiKey: string;\n customerId: string;\n baseUrl?: string;\n environmentId?: string;\n}\n\nfunction authHeaders(opts: Pick<ServerOpts, 'apiKey' | 'environmentId'>): Record<string, string> {\n return {\n 'X-API-Key': opts.apiKey,\n ...(opts.environmentId ? { 'X-Throttle-Environment-Id': opts.environmentId } : {}),\n };\n}\n\nexport async function mintClientToken(opts: ServerOpts): Promise<MintedClientToken> {\n const parsed = await request({\n method: 'POST',\n path: '/api/v1/payment-methods/client-token',\n baseUrl: opts.baseUrl ?? DEFAULT_BASE_URL,\n headers: authHeaders(opts),\n body: { customerId: opts.customerId },\n });\n return parsed.data as MintedClientToken;\n}\n\nexport function createServerPaymentMethods(opts: ServerOpts): {\n list(): Promise<SavedCard[]>;\n setDefault(id: string): Promise<SavedCard>;\n remove(id: string): Promise<{ id: string }>;\n} {\n const base = opts.baseUrl ?? DEFAULT_BASE_URL;\n const prefix = `/api/v1/customers/${encodeURIComponent(opts.customerId)}/payment-methods`;\n const headers = authHeaders(opts);\n\n return {\n async list() {\n const parsed = await request({ method: 'GET', path: prefix, baseUrl: base, headers });\n return parsed.data as SavedCard[];\n },\n async setDefault(id: string) {\n const parsed = await request({\n method: 'PATCH',\n path: `${prefix}/${encodeURIComponent(id)}`,\n baseUrl: base,\n headers,\n body: { isDefault: true },\n });\n return parsed.data as SavedCard;\n },\n async remove(id: string) {\n const parsed = await request({\n method: 'DELETE',\n path: `${prefix}/${encodeURIComponent(id)}`,\n baseUrl: base,\n headers,\n });\n return parsed.data as { id: string };\n },\n };\n}\n","import { ThrottleError, type ThrottleErrorArgs } from '@usethrottle/errors';\n\nexport class ThrottlePaymentMethodsError extends ThrottleError {\n constructor(args: {\n code: string;\n message: string;\n statusCode: number;\n details?: unknown;\n responseHeaders?: ThrottleErrorArgs['responseHeaders'];\n }) {\n super(args);\n this.name = 'ThrottlePaymentMethodsError';\n }\n}\n\nexport { ThrottleError, isThrottleError } from '@usethrottle/errors';\n","import { ThrottlePaymentMethodsError } from './errors';\n\nexport async function request(input: {\n method: string;\n path: string;\n baseUrl: string;\n headers?: Record<string, string>;\n body?: unknown;\n}): Promise<any> {\n const res = await fetch(`${input.baseUrl}${input.path}`, {\n method: input.method,\n headers: {\n ...(input.body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(input.headers ?? {}),\n },\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body (e.g. an HTML 502/503 gateway page). Leave parsed as {} so\n // the non-2xx branch below still throws a typed ThrottlePaymentMethodsError\n // (code 'request_failed') rather than leaking a raw SyntaxError.\n }\n if (!res.ok) {\n const e = parsed?.error ?? {};\n throw new ThrottlePaymentMethodsError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n details: e.details,\n responseHeaders: res.headers,\n });\n }\n return parsed;\n}\n","
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/errors.ts","../src/request.ts","../src/types.ts"],"sourcesContent":["/**\n * Server-only entry for @usethrottle/payment-methods.\n *\n * NOTE: No `startAddCard` here — adding a card is inherently a browser flow.\n * The backend's role is to mint a short-lived client token via `mintClientToken`;\n * the browser then passes that token to <PaymentMethods/> (or the headless client)\n * to initiate the card-add UI. The server cannot initiate the Gr4vy embed flow.\n *\n * MUST NOT import React or @gr4vy/embed-react. Only ./request, ./types, ./errors.\n */\n\nimport { request } from './request';\nimport type { MintedClientToken, SavedCard } from './types';\nimport { DEFAULT_BASE_URL } from './types';\n\ninterface ServerOpts {\n apiKey: string;\n customerId: string;\n baseUrl?: string;\n environmentId?: string;\n}\n\nfunction authHeaders(opts: Pick<ServerOpts, 'apiKey' | 'environmentId'>): Record<string, string> {\n return {\n 'X-API-Key': opts.apiKey,\n ...(opts.environmentId ? { 'X-Throttle-Environment-Id': opts.environmentId } : {}),\n };\n}\n\nexport async function mintClientToken(opts: ServerOpts): Promise<MintedClientToken> {\n const parsed = await request({\n method: 'POST',\n path: '/api/v1/payment-methods/client-token',\n baseUrl: opts.baseUrl ?? DEFAULT_BASE_URL,\n headers: authHeaders(opts),\n body: { customerId: opts.customerId },\n });\n return parsed.data as MintedClientToken;\n}\n\nexport function createServerPaymentMethods(opts: ServerOpts): {\n list(): Promise<SavedCard[]>;\n setDefault(id: string): Promise<SavedCard>;\n remove(id: string): Promise<{ id: string }>;\n} {\n const base = opts.baseUrl ?? DEFAULT_BASE_URL;\n const prefix = `/api/v1/customers/${encodeURIComponent(opts.customerId)}/payment-methods`;\n const headers = authHeaders(opts);\n\n return {\n async list() {\n const parsed = await request({ method: 'GET', path: prefix, baseUrl: base, headers });\n return parsed.data as SavedCard[];\n },\n async setDefault(id: string) {\n const parsed = await request({\n method: 'PATCH',\n path: `${prefix}/${encodeURIComponent(id)}`,\n baseUrl: base,\n headers,\n body: { isDefault: true },\n });\n return parsed.data as SavedCard;\n },\n async remove(id: string) {\n const parsed = await request({\n method: 'DELETE',\n path: `${prefix}/${encodeURIComponent(id)}`,\n baseUrl: base,\n headers,\n });\n return parsed.data as { id: string };\n },\n };\n}\n","import { ThrottleError, type ThrottleErrorArgs } from '@usethrottle/errors';\n\nexport class ThrottlePaymentMethodsError extends ThrottleError {\n constructor(args: {\n code: string;\n message: string;\n statusCode: number;\n details?: unknown;\n responseHeaders?: ThrottleErrorArgs['responseHeaders'];\n }) {\n super(args);\n this.name = 'ThrottlePaymentMethodsError';\n }\n}\n\nexport { ThrottleError, isThrottleError } from '@usethrottle/errors';\n","import { ThrottlePaymentMethodsError } from './errors';\n\nexport async function request(input: {\n method: string;\n path: string;\n baseUrl: string;\n headers?: Record<string, string>;\n body?: unknown;\n}): Promise<any> {\n const res = await fetch(`${input.baseUrl}${input.path}`, {\n method: input.method,\n headers: {\n ...(input.body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(input.headers ?? {}),\n },\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body (e.g. an HTML 502/503 gateway page). Leave parsed as {} so\n // the non-2xx branch below still throws a typed ThrottlePaymentMethodsError\n // (code 'request_failed') rather than leaking a raw SyntaxError.\n }\n if (!res.ok) {\n const e = parsed?.error ?? {};\n throw new ThrottlePaymentMethodsError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n details: e.details,\n responseHeaders: res.headers,\n });\n }\n return parsed;\n}\n","import type { CurrencyCode } from '@usethrottle/money';\n\nexport interface SavedCard {\n id: string;\n methodType: string;\n cardBrand: string | null;\n cardLastFour: string | null;\n cardExpMonth: number | null;\n cardExpYear: number | null;\n isDefault: boolean;\n}\n\nexport interface EmbedPayload {\n embedToken: string;\n checkoutSessionId: string;\n amount: number;\n /** The verification hold's currency. */\n currency: CurrencyCode;\n processorInstanceId: string;\n merchantAccountId?: string;\n environment: 'sandbox' | 'production';\n /**\n * The buyer the embed JWT is pinned to. The add-only vault embed is a\n * recurring/store flow, so Gr4vy asserts the `<Gr4vyEmbed buyerExternalIdentifier>`\n * prop equals the JWT's value — the component MUST forward this or the embed\n * renders no card fields. Always present on the add-only embed payload.\n */\n buyerExternalIdentifier?: string;\n /** ISO country code the embed session was minted for. The Gr4vy embed requires\n * a `country` string at init (\"undefined must be a string ISO country code\").\n * Defaults to 'US' in the component when absent. */\n country?: string;\n hostedUrl?: string;\n embedUrl?: string;\n}\n\nexport interface MintedClientToken {\n token: string;\n expiresAt: string;\n}\n\nexport interface PaymentMethodsTheme {\n colorPrimary?: string;\n colorText?: string;\n colorBorder?: string;\n borderRadius?: string;\n}\n\nexport const DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAAsD;AAetD,IAAAA,iBAA+C;AAbxC,IAAM,8BAAN,cAA0C,4BAAc;AAAA,EAC7D,YAAY,MAMT;AACD,UAAM,IAAI;AACV,SAAK,OAAO;AAAA,EACd;AACF;;;ACXA,eAAsB,QAAQ,OAMb;AACf,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,GAAG,MAAM,IAAI,IAAI;AAAA,IACvD,QAAQ,MAAM;AAAA,IACd,SAAS;AAAA,MACP,GAAI,MAAM,SAAS,SAAY,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,MACzE,GAAI,MAAM,WAAW,CAAC;AAAA,IACxB;AAAA,IACA,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,KAAK,UAAU,MAAM,IAAI,EAAE,IAAI,CAAC;AAAA,EACzE,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,SAAc,CAAC;AACnB,MAAI;AACF,aAAS,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC;AAAA,EACtC,QAAQ;AAAA,EAIR;AACA,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,QAAQ,SAAS,CAAC;AAC5B,UAAM,IAAI,4BAA4B;AAAA,MACpC,MAAM,EAAE,QAAQ;AAAA,MAChB,SAAS,EAAE,WAAW,uBAAuB,IAAI,MAAM;AAAA,MACvD,YAAY,IAAI;AAAA,MAChB,SAAS,EAAE;AAAA,MACX,iBAAiB,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AACA,SAAO;AACT;;;ACWO,IAAM,mBAAmB;;;AH1BhC,SAAS,YAAY,MAA4E;AAC/F,SAAO;AAAA,IACL,aAAa,KAAK;AAAA,IAClB,GAAI,KAAK,gBAAgB,EAAE,6BAA6B,KAAK,cAAc,IAAI,CAAC;AAAA,EAClF;AACF;AAEA,eAAsB,gBAAgB,MAA8C;AAClF,QAAM,SAAS,MAAM,QAAQ;AAAA,IAC3B,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS,KAAK,WAAW;AAAA,IACzB,SAAS,YAAY,IAAI;AAAA,IACzB,MAAM,EAAE,YAAY,KAAK,WAAW;AAAA,EACtC,CAAC;AACD,SAAO,OAAO;AAChB;AAEO,SAAS,2BAA2B,MAIzC;AACA,QAAM,OAAO,KAAK,WAAW;AAC7B,QAAM,SAAS,qBAAqB,mBAAmB,KAAK,UAAU,CAAC;AACvE,QAAM,UAAU,YAAY,IAAI;AAEhC,SAAO;AAAA,IACL,MAAM,OAAO;AACX,YAAM,SAAS,MAAM,QAAQ,EAAE,QAAQ,OAAO,MAAM,QAAQ,SAAS,MAAM,QAAQ,CAAC;AACpF,aAAO,OAAO;AAAA,IAChB;AAAA,IACA,MAAM,WAAW,IAAY;AAC3B,YAAM,SAAS,MAAM,QAAQ;AAAA,QAC3B,QAAQ;AAAA,QACR,MAAM,GAAG,MAAM,IAAI,mBAAmB,EAAE,CAAC;AAAA,QACzC,SAAS;AAAA,QACT;AAAA,QACA,MAAM,EAAE,WAAW,KAAK;AAAA,MAC1B,CAAC;AACD,aAAO,OAAO;AAAA,IAChB;AAAA,IACA,MAAM,OAAO,IAAY;AACvB,YAAM,SAAS,MAAM,QAAQ;AAAA,QAC3B,QAAQ;AAAA,QACR,MAAM,GAAG,MAAM,IAAI,mBAAmB,EAAE,CAAC;AAAA,QACzC,SAAS;AAAA,QACT;AAAA,MACF,CAAC;AACD,aAAO,OAAO;AAAA,IAChB;AAAA,EACF;AACF;","names":["import_errors"]}
|
package/dist/server.d.cts
CHANGED
package/dist/server.d.ts
CHANGED
package/dist/server.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { CurrencyCode } from '@usethrottle/money';
|
|
2
|
+
|
|
1
3
|
interface SavedCard {
|
|
2
4
|
id: string;
|
|
3
5
|
methodType: string;
|
|
@@ -11,7 +13,8 @@ interface EmbedPayload {
|
|
|
11
13
|
embedToken: string;
|
|
12
14
|
checkoutSessionId: string;
|
|
13
15
|
amount: number;
|
|
14
|
-
currency
|
|
16
|
+
/** The verification hold's currency. */
|
|
17
|
+
currency: CurrencyCode;
|
|
15
18
|
processorInstanceId: string;
|
|
16
19
|
merchantAccountId?: string;
|
|
17
20
|
environment: 'sandbox' | 'production';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { CurrencyCode } from '@usethrottle/money';
|
|
2
|
+
|
|
1
3
|
interface SavedCard {
|
|
2
4
|
id: string;
|
|
3
5
|
methodType: string;
|
|
@@ -11,7 +13,8 @@ interface EmbedPayload {
|
|
|
11
13
|
embedToken: string;
|
|
12
14
|
checkoutSessionId: string;
|
|
13
15
|
amount: number;
|
|
14
|
-
currency
|
|
16
|
+
/** The verification hold's currency. */
|
|
17
|
+
currency: CurrencyCode;
|
|
15
18
|
processorInstanceId: string;
|
|
16
19
|
merchantAccountId?: string;
|
|
17
20
|
environment: 'sandbox' | 'production';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usethrottle/payment-methods",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Drop-in saved-card wallet (list, add, set default, remove) for Throttle storefronts — headless core + React component.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@gr4vy/embed-react": "^2.38.0",
|
|
28
|
-
"@usethrottle/errors": "^1.1.0"
|
|
28
|
+
"@usethrottle/errors": "^1.1.0",
|
|
29
|
+
"@usethrottle/money": "^1.0.0"
|
|
29
30
|
},
|
|
30
31
|
"peerDependencies": {
|
|
31
32
|
"react": ">=18",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/types.ts","../src/request.ts"],"sourcesContent":["import { ThrottleError, type ThrottleErrorArgs } from '@usethrottle/errors';\n\nexport class ThrottlePaymentMethodsError extends ThrottleError {\n constructor(args: {\n code: string;\n message: string;\n statusCode: number;\n details?: unknown;\n responseHeaders?: ThrottleErrorArgs['responseHeaders'];\n }) {\n super(args);\n this.name = 'ThrottlePaymentMethodsError';\n }\n}\n\nexport { ThrottleError, isThrottleError } from '@usethrottle/errors';\n","export interface SavedCard {\n id: string;\n methodType: string;\n cardBrand: string | null;\n cardLastFour: string | null;\n cardExpMonth: number | null;\n cardExpYear: number | null;\n isDefault: boolean;\n}\n\nexport interface EmbedPayload {\n embedToken: string;\n checkoutSessionId: string;\n amount: number;\n currency: string;\n processorInstanceId: string;\n merchantAccountId?: string;\n environment: 'sandbox' | 'production';\n /**\n * The buyer the embed JWT is pinned to. The add-only vault embed is a\n * recurring/store flow, so Gr4vy asserts the `<Gr4vyEmbed buyerExternalIdentifier>`\n * prop equals the JWT's value — the component MUST forward this or the embed\n * renders no card fields. Always present on the add-only embed payload.\n */\n buyerExternalIdentifier?: string;\n /** ISO country code the embed session was minted for. The Gr4vy embed requires\n * a `country` string at init (\"undefined must be a string ISO country code\").\n * Defaults to 'US' in the component when absent. */\n country?: string;\n hostedUrl?: string;\n embedUrl?: string;\n}\n\nexport interface MintedClientToken {\n token: string;\n expiresAt: string;\n}\n\nexport interface PaymentMethodsTheme {\n colorPrimary?: string;\n colorText?: string;\n colorBorder?: string;\n borderRadius?: string;\n}\n\nexport const DEFAULT_BASE_URL = 'https://api.usethrottle.dev';\n","import { ThrottlePaymentMethodsError } from './errors';\n\nexport async function request(input: {\n method: string;\n path: string;\n baseUrl: string;\n headers?: Record<string, string>;\n body?: unknown;\n}): Promise<any> {\n const res = await fetch(`${input.baseUrl}${input.path}`, {\n method: input.method,\n headers: {\n ...(input.body !== undefined ? { 'Content-Type': 'application/json' } : {}),\n ...(input.headers ?? {}),\n },\n ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),\n });\n const text = await res.text();\n let parsed: any = {};\n try {\n parsed = text ? JSON.parse(text) : {};\n } catch {\n // Non-JSON body (e.g. an HTML 502/503 gateway page). Leave parsed as {} so\n // the non-2xx branch below still throws a typed ThrottlePaymentMethodsError\n // (code 'request_failed') rather than leaking a raw SyntaxError.\n }\n if (!res.ok) {\n const e = parsed?.error ?? {};\n throw new ThrottlePaymentMethodsError({\n code: e.code ?? 'request_failed',\n message: e.message ?? `Request failed with ${res.status}`,\n statusCode: res.status,\n details: e.details,\n responseHeaders: res.headers,\n });\n }\n return parsed;\n}\n"],"mappings":";AAAA,SAAS,qBAA6C;AAetD,SAAS,iBAAAA,gBAAe,uBAAuB;AAbxC,IAAM,8BAAN,cAA0C,cAAc;AAAA,EAC7D,YAAY,MAMT;AACD,UAAM,IAAI;AACV,SAAK,OAAO;AAAA,EACd;AACF;;;ACgCO,IAAM,mBAAmB;;;AC3ChC,eAAsB,QAAQ,OAMb;AACf,QAAM,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,GAAG,MAAM,IAAI,IAAI;AAAA,IACvD,QAAQ,MAAM;AAAA,IACd,SAAS;AAAA,MACP,GAAI,MAAM,SAAS,SAAY,EAAE,gBAAgB,mBAAmB,IAAI,CAAC;AAAA,MACzE,GAAI,MAAM,WAAW,CAAC;AAAA,IACxB;AAAA,IACA,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,KAAK,UAAU,MAAM,IAAI,EAAE,IAAI,CAAC;AAAA,EACzE,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,SAAc,CAAC;AACnB,MAAI;AACF,aAAS,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC;AAAA,EACtC,QAAQ;AAAA,EAIR;AACA,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,QAAQ,SAAS,CAAC;AAC5B,UAAM,IAAI,4BAA4B;AAAA,MACpC,MAAM,EAAE,QAAQ;AAAA,MAChB,SAAS,EAAE,WAAW,uBAAuB,IAAI,MAAM;AAAA,MACvD,YAAY,IAAI;AAAA,MAChB,SAAS,EAAE;AAAA,MACX,iBAAiB,IAAI;AAAA,IACvB,CAAC;AAAA,EACH;AACA,SAAO;AACT;","names":["ThrottleError"]}
|