From 6ee7fa6aa1c221f1b4755584555bbda245ef3f0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Gr=C3=BCneberg?= Date: Mon, 3 Jun 2024 20:50:11 +0800 Subject: [PATCH] chore: adopt to new endpoint to mark payment as default --- .../ChangePaymentMethodModal.tsx | 23 ++----- .../PaymentMethods/PaymentMethods.tsx | 25 ++++--- ...ization-payment-method-default-mutation.ts | 65 +++++++++++++++++++ packages/api-types/types/api.d.ts | 33 ++++++++++ 4 files changed, 115 insertions(+), 31 deletions(-) create mode 100644 apps/studio/data/organizations/organization-payment-method-default-mutation.ts diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/ChangePaymentMethodModal.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/ChangePaymentMethodModal.tsx index 926df400a99..1f4e517b8aa 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/ChangePaymentMethodModal.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/ChangePaymentMethodModal.tsx @@ -2,10 +2,8 @@ import toast from 'react-hot-toast' import { useParams } from 'common' import type { OrganizationPaymentMethod } from 'data/organizations/organization-payment-methods-query' -import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' -import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation' -import type { SubscriptionTier } from 'data/subscriptions/types' import { Button, Modal } from 'ui' +import { useOrganizationPaymentMethodMarkAsDefaultMutation } from 'data/organizations/organization-payment-method-default-mutation' export interface ChangePaymentMethodModalProps { selectedPaymentMethod?: OrganizationPaymentMethod @@ -17,9 +15,8 @@ const ChangePaymentMethodModal = ({ onClose, }: ChangePaymentMethodModalProps) => { const { slug } = useParams() - const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: slug }) - const { mutate: updateOrgSubscription, isLoading: isUpdating } = useOrgSubscriptionUpdateMutation( - { + const { mutate: markAsDefault, isLoading: isUpdating } = + useOrganizationPaymentMethodMarkAsDefaultMutation({ onSuccess: () => { toast.success( `Successfully changed payment method to the card ending with ${ @@ -31,23 +28,15 @@ const ChangePaymentMethodModal = ({ onError: (error) => { toast.error(`Failed to change payment method: ${error.message}`) }, - } - ) + }) const onConfirmUpdate = async () => { if (!slug) return console.error('Slug is required') - if (!subscription) return console.error('Subscription is required') if (!selectedPaymentMethod) return console.error('Card ID is required') - const selectedTier = - subscription.plan.id === 'pro' && subscription.usage_billing_enabled - ? 'tier_payg' - : `tier_${subscription.plan.id}` - - updateOrgSubscription({ + markAsDefault({ slug, - tier: selectedTier as SubscriptionTier, - paymentMethod: selectedPaymentMethod.id, + paymentMethodId: selectedPaymentMethod.id, }) } diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/PaymentMethods.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/PaymentMethods.tsx index 6ff7ab6dcc0..450e8ce5519 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/PaymentMethods.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/PaymentMethods.tsx @@ -177,20 +177,17 @@ const PaymentMethods = () => { - {subscription?.plan.id !== 'free' && - subscription?.payment_method_type === 'card' && ( - <> - - setSelectedMethodForUse(paymentMethod) - } - > -

Use this card

-
- - - )} + { + <> + setSelectedMethodForUse(paymentMethod)} + > +

Use this card

+
+ + + } setSelectedMethodToDelete(paymentMethod)} diff --git a/apps/studio/data/organizations/organization-payment-method-default-mutation.ts b/apps/studio/data/organizations/organization-payment-method-default-mutation.ts new file mode 100644 index 00000000000..7b05a0ad7e2 --- /dev/null +++ b/apps/studio/data/organizations/organization-payment-method-default-mutation.ts @@ -0,0 +1,65 @@ +import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' +import { toast } from 'react-hot-toast' +import { put, handleError } from 'data/fetchers' +import type { ResponseError } from 'types' +import { organizationKeys } from './keys' + +export type OrganizationPaymentMethodDefaultVariables = { + slug: string + paymentMethodId: string +} + +export async function markPaymentMethodAsDefault({ + slug, + paymentMethodId, +}: OrganizationPaymentMethodDefaultVariables) { + if (!slug) throw new Error('Slug is required') + + const { data, error } = await put(`/platform/organizations/{slug}/payments/default`, { + body: { payment_method_id: paymentMethodId }, + params: { + path: { + slug, + }, + }, + }) + if (error) throw handleError(error) + return data +} + +type OrganizationPaymentMethodDefaultData = Awaited> + +export const useOrganizationPaymentMethodMarkAsDefaultMutation = ({ + onSuccess, + onError, + ...options +}: Omit< + UseMutationOptions< + OrganizationPaymentMethodDefaultData, + ResponseError, + OrganizationPaymentMethodDefaultVariables + >, + 'mutationFn' +> = {}) => { + const queryClient = useQueryClient() + + return useMutation< + OrganizationPaymentMethodDefaultData, + ResponseError, + OrganizationPaymentMethodDefaultVariables + >((vars) => markPaymentMethodAsDefault(vars), { + async onSuccess(data, variables, context) { + const { slug } = variables + await queryClient.invalidateQueries(organizationKeys.paymentMethods(slug)) + await onSuccess?.(data, variables, context) + }, + async onError(data, variables, context) { + if (onError === undefined) { + toast.error(`Failed to mark payment method as default: ${data.message}`) + } else { + onError(data, variables, context) + } + }, + ...options, + }) +} diff --git a/packages/api-types/types/api.d.ts b/packages/api-types/types/api.d.ts index e8c663dc5cc..73dc652c627 100644 --- a/packages/api-types/types/api.d.ts +++ b/packages/api-types/types/api.d.ts @@ -267,6 +267,10 @@ export interface paths { /** Detach Stripe payment method with the given card ID */ delete: operations['PaymentsController_detachPaymentMethod'] } + '/platform/organizations/{slug}/payments/default': { + /** Mark given paymnet method as default for organization */ + put: operations['PaymentsController_markPaymentMethodAsDefault'] + } '/platform/organizations/{slug}/payments/setup-intent': { /** Sets up a payment method */ post: operations['SetupIntentOrgController_setUpPaymentMethod'] @@ -2925,6 +2929,9 @@ export interface components { stripeAccount?: string } } + MarkDefaultPaymentMethodBody: { + payment_method_id: string + } HCaptchaBody: { hcaptchaToken: string } @@ -7588,6 +7595,32 @@ export interface operations { } } } + /** Mark given paymnet method as default for organization */ + PaymentsController_markPaymentMethodAsDefault: { + parameters: { + path: { + /** @description Organization slug */ + slug: string + } + } + requestBody: { + content: { + 'application/json': components['schemas']['MarkDefaultPaymentMethodBody'] + } + } + responses: { + 200: { + content: never + } + 403: { + content: never + } + /** @description Failed to mark payment method as default */ + 500: { + content: never + } + } + } /** Sets up a payment method */ SetupIntentOrgController_setUpPaymentMethod: { parameters: {