diff --git a/.changeset/rich-breads-move.md b/.changeset/rich-breads-move.md new file mode 100644 index 00000000000..851f33a0f2e --- /dev/null +++ b/.changeset/rich-breads-move.md @@ -0,0 +1,7 @@ +--- +'@clerk/clerk-js': minor +'@clerk/shared': patch +'@clerk/types': minor +--- + +[Billing Beta] Rename payment source to payment method. diff --git a/packages/clerk-js/src/core/modules/billing/namespace.ts b/packages/clerk-js/src/core/modules/billing/namespace.ts index 6976b653729..86ac5aeadad 100644 --- a/packages/clerk-js/src/core/modules/billing/namespace.ts +++ b/packages/clerk-js/src/core/modules/billing/namespace.ts @@ -28,6 +28,13 @@ import { } from '../../resources/internal'; export class Billing implements BillingNamespace { + static readonly #pathRoot = '/commerce'; + static path(subPath: string, param?: { orgId?: string }): string { + const { orgId } = param || {}; + const prefix = orgId ? `/organizations/${orgId}` : '/me'; + return `${prefix}${Billing.#pathRoot}${subPath}`; + } + getPlans = async (params?: GetPlansParams): Promise> => { const { for: forParam, ...safeParams } = params || {}; const searchParams = { ...safeParams, payer_type: forParam === 'organization' ? 'org' : 'user' }; diff --git a/packages/clerk-js/src/core/modules/billing/payment-source-methods.ts b/packages/clerk-js/src/core/modules/billing/payment-source-methods.ts index 5dd2fc7bbc9..a454aaaabcc 100644 --- a/packages/clerk-js/src/core/modules/billing/payment-source-methods.ts +++ b/packages/clerk-js/src/core/modules/billing/payment-source-methods.ts @@ -1,55 +1,56 @@ import type { - AddPaymentSourceParams, - BillingInitializedPaymentSourceJSON, - BillingPaymentSourceJSON, + AddPaymentMethodParams, + BillingInitializedPaymentMethodJSON, + BillingPaymentMethodJSON, ClerkPaginatedResponse, - GetPaymentSourcesParams, - InitializePaymentSourceParams, + GetPaymentMethodsParams, + InitializePaymentMethodParams, } from '@clerk/types'; import { convertPageToOffsetSearchParams } from '../../../utils/convertPageToOffsetSearchParams'; -import { BaseResource, BillingInitializedPaymentSource, BillingPaymentSource } from '../../resources/internal'; +import { BaseResource, BillingInitializedPaymentMethod, BillingPaymentMethod } from '../../resources/internal'; +import { Billing } from './namespace'; -export const initializePaymentSource = async (params: InitializePaymentSourceParams) => { +const PAYMENT_METHODS_PATH = '/payment_methods'; + +export const initializePaymentMethod = async (params: InitializePaymentMethodParams) => { const { orgId, ...rest } = params; const json = ( await BaseResource._fetch({ - path: orgId - ? `/organizations/${orgId}/commerce/payment_sources/initialize` - : `/me/commerce/payment_sources/initialize`, + path: Billing.path(`${PAYMENT_METHODS_PATH}/initialize`, { orgId }), method: 'POST', body: rest as any, }) - )?.response as unknown as BillingInitializedPaymentSourceJSON; - return new BillingInitializedPaymentSource(json); + )?.response as unknown as BillingInitializedPaymentMethodJSON; + return new BillingInitializedPaymentMethod(json); }; -export const addPaymentSource = async (params: AddPaymentSourceParams) => { +export const addPaymentMethod = async (params: AddPaymentMethodParams) => { const { orgId, ...rest } = params; const json = ( await BaseResource._fetch({ - path: orgId ? `/organizations/${orgId}/commerce/payment_sources` : `/me/commerce/payment_sources`, + path: Billing.path(PAYMENT_METHODS_PATH, { orgId }), method: 'POST', body: rest as any, }) - )?.response as unknown as BillingPaymentSourceJSON; - return new BillingPaymentSource(json); + )?.response as unknown as BillingPaymentMethodJSON; + return new BillingPaymentMethod(json); }; -export const getPaymentSources = async (params: GetPaymentSourcesParams) => { +export const getPaymentMethods = async (params: GetPaymentMethodsParams) => { const { orgId, ...rest } = params; return await BaseResource._fetch({ - path: orgId ? `/organizations/${orgId}/commerce/payment_sources` : `/me/commerce/payment_sources`, + path: Billing.path(PAYMENT_METHODS_PATH, { orgId }), method: 'GET', search: convertPageToOffsetSearchParams(rest), }).then(res => { const { data: paymentSources, total_count } = - res?.response as unknown as ClerkPaginatedResponse; + res?.response as unknown as ClerkPaginatedResponse; return { total_count, - data: paymentSources.map(paymentSource => new BillingPaymentSource(paymentSource)), + data: paymentSources.map(paymentMethod => new BillingPaymentMethod(paymentMethod)), }; }); }; diff --git a/packages/clerk-js/src/core/resources/BillingCheckout.ts b/packages/clerk-js/src/core/resources/BillingCheckout.ts index e4bde5ea84a..3e640287771 100644 --- a/packages/clerk-js/src/core/resources/BillingCheckout.ts +++ b/packages/clerk-js/src/core/resources/BillingCheckout.ts @@ -5,6 +5,7 @@ import type { BillingCheckoutResource, BillingCheckoutTotals, BillingPayerResource, + BillingPaymentMethodResource, BillingSubscriptionPlanPeriod, ConfirmCheckoutParams, } from '@clerk/types'; @@ -13,13 +14,13 @@ import { unixEpochToDate } from '@/utils/date'; import { billingTotalsFromJSON } from '../../utils'; import { BillingPayer } from './BillingPayer'; -import { BaseResource, BillingPaymentSource, BillingPlan } from './internal'; +import { BaseResource, BillingPaymentMethod, BillingPlan } from './internal'; export class BillingCheckout extends BaseResource implements BillingCheckoutResource { id!: string; externalClientSecret!: string; externalGatewayId!: string; - paymentSource?: BillingPaymentSource; + paymentMethod?: BillingPaymentMethodResource; plan!: BillingPlan; planPeriod!: BillingSubscriptionPlanPeriod; planPeriodStart!: number | undefined; @@ -42,7 +43,7 @@ export class BillingCheckout extends BaseResource implements BillingCheckoutReso this.id = data.id; this.externalClientSecret = data.external_client_secret; this.externalGatewayId = data.external_gateway_id; - this.paymentSource = data.payment_source ? new BillingPaymentSource(data.payment_source) : undefined; + this.paymentMethod = data.payment_method ? new BillingPaymentMethod(data.payment_method) : undefined; this.plan = new BillingPlan(data.plan); this.planPeriod = data.plan_period; this.planPeriodStart = data.plan_period_start; diff --git a/packages/clerk-js/src/core/resources/BillingPayment.ts b/packages/clerk-js/src/core/resources/BillingPayment.ts index c9365a5177b..a38c4825e02 100644 --- a/packages/clerk-js/src/core/resources/BillingPayment.ts +++ b/packages/clerk-js/src/core/resources/BillingPayment.ts @@ -2,15 +2,15 @@ import type { BillingMoneyAmount, BillingPaymentChargeType, BillingPaymentJSON, + BillingPaymentMethodResource, BillingPaymentResource, - BillingPaymentSourceResource, BillingPaymentStatus, BillingSubscriptionItemResource, } from '@clerk/types'; import { billingMoneyAmountFromJSON } from '../../utils'; import { unixEpochToDate } from '../../utils/date'; -import { BaseResource, BillingPaymentSource, BillingSubscriptionItem } from './internal'; +import { BaseResource, BillingPaymentMethod, BillingSubscriptionItem } from './internal'; export class BillingPayment extends BaseResource implements BillingPaymentResource { id!: string; @@ -18,7 +18,7 @@ export class BillingPayment extends BaseResource implements BillingPaymentResour failedAt?: Date; paidAt?: Date; updatedAt!: Date; - paymentSource!: BillingPaymentSourceResource; + paymentMethod!: BillingPaymentMethodResource; subscriptionItem!: BillingSubscriptionItemResource; chargeType!: BillingPaymentChargeType; status!: BillingPaymentStatus; @@ -38,7 +38,7 @@ export class BillingPayment extends BaseResource implements BillingPaymentResour this.paidAt = data.paid_at ? unixEpochToDate(data.paid_at) : undefined; this.failedAt = data.failed_at ? unixEpochToDate(data.failed_at) : undefined; this.updatedAt = unixEpochToDate(data.updated_at); - this.paymentSource = new BillingPaymentSource(data.payment_source); + this.paymentMethod = new BillingPaymentMethod(data.payment_method); this.subscriptionItem = new BillingSubscriptionItem(data.subscription_item); this.chargeType = data.charge_type; this.status = data.status; diff --git a/packages/clerk-js/src/core/resources/BillingPaymentSource.ts b/packages/clerk-js/src/core/resources/BillingPaymentSource.ts index 1339dc4db70..f863983fdda 100644 --- a/packages/clerk-js/src/core/resources/BillingPaymentSource.ts +++ b/packages/clerk-js/src/core/resources/BillingPaymentSource.ts @@ -1,9 +1,9 @@ import type { - BillingInitializedPaymentSourceJSON, - BillingInitializedPaymentSourceResource, - BillingPaymentSourceJSON, - BillingPaymentSourceResource, - BillingPaymentSourceStatus, + BillingInitializedPaymentMethodJSON, + BillingInitializedPaymentMethodResource, + BillingPaymentMethodJSON, + BillingPaymentMethodResource, + BillingPaymentMethodStatus, DeletedObjectJSON, MakeDefaultPaymentSourceParams, RemovePaymentSourceParams, @@ -11,29 +11,29 @@ import type { import { BaseResource, DeletedObject } from './internal'; -export class BillingPaymentSource extends BaseResource implements BillingPaymentSourceResource { +export class BillingPaymentMethod extends BaseResource implements BillingPaymentMethodResource { id!: string; last4!: string; - paymentMethod!: string; + paymentType!: 'card' | 'link'; cardType!: string; isDefault!: boolean; isRemovable!: boolean; - status!: BillingPaymentSourceStatus; + status!: BillingPaymentMethodStatus; walletType: string | undefined; - constructor(data: BillingPaymentSourceJSON) { + constructor(data: BillingPaymentMethodJSON) { super(); this.fromJSON(data); } - protected fromJSON(data: BillingPaymentSourceJSON | null): this { + protected fromJSON(data: BillingPaymentMethodJSON | null): this { if (!data) { return this; } this.id = data.id; this.last4 = data.last4; - this.paymentMethod = data.payment_method; + this.paymentType = data.payment_type; this.cardType = data.card_type; this.isDefault = data.is_default; this.isRemovable = data.is_removable; @@ -71,17 +71,17 @@ export class BillingPaymentSource extends BaseResource implements BillingPayment } } -export class BillingInitializedPaymentSource extends BaseResource implements BillingInitializedPaymentSourceResource { +export class BillingInitializedPaymentMethod extends BaseResource implements BillingInitializedPaymentMethodResource { externalClientSecret!: string; externalGatewayId!: string; paymentMethodOrder!: string[]; - constructor(data: BillingInitializedPaymentSourceJSON) { + constructor(data: BillingInitializedPaymentMethodJSON) { super(); this.fromJSON(data); } - protected fromJSON(data: BillingInitializedPaymentSourceJSON | null): this { + protected fromJSON(data: BillingInitializedPaymentMethodJSON | null): this { if (!data) { return this; } diff --git a/packages/clerk-js/src/core/resources/BillingSubscription.ts b/packages/clerk-js/src/core/resources/BillingSubscription.ts index a5550d4c8d8..1c2a5754f18 100644 --- a/packages/clerk-js/src/core/resources/BillingSubscription.ts +++ b/packages/clerk-js/src/core/resources/BillingSubscription.ts @@ -59,7 +59,7 @@ export class BillingSubscription extends BaseResource implements BillingSubscrip export class BillingSubscriptionItem extends BaseResource implements BillingSubscriptionItemResource { id!: string; - paymentSourceId!: string; + paymentMethodId!: string; plan!: BillingPlan; planPeriod!: BillingSubscriptionPlanPeriod; status!: BillingSubscriptionStatus; @@ -86,7 +86,7 @@ export class BillingSubscriptionItem extends BaseResource implements BillingSubs } this.id = data.id; - this.paymentSourceId = data.payment_source_id; + this.paymentMethodId = data.payment_method_id; this.plan = new BillingPlan(data.plan); this.planPeriod = data.plan_period; this.status = data.status; diff --git a/packages/clerk-js/src/core/resources/Organization.ts b/packages/clerk-js/src/core/resources/Organization.ts index 91f7373c4f5..87893f2da61 100644 --- a/packages/clerk-js/src/core/resources/Organization.ts +++ b/packages/clerk-js/src/core/resources/Organization.ts @@ -28,7 +28,7 @@ import type { import { convertPageToOffsetSearchParams } from '../../utils/convertPageToOffsetSearchParams'; import { unixEpochToDate } from '../../utils/date'; -import { addPaymentSource, getPaymentSources, initializePaymentSource } from '../modules/billing'; +import { addPaymentMethod, getPaymentMethods, initializePaymentMethod } from '../modules/billing'; import { BaseResource, OrganizationInvitation, OrganizationMembership } from './internal'; import { OrganizationDomain } from './OrganizationDomain'; import { OrganizationMembershipRequest } from './OrganizationMembershipRequest'; @@ -262,22 +262,22 @@ export class Organization extends BaseResource implements OrganizationResource { }).then(res => new Organization(res?.response as OrganizationJSON)); }; - initializePaymentSource: typeof initializePaymentSource = params => { - return initializePaymentSource({ + initializePaymentMethod: typeof initializePaymentMethod = params => { + return initializePaymentMethod({ ...params, orgId: this.id, }); }; - addPaymentSource: typeof addPaymentSource = params => { - return addPaymentSource({ + addPaymentMethod: typeof addPaymentMethod = params => { + return addPaymentMethod({ ...params, orgId: this.id, }); }; - getPaymentSources: typeof getPaymentSources = params => { - return getPaymentSources({ + getPaymentMethods: typeof getPaymentMethods = params => { + return getPaymentMethods({ ...params, orgId: this.id, }); diff --git a/packages/clerk-js/src/core/resources/User.ts b/packages/clerk-js/src/core/resources/User.ts index 04789175adc..61fa366324c 100644 --- a/packages/clerk-js/src/core/resources/User.ts +++ b/packages/clerk-js/src/core/resources/User.ts @@ -36,7 +36,7 @@ import { unixEpochToDate } from '../../utils/date'; import { normalizeUnsafeMetadata } from '../../utils/resourceParams'; import { getFullName } from '../../utils/user'; import { eventBus, events } from '../events'; -import { addPaymentSource, getPaymentSources, initializePaymentSource } from '../modules/billing'; +import { addPaymentMethod, getPaymentMethods, initializePaymentMethod } from '../modules/billing'; import { BackupCode } from './BackupCode'; import { BaseResource, @@ -291,16 +291,16 @@ export class User extends BaseResource implements UserResource { return new DeletedObject(json); }; - initializePaymentSource: typeof initializePaymentSource = params => { - return initializePaymentSource(params); + initializePaymentMethod: typeof initializePaymentMethod = params => { + return initializePaymentMethod(params); }; - addPaymentSource: typeof addPaymentSource = params => { - return addPaymentSource(params); + addPaymentMethod: typeof addPaymentMethod = params => { + return addPaymentMethod(params); }; - getPaymentSources: typeof getPaymentSources = params => { - return getPaymentSources(params); + getPaymentMethods: typeof getPaymentMethods = params => { + return getPaymentMethods(params); }; get verifiedExternalAccounts() { diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx index 709c285cb3b..77fd27f46fa 100644 --- a/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutComplete.tsx @@ -161,7 +161,7 @@ export const CheckoutComplete = () => { const { setIsOpen } = useDrawerContext(); const { newSubscriptionRedirectUrl } = useCheckoutContext(); const { checkout } = useCheckout(); - const { totals, paymentSource, planPeriodStart, freeTrialEndsAt } = checkout; + const { totals, paymentMethod, planPeriodStart, freeTrialEndsAt } = checkout; const [mousePosition, setMousePosition] = useState({ x: 256, y: 256 }); const prefersReducedMotion = usePrefersReducedMotion(); @@ -438,10 +438,10 @@ export const CheckoutComplete = () => { 0 || freeTrialEndsAt !== null - ? paymentSource - ? paymentSource.paymentMethod !== 'card' - ? `${capitalize(paymentSource.paymentMethod)}` - : `${capitalize(paymentSource.cardType)} ⋯ ${paymentSource.last4}` + ? paymentMethod + ? paymentMethod.paymentType !== 'card' + ? `${capitalize(paymentMethod.paymentType)}` + : `${capitalize(paymentMethod.cardType)} ⋯ ${paymentMethod.last4}` : '–' : planPeriodStart ? formatDate(new Date(planPeriodStart)) diff --git a/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx b/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx index 1178a58dc37..3cb07773b07 100644 --- a/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx @@ -1,5 +1,5 @@ import { __experimental_useCheckout as useCheckout } from '@clerk/shared/react'; -import type { BillingMoneyAmount, BillingPaymentSourceResource, ConfirmCheckoutParams } from '@clerk/types'; +import type { BillingMoneyAmount, BillingPaymentMethodResource, ConfirmCheckoutParams } from '@clerk/types'; import { useMemo, useState } from 'react'; import { Card } from '@/ui/elements/Card'; @@ -23,6 +23,8 @@ type PaymentMethodSource = 'existing' | 'new'; const capitalize = (name: string) => name[0].toUpperCase() + name.slice(1); +const HIDDEN_INPUT_NAME = 'payment_method_id'; + export const CheckoutForm = withCardStateProvider(() => { const { checkout } = useCheckout(); @@ -163,10 +165,10 @@ const useCheckoutMutations = () => { e.preventDefault(); const data = new FormData(e.currentTarget); - const paymentSourceId = data.get('payment_source_id') as string; + const paymentMethodId = data.get(HIDDEN_INPUT_NAME) as string; return confirmCheckout({ - paymentSourceId, + paymentMethodId, }); }; @@ -367,23 +369,23 @@ const ExistingPaymentSourceForm = withCardStateProvider( paymentSources, }: { totalDueNow: BillingMoneyAmount; - paymentSources: BillingPaymentSourceResource[]; + paymentSources: BillingPaymentMethodResource[]; }) => { const submitLabel = useSubmitLabel(); const { checkout } = useCheckout(); - const { paymentSource, isImmediatePlanChange, freeTrialEndsAt } = checkout; + const { paymentMethod, isImmediatePlanChange, freeTrialEndsAt } = checkout; const { payWithExistingPaymentSource } = useCheckoutMutations(); const card = useCardState(); - const [selectedPaymentSource, setSelectedPaymentSource] = useState( - paymentSource || paymentSources.find(p => p.isDefault), + const [selectedPaymentSource, setSelectedPaymentSource] = useState( + paymentMethod || paymentSources.find(p => p.isDefault), ); const options = useMemo(() => { return paymentSources.map(source => { const label = - source.paymentMethod !== 'card' - ? `${capitalize(source.paymentMethod)}` + source.paymentType !== 'card' + ? `${capitalize(source.paymentType)}` : `${capitalize(source.cardType)} ⋯ ${source.last4}`; return { @@ -417,7 +419,7 @@ const ExistingPaymentSourceForm = withCardStateProvider( > {/*Store value inside an input in order to be accessible as form data*/} @@ -439,7 +441,7 @@ const ExistingPaymentSourceForm = withCardStateProvider( ) : ( diff --git a/packages/clerk-js/src/ui/components/Checkout/__tests__/Checkout.test.tsx b/packages/clerk-js/src/ui/components/Checkout/__tests__/Checkout.test.tsx index a7d7071da7e..3b5618a0144 100644 --- a/packages/clerk-js/src/ui/components/Checkout/__tests__/Checkout.test.tsx +++ b/packages/clerk-js/src/ui/components/Checkout/__tests__/Checkout.test.tsx @@ -530,10 +530,10 @@ describe('Checkout', () => { freeTrialDays: 7, freeTrialEnabled: true, }, - paymentSource: { + paymentMethod: { id: 'pm_test_visa', last4: '4242', - paymentMethod: 'card', + paymentType: 'card', cardType: 'visa', isDefault: true, isRemovable: true, @@ -622,10 +622,10 @@ describe('Checkout', () => { freeTrialDays: 7, freeTrialEnabled: true, }, - paymentSource: { + paymentMethod: { id: 'pm_test_visa', last4: '4242', - paymentMethod: 'card', + paymentType: 'card', cardType: 'visa', isDefault: true, isRemovable: true, @@ -665,12 +665,12 @@ describe('Checkout', () => { f.withBilling(); }); - fixtures.clerk.user?.getPaymentSources.mockResolvedValue({ + fixtures.clerk.user?.getPaymentMethods.mockResolvedValue({ data: [ { id: 'pm_test_visa', last4: '4242', - paymentMethod: 'card', + paymentType: 'card', cardType: 'visa', isDefault: true, isRemovable: true, @@ -684,7 +684,7 @@ describe('Checkout', () => { { id: 'pm_test_mastercard', last4: '5555', - paymentMethod: 'card', + paymentType: 'card', cardType: 'mastercard', isDefault: false, isRemovable: true, @@ -791,7 +791,7 @@ describe('Checkout', () => { expect(defaultBadge).toBeVisible(); // Verify the hidden input contains the correct payment source id - const hiddenInput = baseElement.querySelector('input[name="payment_source_id"]'); + const hiddenInput = baseElement.querySelector('input[name="payment_method_id"]'); expect(hiddenInput).toHaveAttribute('value', 'pm_test_visa'); expect(getByRole('button', { name: 'Start free trial' })).toBeInTheDocument(); @@ -804,12 +804,12 @@ describe('Checkout', () => { f.withBilling(); }); - fixtures.clerk.user?.getPaymentSources.mockResolvedValue({ + fixtures.clerk.user?.getPaymentMethods.mockResolvedValue({ data: [ { id: 'pm_test_visa', last4: '4242', - paymentMethod: 'card', + paymentType: 'card', cardType: 'visa', isDefault: true, isRemovable: true, @@ -823,7 +823,7 @@ describe('Checkout', () => { { id: 'pm_test_mastercard', last4: '5555', - paymentMethod: 'card', + paymentType: 'card', cardType: 'mastercard', isDefault: false, isRemovable: true, @@ -930,7 +930,7 @@ describe('Checkout', () => { expect(defaultBadge).toBeVisible(); // Verify the hidden input contains the correct payment source id - const hiddenInput = baseElement.querySelector('input[name="payment_source_id"]'); + const hiddenInput = baseElement.querySelector('input[name="payment_method_id"]'); expect(hiddenInput).toHaveAttribute('value', 'pm_test_visa'); expect(getByRole('button', { name: 'Pay $10.00' })).toBeInTheDocument(); @@ -943,12 +943,12 @@ describe('Checkout', () => { f.withBilling(); }); - fixtures.clerk.user?.getPaymentSources.mockResolvedValue({ + fixtures.clerk.user?.getPaymentMethods.mockResolvedValue({ data: [ { id: 'pm_test_visa', last4: '4242', - paymentMethod: 'card', + paymentType: 'card', cardType: 'visa', isDefault: true, isRemovable: true, @@ -962,7 +962,7 @@ describe('Checkout', () => { { id: 'pm_test_mastercard', last4: '5555', - paymentMethod: 'card', + paymentType: 'card', cardType: 'mastercard', isDefault: false, isRemovable: true, @@ -1065,7 +1065,7 @@ describe('Checkout', () => { ).toBeInTheDocument(); // Verify the hidden input contains the correct payment source id - const hiddenInput = baseElement.querySelector('input[name="payment_source_id"]'); + const hiddenInput = baseElement.querySelector('input[name="payment_method_id"]'); expect(hiddenInput).toHaveAttribute('value', 'pm_test_visa'); expect(queryByRole('button', { name: 'Subscribe' })).toBeInTheDocument(); diff --git a/packages/clerk-js/src/ui/components/PaymentSources/PaymentSourceRow.tsx b/packages/clerk-js/src/ui/components/PaymentSources/PaymentSourceRow.tsx index e97fbf2e37b..7c32781972f 100644 --- a/packages/clerk-js/src/ui/components/PaymentSources/PaymentSourceRow.tsx +++ b/packages/clerk-js/src/ui/components/PaymentSources/PaymentSourceRow.tsx @@ -1,9 +1,9 @@ -import type { BillingPaymentSourceResource } from '@clerk/types'; +import type { BillingPaymentMethodResource } from '@clerk/types'; import { Badge, descriptors, Flex, Icon, localizationKeys, Text } from '../../customizables'; import { CreditCard, GenericPayment } from '../../icons'; -export const PaymentSourceRow = ({ paymentSource }: { paymentSource: BillingPaymentSourceResource }) => { +export const PaymentSourceRow = ({ paymentSource }: { paymentSource: BillingPaymentMethodResource }) => { return ( ({ alignSelf: 'center', color: t.colors.$colorMutedForeground })} elementDescriptor={descriptors.paymentSourceRowIcon} /> @@ -21,7 +21,8 @@ export const PaymentSourceRow = ({ paymentSource }: { paymentSource: BillingPaym truncate elementDescriptor={descriptors.paymentSourceRowType} > - {paymentSource.paymentMethod === 'card' ? paymentSource.cardType : paymentSource.paymentMethod} + {/* TODO(@COMMERCE): Localize this */} + {paymentSource.paymentType === 'card' ? paymentSource.cardType : paymentSource.paymentType} ({ color: t.colors.$colorMutedForeground })} @@ -29,7 +30,7 @@ export const PaymentSourceRow = ({ paymentSource }: { paymentSource: BillingPaym truncate elementDescriptor={descriptors.paymentSourceRowValue} > - {paymentSource.paymentMethod === 'card' ? `⋯ ${paymentSource.last4}` : null} + {paymentSource.paymentType === 'card' ? `⋯ ${paymentSource.last4}` : null} {paymentSource.isDefault && ( void const onAddPaymentSourceSuccess = async (context: { gateway: 'stripe'; paymentToken: string }) => { const resource = subscriberType === 'organization' ? clerk?.organization : clerk.user; - await resource?.addPaymentSource(context); + await resource?.addPaymentMethod(context); onSuccess(); close(); return Promise.resolve(); @@ -54,7 +54,7 @@ const RemoveScreen = ({ paymentSource, revalidate, }: { - paymentSource: BillingPaymentSourceResource; + paymentSource: BillingPaymentMethodResource; revalidate: () => void; }) => { const { close } = useActionContext(); @@ -63,7 +63,7 @@ const RemoveScreen = ({ const { organization } = useOrganization(); const localizationRoot = useSubscriberTypeLocalizationRoot(); const ref = useRef( - `${paymentSource.paymentMethod === 'card' ? paymentSource.cardType : paymentSource.paymentMethod} ${paymentSource.paymentMethod === 'card' ? `⋯ ${paymentSource.last4}` : '-'}`, + `${paymentSource.paymentType === 'card' ? paymentSource.cardType : paymentSource.paymentType} ${paymentSource.paymentType === 'card' ? `⋯ ${paymentSource.last4}` : '-'}`, ); if (!ref.current) { @@ -193,7 +193,7 @@ const PaymentSourceMenu = ({ paymentSource, revalidate, }: { - paymentSource: BillingPaymentSourceResource; + paymentSource: BillingPaymentMethodResource; revalidate: () => void; }) => { const { open } = useActionContext(); diff --git a/packages/shared/src/react/commerce.tsx b/packages/shared/src/react/commerce.tsx index 72fd978f4b1..f9b7b9335da 100644 --- a/packages/shared/src/react/commerce.tsx +++ b/packages/shared/src/react/commerce.tsx @@ -68,13 +68,13 @@ const usePaymentSourceUtils = (forResource: ForPayerType = 'user') => { const resource = forResource === 'organization' ? organization : user; const stripeClerkLibs = useStripeLibsContext(); - const { data: initializedPaymentSource, trigger: initializePaymentSource } = useSWRMutation( + const { data: initializedPaymentMethod, trigger: initializePaymentMethod } = useSWRMutation( { - key: 'billing-payment-source-initialize', + key: 'billing-payment-method-initialize', resourceId: resource?.id, }, () => { - return resource?.initializePaymentSource({ + return resource?.initializePaymentMethod({ gateway: 'stripe', }); }, @@ -86,14 +86,14 @@ const usePaymentSourceUtils = (forResource: ForPayerType = 'user') => { if (!resource?.id) { return; } - initializePaymentSource().catch(() => { + initializePaymentMethod().catch(() => { // ignore errors }); }, [resource?.id]); - const externalGatewayId = initializedPaymentSource?.externalGatewayId; - const externalClientSecret = initializedPaymentSource?.externalClientSecret; - const paymentMethodOrder = initializedPaymentSource?.paymentMethodOrder; + const externalGatewayId = initializedPaymentMethod?.externalGatewayId; + const externalClientSecret = initializedPaymentMethod?.externalClientSecret; + const paymentMethodOrder = initializedPaymentMethod?.paymentMethodOrder; const stripePublishableKey = environment?.commerceSettings.billing.stripePublishableKey; const { data: stripe } = useSWR( @@ -114,7 +114,7 @@ const usePaymentSourceUtils = (forResource: ForPayerType = 'user') => { return { stripe, - initializePaymentSource, + initializePaymentMethod, externalClientSecret, paymentMethodOrder, }; @@ -323,7 +323,7 @@ type UsePaymentElementReturn = { ); const usePaymentElement = (): UsePaymentElementReturn => { - const { isPaymentElementReady, initializePaymentSource } = usePaymentElementContext(); + const { isPaymentElementReady, initializePaymentMethod } = usePaymentElementContext(); const { stripe, elements } = useStripeUtilsContext(); const { externalClientSecret } = usePaymentElementContext(); @@ -363,8 +363,8 @@ const usePaymentElement = (): UsePaymentElementReturn => { return throwLibsMissingError(); } - await initializePaymentSource(); - }, [stripe, elements, initializePaymentSource]); + await initializePaymentMethod(); + }, [stripe, elements, initializePaymentMethod]); const isProviderReady = Boolean(stripe && externalClientSecret); diff --git a/packages/shared/src/react/hooks/__tests__/useCheckout.type.spec.ts b/packages/shared/src/react/hooks/__tests__/useCheckout.type.spec.ts index fcfaa8f00a1..5480591804d 100644 --- a/packages/shared/src/react/hooks/__tests__/useCheckout.type.spec.ts +++ b/packages/shared/src/react/hooks/__tests__/useCheckout.type.spec.ts @@ -144,7 +144,7 @@ describe('useCheckout type tests', () => { | 'isImmediatePlanChange' | 'planPeriod' | 'plan' - | 'paymentSource' + | 'paymentMethod' >; type PropNames = keyof CheckoutProps; @@ -156,7 +156,7 @@ describe('useCheckout type tests', () => { | 'isImmediatePlanChange' | 'planPeriod' | 'plan' - | 'paymentSource' + | 'paymentMethod' >(); }); }); @@ -201,7 +201,7 @@ describe('useCheckout type tests', () => { expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); // Test that the status property is correctly typed expectTypeOf().toEqualTypeOf<'needs_initialization'>(); diff --git a/packages/shared/src/react/hooks/usePaymentMethods.tsx b/packages/shared/src/react/hooks/usePaymentMethods.tsx index 8537f32caa9..2cfb7bbc3c9 100644 --- a/packages/shared/src/react/hooks/usePaymentMethods.tsx +++ b/packages/shared/src/react/hooks/usePaymentMethods.tsx @@ -1,4 +1,4 @@ -import type { BillingPaymentSourceResource, GetPaymentSourcesParams } from '@clerk/types'; +import type { BillingPaymentMethodResource, GetPaymentMethodsParams } from '@clerk/types'; import { useOrganizationContext, useUserContext } from '../contexts'; import { createBillingPaginatedHook } from './createBillingPaginatedHook'; @@ -6,7 +6,7 @@ import { createBillingPaginatedHook } from './createBillingPaginatedHook'; /** * @internal */ -export const usePaymentMethods = createBillingPaginatedHook({ +export const usePaymentMethods = createBillingPaginatedHook({ hookName: 'usePaymentMethods', resourceType: 'commerce-payment-methods', useFetcher: resource => { @@ -14,8 +14,8 @@ export const usePaymentMethods = createBillingPaginatedHook, - ) => Promise; + initializePaymentMethod: ( + params: Exclude, + ) => Promise; /** * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ - addPaymentSource: (params: Exclude) => Promise; + addPaymentMethod: (params: Exclude) => Promise; /** * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ - getPaymentSources: ( - params: Exclude, - ) => Promise>; + getPaymentMethods: ( + params: Exclude, + ) => Promise>; } /** @@ -220,12 +220,13 @@ export interface FeatureResource extends ClerkResource { * The status of a payment source. * @inline */ -export type BillingPaymentSourceStatus = 'active' | 'expired' | 'disconnected'; +export type BillingPaymentMethodStatus = 'active' | 'expired' | 'disconnected'; +// TODO(@COMMERCE): Is expired returned from FAPI ? /** * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ -export type GetPaymentSourcesParams = WithOptionalOrgType; +export type GetPaymentMethodsParams = WithOptionalOrgType; /** * @inline @@ -237,7 +238,7 @@ export type PaymentGateway = 'stripe' | 'paypal'; /** * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ -export type InitializePaymentSourceParams = WithOptionalOrgType<{ +export type InitializePaymentMethodParams = WithOptionalOrgType<{ /** * The payment gateway to use. */ @@ -247,7 +248,7 @@ export type InitializePaymentSourceParams = WithOptionalOrgType<{ /** * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ -export type AddPaymentSourceParams = WithOptionalOrgType<{ +export type AddPaymentMethodParams = WithOptionalOrgType<{ /** * The payment gateway to use. */ @@ -269,11 +270,11 @@ export type RemovePaymentSourceParams = WithOptionalOrgType; export type MakeDefaultPaymentSourceParams = WithOptionalOrgType; /** - * The `BillingPaymentSourceResource` type represents a payment source for a checkout session. + * The `BillingPaymentMethodResource` type represents a payment source for a checkout session. * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ -export interface BillingPaymentSourceResource extends ClerkResource { +export interface BillingPaymentMethodResource extends ClerkResource { /** * The unique identifier for the payment method. */ @@ -283,9 +284,9 @@ export interface BillingPaymentSourceResource extends ClerkResource { */ last4: string; /** - * The type of payment method. For example, `'card'` or `'bank_account'`. + * The type of payment method. For example, `'card'` or `'link'`. */ - paymentMethod: string; + paymentType: 'card' | 'link'; /** * The brand or type of card. For example, `'visa'` or `'mastercard'`. */ @@ -301,7 +302,7 @@ export interface BillingPaymentSourceResource extends ClerkResource { /** * The current status of the payment method. */ - status: BillingPaymentSourceStatus; + status: BillingPaymentMethodStatus; /** * The type of digital wallet, if applicable. For example, `'apple_pay'`, or `'google_pay'`. */ @@ -315,6 +316,7 @@ export interface BillingPaymentSourceResource extends ClerkResource { * @param params - The parameters for the remove operation. * @returns A promise that resolves to a `DeletedObjectResource` object. */ + // TODO: orgId should be implied by the payment method remove: (params?: RemovePaymentSourceParams) => Promise; /** * A function that sets this payment source as the default for the account. Accepts the following parameters: @@ -325,13 +327,14 @@ export interface BillingPaymentSourceResource extends ClerkResource { * @param params - The parameters for the make default operation. * @returns A promise that resolves to `null`. */ + // TODO: orgId should be implied by the payment method makeDefault: (params?: MakeDefaultPaymentSourceParams) => Promise; } /** * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ -export interface BillingInitializedPaymentSourceResource extends ClerkResource { +export interface BillingInitializedPaymentMethodResource extends ClerkResource { /** * A client secret from an external payment provider (such as Stripe) used to complete the payment on the client-side. */ @@ -389,7 +392,7 @@ export interface BillingPaymentResource extends ClerkResource { /** * The payment source being used for the payment, such as credit card or bank account. */ - paymentSource: BillingPaymentSourceResource; + paymentMethod: BillingPaymentMethodResource; /** * The subscription item being paid for. */ @@ -492,7 +495,7 @@ export interface BillingSubscriptionItemResource extends ClerkResource { * The unique identifier for the payment source being used for the subscription item. */ //TODO(@COMMERCE): should this be nullable ? - paymentSourceId: string; + paymentMethodId: string; /** * The plan associated with the subscription item. */ @@ -692,10 +695,10 @@ export type CreateCheckoutParams = WithOptionalOrgType<{ }>; /** - * The `confirm()` method accepts the following parameters. **Only one of `paymentSourceId`, `paymentToken`, or `useTestCard` should be provided.** + * The `confirm()` method accepts the following parameters. **Only one of `paymentMethodId`, `paymentToken`, or `useTestCard` should be provided.** * * @unionReturnHeadings - * ["paymentSourceId", "paymentToken", "useTestCard"] + * ["paymentMethodId", "paymentToken", "useTestCard"] * * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ @@ -704,7 +707,7 @@ export type ConfirmCheckoutParams = /** * The ID of a saved payment source to use for this checkout. */ - paymentSourceId?: string; + paymentMethodId?: string; } | { /** @@ -746,9 +749,9 @@ export interface BillingCheckoutResource extends ClerkResource { */ externalGatewayId: string; /** - * The payment source being used for the checkout, such as a credit card or bank account. + * The payment method being used for the checkout, such as a credit card or bank account. */ - paymentSource?: BillingPaymentSourceResource; + paymentMethod?: BillingPaymentMethodResource; /** * The subscription plan details for the checkout. */ diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts index 9ab16c1540c..fb8a8eb3b9c 100644 --- a/packages/types/src/json.ts +++ b/packages/types/src/json.ts @@ -6,7 +6,7 @@ import type { APIKeysSettingsJSON } from './apiKeysSettings'; import type { BillingPayerResourceType, BillingPaymentChargeType, - BillingPaymentSourceStatus, + BillingPaymentMethodStatus, BillingPaymentStatus, BillingStatementStatus, BillingSubscriptionPlanPeriod, @@ -671,22 +671,22 @@ export interface BillingPlanJSON extends ClerkResourceJSON { /** * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ -export interface BillingPaymentSourceJSON extends ClerkResourceJSON { - object: 'commerce_payment_source'; +export interface BillingPaymentMethodJSON extends ClerkResourceJSON { + object: 'commerce_payment_method'; id: string; last4: string; - payment_method: string; + payment_type: 'card' | 'link'; card_type: string; is_default: boolean; is_removable: boolean; - status: BillingPaymentSourceStatus; + status: BillingPaymentMethodStatus; wallet_type: string | null; } /** * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes. */ -export interface BillingInitializedPaymentSourceJSON extends ClerkResourceJSON { +export interface BillingInitializedPaymentMethodJSON extends ClerkResourceJSON { object: 'commerce_payment_source_initialize'; external_client_secret: string; external_gateway_id: string; @@ -724,7 +724,7 @@ export interface BillingPaymentJSON extends ClerkResourceJSON { paid_at?: number; failed_at?: number; updated_at: number; - payment_source: BillingPaymentSourceJSON; + payment_method: BillingPaymentMethodJSON; subscription: BillingSubscriptionItemJSON; subscription_item: BillingSubscriptionItemJSON; charge_type: BillingPaymentChargeType; @@ -741,7 +741,7 @@ export interface BillingSubscriptionItemJSON extends ClerkResourceJSON { credit?: { amount: BillingMoneyAmountJSON; }; - payment_source_id: string; + payment_method_id: string; plan: BillingPlanJSON; plan_period: BillingSubscriptionPlanPeriod; status: BillingSubscriptionStatus; @@ -818,7 +818,7 @@ export interface BillingCheckoutJSON extends ClerkResourceJSON { id: string; external_client_secret: string; external_gateway_id: string; - payment_source?: BillingPaymentSourceJSON; + payment_method?: BillingPaymentMethodJSON; plan: BillingPlanJSON; plan_period: BillingSubscriptionPlanPeriod; plan_period_start?: number; diff --git a/packages/types/src/organization.ts b/packages/types/src/organization.ts index 7b1b4c6eb9d..7d1693438d9 100644 --- a/packages/types/src/organization.ts +++ b/packages/types/src/organization.ts @@ -1,4 +1,4 @@ -import type { BillingPaymentSourceMethods } from './billing'; +import type { BillingPayerMethods } from './billing'; import type { OrganizationDomainResource, OrganizationEnrollmentMode } from './organizationDomain'; import type { OrganizationInvitationResource, OrganizationInvitationStatus } from './organizationInvitation'; import type { OrganizationCustomRoleKey, OrganizationMembershipResource } from './organizationMembership'; @@ -35,7 +35,7 @@ declare global { * * @interface */ -export interface OrganizationResource extends ClerkResource, BillingPaymentSourceMethods { +export interface OrganizationResource extends ClerkResource, BillingPayerMethods { id: string; name: string; slug: string | null; diff --git a/packages/types/src/user.ts b/packages/types/src/user.ts index 2cfd6910f1b..951735d88c6 100644 --- a/packages/types/src/user.ts +++ b/packages/types/src/user.ts @@ -1,5 +1,5 @@ import type { BackupCodeResource } from './backupCode'; -import type { BillingPaymentSourceMethods } from './billing'; +import type { BillingPayerMethods } from './billing'; import type { DeletedObjectResource } from './deletedObject'; import type { EmailAddressResource } from './emailAddress'; import type { EnterpriseAccountResource } from './enterpriseAccount'; @@ -61,7 +61,7 @@ declare global { * * The ClerkJS SDK provides some helper [methods](#methods) on the `User` object to help retrieve and update user information and authentication status. */ -export interface UserResource extends ClerkResource, BillingPaymentSourceMethods { +export interface UserResource extends ClerkResource, BillingPayerMethods { id: string; externalId: string | null; primaryEmailAddressId: string | null;