From c639fe0435c461f94b8ce276e253182bc01ed187 Mon Sep 17 00:00:00 2001 From: Arman Date: Fri, 30 Aug 2024 10:07:07 +0200 Subject: [PATCH] feat: more refactors --- src/lib/sdk/billing.ts | 35 +++++++++-- src/lib/stores/billing.ts | 20 ++++++ src/lib/stores/organization.ts | 1 + .../(console)/apply-credit/+page.svelte | 62 ++++++++++++++----- .../create-organization/+page.svelte | 38 +++++++++--- .../change-plan/+page.svelte | 61 +++++++++--------- 6 files changed, 156 insertions(+), 61 deletions(-) diff --git a/src/lib/sdk/billing.ts b/src/lib/sdk/billing.ts index 0612399671..8229dc38d3 100644 --- a/src/lib/sdk/billing.ts +++ b/src/lib/sdk/billing.ts @@ -309,7 +309,9 @@ export class Billing { paymentMethodId: string, billingAddressId: string = undefined, invites: string[] = undefined, - couponId: string = undefined + couponId: string = undefined, + taxId: string = undefined, + billingBudget: number = undefined ): Promise { const path = `/organizations`; const params = { @@ -319,7 +321,9 @@ export class Billing { paymentMethodId, billingAddressId, invites, - couponId + couponId, + taxId, + billingBudget }; const uri = new URL(this.client.config.endpoint + path); return await this.client.call( @@ -368,14 +372,37 @@ export class Billing { organizationId: string, billingPlan: string, paymentMethodId: string, - billingAddressId: string = undefined + billingAddressId: string = undefined, + invites: string[] = undefined, + couponId: string = undefined, + taxId: string = undefined, + billingBudget: number = undefined ): Promise { const path = `/organizations/${organizationId}/plan`; const params = { organizationId, billingPlan, paymentMethodId, - billingAddressId + billingAddressId, + invites, + couponId, + taxId, + billingBudget + }; + const uri = new URL(this.client.config.endpoint + path); + return await this.client.call( + 'patch', + uri, + { + 'content-type': 'application/json' + }, + params + ); + } + async validateOrganization(organizationId: string): Promise { + const path = `/organizations/${organizationId}/validate`; + const params = { + organizationId }; const uri = new URL(this.client.config.endpoint + path); return await this.client.call( diff --git a/src/lib/stores/billing.ts b/src/lib/stores/billing.ts index aba284f215..8e2efd31dd 100644 --- a/src/lib/stores/billing.ts +++ b/src/lib/stores/billing.ts @@ -438,3 +438,23 @@ export function calculateResourceSurplus(total: number, limit: number, limitUnit const realLimit = (limitUnit ? sizeToBytes(limit, limitUnit) : limit) || Infinity; return total > realLimit ? total - realLimit : 0; } + +export function buildOrgRedirectURL( + base: string, + name: string, + plan: string, + paymentMethodId: string, + id: string, + collaborators: string[] = null, + coupon: string = null +) { + const url = new URL(base); + url.searchParams.append('name', name); + url.searchParams.append('plan', plan); + url.searchParams.append('paymentMethod', paymentMethodId); + url.searchParams.append('validate', id); + if (collaborators?.length) url.searchParams.append('collaborators', collaborators.join(',')); + if (coupon) url.searchParams.append('coupon', coupon); + + return url.toString(); +} diff --git a/src/lib/stores/organization.ts b/src/lib/stores/organization.ts index f0f363d56f..6dd86828ae 100644 --- a/src/lib/stores/organization.ts +++ b/src/lib/stores/organization.ts @@ -20,6 +20,7 @@ export type Organization = Models.Team> & { amount: number; billingTaxId?: string; billingPlanDowngrade?: Tier; + client_secret?: string; }; export type OrganizationList = { diff --git a/src/routes/(console)/apply-credit/+page.svelte b/src/routes/(console)/apply-credit/+page.svelte index 91ea2eddf6..f656e2ce02 100644 --- a/src/routes/(console)/apply-credit/+page.svelte +++ b/src/routes/(console)/apply-credit/+page.svelte @@ -18,10 +18,12 @@ } from '$lib/layout'; import { type PaymentList } from '$lib/sdk/billing'; import { app } from '$lib/stores/app'; + import { buildOrgRedirectURL } from '$lib/stores/billing.js'; import { campaigns } from '$lib/stores/campaigns'; import { addNotification } from '$lib/stores/notifications'; import { organizationList, type Organization } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; + import { confirmPayment } from '$lib/stores/stripe.js'; import { ID } from '@appwrite.io/console'; import { onMount } from 'svelte'; import { writable } from 'svelte/store'; @@ -83,6 +85,19 @@ if (campaign?.plan) { billingPlan = campaign.plan; } + if ($page.url.searchParams.has('name')) { + name = $page.url.searchParams.get('name'); + } + if ($page.url.searchParams.has('paymentMethod')) { + paymentMethodId = $page.url.searchParams.get('paymentMethod'); + } + if ($page.url.searchParams.has('collaborators')) { + collaborators = $page.url.searchParams.get('collaborators')?.split(',') ?? []; + } + if ($page.url.searchParams.has('validate')) { + const id = $page.url.searchParams.get('validate'); + await sdk.forConsole.billing.validateOrganization(id); + } }); async function loadPaymentMethods() { @@ -106,9 +121,11 @@ name, billingPlan, paymentMethodId, - undefined, - collaborators?.length ? collaborators : undefined, - couponData?.code ? couponData.code : undefined + null, + collaborators?.length ? collaborators : null, + couponData?.code ? couponData.code : null, + taxId ?? null, + billingBudget ?? null ); } // Upgrade existing org @@ -117,20 +134,20 @@ selectedOrg.$id, billingPlan, paymentMethodId, - undefined, - collaborators?.length ? collaborators : undefined, - couponData?.code ? couponData.code : undefined + null, + collaborators?.length ? collaborators : null, + couponData?.code ? couponData.code : null, + taxId ?? null, + billingBudget ?? null ); } // Existing pro org else { org = selectedOrg; - // Add coupon if (couponData?.code) { await sdk.forConsole.billing.addCredit(org.$id, couponData.code); } - // Add collaborators if (collaborators?.length) { collaborators.forEach(async (collaborator) => { await sdk.forConsole.teams.createMembership( @@ -143,17 +160,32 @@ ); }); } - } - // Add budget - if (billingBudget) { - await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); + if (billingBudget) { + await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); + } + + if (taxId) { + await sdk.forConsole.billing.updateTaxId(org.$id, taxId); + } } - // Add tax ID - if (taxId) { - await sdk.forConsole.billing.updateTaxId(org.$id, taxId); + // Confirm payment if required + if (org?.client_secret) { + let redirectURL = buildOrgRedirectURL( + `${base}/apply-credit`, + name, + billingPlan, + paymentMethodId, + org.$id, + collaborators, + couponData?.code + ); + await confirmPayment(org.$id, org.client_secret, paymentMethodId, redirectURL); + + await sdk.forConsole.billing.validateOrganization(org.$id); } + trackEvent(Submit.CreditRedeem, { coupon: couponData.code, campaign: couponData?.campaign diff --git a/src/routes/(console)/create-organization/+page.svelte b/src/routes/(console)/create-organization/+page.svelte index 43faab86d9..6685d60168 100644 --- a/src/routes/(console)/create-organization/+page.svelte +++ b/src/routes/(console)/create-organization/+page.svelte @@ -18,10 +18,11 @@ WizardSecondaryFooter } from '$lib/layout'; import type { Coupon, PaymentList } from '$lib/sdk/billing'; - import { tierToPlan } from '$lib/stores/billing'; + import { buildOrgRedirectURL, tierToPlan } from '$lib/stores/billing'; import { addNotification } from '$lib/stores/notifications'; import { organizationList, type Organization } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; + import { confirmPayment } from '$lib/stores/stripe'; import { ID } from '@appwrite.io/console'; import { onMount } from 'svelte'; import { writable } from 'svelte/store'; @@ -78,6 +79,16 @@ billingPlan = plan as BillingPlan; } } + if ($page.url.searchParams.has('paymentMethod')) { + paymentMethodId = $page.url.searchParams.get('paymentMethod'); + } + if ($page.url.searchParams.has('collaborators')) { + collaborators = $page.url.searchParams.get('collaborators')?.split(',') ?? []; + } + if ($page.url.searchParams.has('validate')) { + const id = $page.url.searchParams.get('validate'); + await sdk.forConsole.billing.validateOrganization(id); + } if (anyOrgFree) { billingPlan = BillingPlan.PRO; } @@ -118,18 +129,25 @@ billingPlan, paymentMethodId, null, - collaborators?.length ? collaborators : undefined, - couponData?.code + collaborators?.length ? collaborators : null, + couponData?.code ?? null, + taxId ?? null, + billingBudget ?? null ); - //Add budget - if (billingBudget) { - await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); - } + if (org?.client_secret) { + let redirectURL = buildOrgRedirectURL( + `${base}/create-organization`, + name, + billingPlan, + paymentMethodId, + org.$id, + collaborators, + couponData?.code + ); + await confirmPayment(org.$id, org.client_secret, paymentMethodId, redirectURL); - // Add tax ID - if (taxId) { - await sdk.forConsole.billing.updateTaxId(org.$id, taxId); + await sdk.forConsole.billing.validateOrganization(org.$id); } } diff --git a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte index 705b96abe6..60cb29ec78 100644 --- a/src/routes/(console)/organization-[organization]/change-plan/+page.svelte +++ b/src/routes/(console)/organization-[organization]/change-plan/+page.svelte @@ -29,10 +29,11 @@ WizardSecondaryFooter } from '$lib/layout'; import { type Coupon, type PaymentList } from '$lib/sdk/billing'; - import { plansInfo, tierToPlan, type Tier } from '$lib/stores/billing'; + import { buildOrgRedirectURL, plansInfo, tierToPlan, type Tier } from '$lib/stores/billing'; import { addNotification } from '$lib/stores/notifications'; import { organization, organizationList, type Organization } from '$lib/stores/organization'; import { sdk } from '$lib/stores/sdk'; + import { confirmPayment } from '$lib/stores/stripe.js'; import { user } from '$lib/stores/user'; import { VARS } from '$lib/system'; import { onMount } from 'svelte'; @@ -98,6 +99,17 @@ } else { billingPlan = BillingPlan.PRO; } + + if ($page.url.searchParams.has('paymentMethod')) { + paymentMethodId = $page.url.searchParams.get('paymentMethod'); + } + if ($page.url.searchParams.has('collaborators')) { + collaborators = $page.url.searchParams.get('collaborators')?.split(',') ?? []; + } + if ($page.url.searchParams.has('validate')) { + const id = $page.url.searchParams.get('validate'); + await sdk.forConsole.billing.validateOrganization(id); + } }); async function loadPaymentMethods() { @@ -172,41 +184,26 @@ $organization.$id, billingPlan, paymentMethodId, - null + null, + collaborators?.length ? collaborators : null, + couponData?.code ? couponData.code : null, + taxId ?? null, + billingBudget ?? null ); - //Add coupon - if (couponData?.code) { - await sdk.forConsole.billing.addCredit(org.$id, couponData.code); - trackEvent(Submit.CreditRedeem); - } - - //Add budget - if (billingBudget) { - await sdk.forConsole.billing.updateBudget(org.$id, billingBudget, [75]); - } - - //Add collaborators - if (collaborators?.length) { - const newCollaborators = collaborators.filter( - (collaborator) => - !data?.members?.memberships?.find((m) => m.userEmail === collaborator) + if (org?.client_secret) { + let redirectURL = buildOrgRedirectURL( + `${base}/create-organization`, + org.name, + billingPlan, + paymentMethodId, + org.$id, + collaborators, + couponData?.code ); - newCollaborators.forEach(async (collaborator) => { - await sdk.forConsole.teams.createMembership( - org.$id, - ['owner'], - collaborator, - undefined, - undefined, - `${$page.url.origin}/${base}/organization-${org.$id}` - ); - }); - } + await confirmPayment(org.$id, org.client_secret, paymentMethodId, redirectURL); - //Add tax ID - if (taxId) { - await sdk.forConsole.billing.updateTaxId(org.$id, taxId); + await sdk.forConsole.billing.validateOrganization(org.$id); } await invalidate(Dependencies.ACCOUNT);