Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ef1435c
remove: hardcoded `Plan` type and use billing models from sdk.
ItzNotABug Dec 18, 2025
c462360
remove: credits manual type.
ItzNotABug Dec 18, 2025
c3a5a3a
remove: paymentMethodList & paymentMethodData manual types.
ItzNotABug Dec 18, 2025
36f11f3
remove: `invoice`, `invoiceList` and `estimationDeleteOrganization` m…
ItzNotABug Dec 18, 2025
2f7bb0c
remove: `Tier` type, ugh!
ItzNotABug Dec 18, 2025
ce1c088
remove: `Estimation` type!
ItzNotABug Dec 18, 2025
f2fd84b
remove: `Coupon` type!
ItzNotABug Dec 18, 2025
c18713e
remove: `Campaign` type!
ItzNotABug Dec 18, 2025
387fca1
remove: `AggregationTeam` type!
ItzNotABug Dec 18, 2025
183f821
remove: `AggregationBreakdown`, `InvoiceUsage` and `AggregationList` …
ItzNotABug Dec 18, 2025
38b814a
remove: `OrganizationUsage` type.
ItzNotABug Dec 18, 2025
8aad7cf
remove: `Address` and `AddressList` type.
ItzNotABug Dec 18, 2025
facf695
remove: `Roles` type.
ItzNotABug Dec 18, 2025
529d8b1
replace: `Organization` type with `Models.Organizations`.
ItzNotABug Dec 18, 2025
67dff43
clear: tests.
ItzNotABug Dec 18, 2025
a7b7b77
remove: `validateOrganization`.
ItzNotABug Dec 18, 2025
b4e08b5
remove: `estimationCreateOrganization`, `deleteOrganization`, `estima…
ItzNotABug Dec 18, 2025
8382911
remove: `listPlans`.
ItzNotABug Dec 19, 2025
60af829
remove: `getPlan`.
ItzNotABug Dec 19, 2025
dddadc3
remove: projects, plans, payment methods hardcoded methods.
ItzNotABug Dec 19, 2025
91aaa34
remove: invoices, payments, usage, aggregation hardcoded methods.
ItzNotABug Dec 19, 2025
9a0059c
remove: credits hardcoded methods.
ItzNotABug Dec 19, 2025
3b14de3
remove: campaigns hardcoded methods.
ItzNotABug Dec 19, 2025
4216f3b
remove: `setMembership` hardcoded methods.
ItzNotABug Dec 19, 2025
0597d21
refactor: remove `getCouponAccount` hardcoded method and its usages.
ItzNotABug Dec 19, 2025
7fc29ae
refactor: remove `billingAddress`, `tax`, `orgPayment` hardcoded meth…
ItzNotABug Dec 19, 2025
b3e040b
refactor: remove `accounts`, `console`, `regions` based hardcoded met…
ItzNotABug Dec 19, 2025
7710e55
Merge branch 'main' into billing-sdk-refactor
ItzNotABug Dec 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
},
"dependencies": {
"@ai-sdk/svelte": "^1.1.24",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@9b32107",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@7a147b9",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc",
"@appwrite.io/pink-legacy": "^1.0.3",
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 8 additions & 10 deletions src/lib/commandCenter/searchers/organizations.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import { resolve } from '$app/paths';
import { goto } from '$app/navigation';
import { base } from '$app/paths';
import { sdk } from '$lib/stores/sdk';
import type { Searcher } from '../commands';
import { isCloud } from '$lib/system';
import { Platform, Query } from '@appwrite.io/console';
import { getTeamOrOrganizationList } from '$lib/stores/organization';

export const orgSearcher = (async (query: string) => {
const { teams } = !isCloud
? await sdk.forConsole.teams.list()
: await sdk.forConsole.billing.listOrganization([
Query.equal('platform', Platform.Appwrite)
]);
const { teams } = await getTeamOrOrganizationList();

return teams
.filter((organization) => organization.name.toLowerCase().includes(query.toLowerCase()))
.map((organization) => {
return {
label: organization.name,
callback: () => {
goto(`${base}/organization-${organization.$id}`);
goto(
resolve('/(console)/organization-[organization]', {
organization: organization.$id
})
);
},
group: 'organizations'
} as const;
Expand Down
6 changes: 2 additions & 4 deletions src/lib/components/archiveProject.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,12 @@
import { isSmallViewport } from '$lib/stores/viewport';
import { isCloud } from '$lib/system';
import { regions as regionsStore } from '$lib/stores/organization';
import type { Organization } from '$lib/stores/organization';
import type { Plan } from '$lib/sdk/billing';

// props
interface Props {
currentPlan: Models.BillingPlan;
organization: Models.Organization;
projectsToArchive: Models.Project[];
organization: Organization;
currentPlan: Plan;
}

let { projectsToArchive, organization, currentPlan }: Props = $props();
Expand Down
11 changes: 8 additions & 3 deletions src/lib/components/billing/alerts/limitReached.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@
import { BillingPlan } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { HeaderAlert } from '$lib/layout';
import { hideBillingHeaderRoutes, readOnly, tierToPlan, upgradeURL } from '$lib/stores/billing';
import {
hideBillingHeaderRoutes,
readOnly,
billingIdToPlan,
upgradeURL
} from '$lib/stores/billing';
import { organization } from '$lib/stores/organization';
</script>

{#if $organization?.$id && $organization?.billingPlan === BillingPlan.FREE && $readOnly && !hideBillingHeaderRoutes.includes(page.url.pathname)}
<HeaderAlert
type="error"
title={`${$organization.name} usage has reached the ${tierToPlan($organization.billingPlan).name} plan limit`}>
title={`${$organization.name} usage has reached the ${billingIdToPlan($organization.billingPlan).name} plan limit`}>
<svelte:fragment>
Usage for the <b>{$organization.name}</b> organization has reached the limits of the {tierToPlan(
Usage for the <b>{$organization.name}</b> organization has reached the limits of the {billingIdToPlan(
$organization.billingPlan
).name}
plan. Consider upgrading to increase your resource usage.
Expand Down
7 changes: 3 additions & 4 deletions src/lib/components/billing/alerts/paymentMandate.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
import { confirmSetup } from '$lib/stores/stripe';

async function verifyPaymentMethod() {
const method = await sdk.forConsole.billing.setupPaymentMandate(
$organization.$id,
$paymentMissingMandate.$id
);
const method = await sdk.forConsole.account.updatePaymentMethodMandateOptions({
paymentMethodId: $paymentMissingMandate.$id
});
await confirmSetup(method.clientSecret, method.providerMethodId);
}
</script>
Expand Down
8 changes: 4 additions & 4 deletions src/lib/components/billing/alerts/selectProjectCloud.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@

async function updateSelected() {
try {
await sdk.forConsole.billing.updateSelectedProjects(
projects[0].teamId,
selectedProjects
);
await sdk.forConsole.organizations.updateProjects({
organizationId: projects[0].teamId,
projects: selectedProjects
});

showSelectProject = false;
invalidate(Dependencies.ORGANIZATION);
Expand Down
16 changes: 9 additions & 7 deletions src/lib/components/billing/couponInput.svelte
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
<script lang="ts">
import { Button, InputText } from '$lib/elements/forms';
import { formatCurrency } from '$lib/helpers/numbers';
import type { Coupon } from '$lib/sdk/billing';
import { sdk } from '$lib/stores/sdk';
import { Layout } from '@appwrite.io/pink-svelte';
import { createEventDispatcher } from 'svelte';
import { Layout } from '@appwrite.io/pink-svelte';
import type { Models } from '@appwrite.io/console';
import { formatCurrency } from '$lib/helpers/numbers';
import { Button, InputText } from '$lib/elements/forms';

const dispatch = createEventDispatcher();

export let required = false;
export let coupon: string = '';
export let couponData: Partial<Coupon> = {
export let couponData: Partial<Models.Coupon> = {
code: null,
status: null,
credits: null
};

async function addCoupon() {
try {
const response = await sdk.forConsole.billing.getCouponAccount(coupon);
couponData = response;
couponData = await sdk.forConsole.account.getCoupon({
couponId: coupon
});

dispatch('validation', couponData);
coupon = null;
} catch (error) {
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/billing/creditsApplied.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<script lang="ts">
import { Button } from '$lib/elements/forms';
import type { Coupon } from '$lib/sdk/billing';
import type { Models } from '@appwrite.io/console';
import { formatCurrency } from '$lib/helpers/numbers';
import { IconTag, IconX } from '@appwrite.io/pink-icons-svelte';
import { Badge, Icon, Layout, Tooltip, Typography } from '@appwrite.io/pink-svelte';

export let couponData: Partial<Coupon> = {
export let couponData: Partial<Models.Coupon> = {
code: null,
status: null,
credits: null
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/billing/discountsApplied.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<script lang="ts">
import type { Coupon } from '$lib/sdk/billing';
import type { Models } from '@appwrite.io/console';
import { formatCurrency } from '$lib/helpers/numbers';
import { IconTag } from '@appwrite.io/pink-icons-svelte';
import { Badge, Icon, Layout, Typography } from '@appwrite.io/pink-svelte';

export let label: string;
export let value: number;
export let couponData: Partial<Coupon> = {
export let couponData: Partial<Models.Coupon> = {
code: null,
status: null,
credits: null
Expand Down
30 changes: 21 additions & 9 deletions src/lib/components/billing/emptyCardCloud.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
<script lang="ts">
import { Click, trackEvent } from '$lib/actions/analytics';
import { BillingPlan } from '$lib/constants';
import { Card } from '..';
import type { Snippet } from 'svelte';
import { Button } from '$lib/elements/forms';
import { tierToPlan, upgradeURL } from '$lib/stores/billing';
import { BillingPlanGroup } from '@appwrite.io/console';
import { Click, trackEvent } from '$lib/actions/analytics';
import { Layout, Typography } from '@appwrite.io/pink-svelte';
import { Card } from '..';
import { getBasePlanFromGroup, upgradeURL } from '$lib/stores/billing';

let {
service,
eventSource,
children = null
}: {
service: string;
eventSource: string;
children?: Snippet;
} = $props();

export let service: string;
export let eventSource: string;
const proPlanName = getBasePlanFromGroup(BillingPlanGroup.Pro).name;
</script>

<Card>
<slot>
{#if children}
{@render children()}
{:else}
<Layout.Stack alignItems="center">
<Typography.Text variant="m-600">Upgrade to add {service}</Typography.Text>
<Typography.Text>
Upgrade to a {tierToPlan(BillingPlan.PRO).name} plan to add {service} to your organization
Upgrade to a {proPlanName} plan to add {service} to your organization
</Typography.Text>

<Button
Expand All @@ -31,5 +43,5 @@
Upgrade
</Button>
</Layout.Stack>
</slot>
{/if}
</Card>
33 changes: 17 additions & 16 deletions src/lib/components/billing/estimatedTotalBox.svelte
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
<script lang="ts">
import { InputChoice, InputNumber } from '$lib/elements/forms';
import { formatCurrency } from '$lib/helpers/numbers';
import type { Coupon, Estimation } from '$lib/sdk/billing';
import { type Tier } from '$lib/stores/billing';
import { Card, Divider, Layout, Typography } from '@appwrite.io/pink-svelte';
import { CreditsApplied } from '.';
import { sdk } from '$lib/stores/sdk';
import { AppwriteException } from '@appwrite.io/console';
import { AppwriteException, BillingPlan, type Models } from '@appwrite.io/console';
import DiscountsApplied from './discountsApplied.svelte';

export let billingPlan: Tier;
export let billingPlan: string;
export let collaborators: string[];
export let couponData: Partial<Coupon>;
export let couponData: Partial<Models.Coupon>;
export let billingBudget: number;
export let fixedCoupon = false; // If true, the coupon cannot be removed
export let isDowngrade = false;
export let organizationId: string | undefined = undefined;

let budgetEnabled = false;
let estimation: Estimation;
let estimation: Models.Estimation;

async function getEstimate(
billingPlan: string,
collaborators: string[],
couponId: string | undefined
) {
/* TODO: @itznotabug - temporary fix */
const billingPlanTyped = billingPlan as BillingPlan;

try {
estimation = await sdk.forConsole.billing.estimationCreateOrganization(
billingPlan,
couponId === '' ? null : couponId,
collaborators ?? []
);
estimation = await sdk.forConsole.organizations.estimationCreateOrganization({
billingPlan: billingPlanTyped,
invites: collaborators ?? [],
couponId: couponId === '' ? null : couponId
});
} catch (e) {
if (e instanceof AppwriteException) {
if (
Expand All @@ -56,12 +57,12 @@
couponId: string | undefined
) {
try {
estimation = await sdk.forConsole.billing.estimationUpdatePlan(
estimation = await sdk.forConsole.organizations.estimationUpdatePlan({
organizationId,
billingPlan,
couponId && couponId.length > 0 ? couponId : null,
collaborators ?? []
);
billingPlan: billingPlan as BillingPlan,
invites: collaborators ?? [],
couponId: couponId && couponId.length > 0 ? couponId : null
});
} catch (e) {
if (e instanceof AppwriteException) {
if (
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/billing/paymentBoxes.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
import { CreditCardBrandImage } from '..';
import { initializeStripe, unmountPaymentElement } from '$lib/stores/stripe';
import { Badge, Card, Layout } from '@appwrite.io/pink-svelte';
import type { PaymentMethodData } from '$lib/sdk/billing';
import type { PaymentMethod } from '@stripe/stripe-js';
import StatePicker from './statePicker.svelte';
import type { Models } from '@appwrite.io/console';

export let methods: PaymentMethodData[];
export let methods: Array<Models.PaymentMethod>;
export let group: string;
export let name: string;
export let defaultMethod: string = null;
Expand Down
14 changes: 7 additions & 7 deletions src/lib/components/billing/paymentModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@
import { addNotification } from '$lib/stores/notifications';
import { page } from '$app/state';
import { Spinner } from '@appwrite.io/pink-svelte';
import type { PaymentMethod } from '@stripe/stripe-js';
import type { PaymentMethod as StripePaymentMethod } from '@stripe/stripe-js';
import StatePicker from './statePicker.svelte';
import type { PaymentMethodData } from '$lib/sdk/billing';
import type { Models } from '@appwrite.io/console';

export let show = false;
export let onCardSubmit: ((card: PaymentMethodData) => void) | null = null;
export let onCardSubmit: ((card: Models.PaymentMethod) => void) | null = null;

let modal: FakeModal;
let name: string;
let state: string = '';
let error: string = null;
let showState: boolean = false;
let paymentMethod: PaymentMethod | null = null;
let paymentMethod: StripePaymentMethod | null = null;

async function handleSubmit() {
try {
Expand All @@ -47,15 +47,15 @@

const card = await submitStripeCard(name, page?.params?.organization ?? null);
if (card && Object.hasOwn(card, 'id')) {
if ((card as PaymentMethod).card?.country === 'US') {
paymentMethod = card as PaymentMethod;
if ((card as StripePaymentMethod).card?.country === 'US') {
paymentMethod = card as StripePaymentMethod;
showState = true;
return;
}
}
modal.closeModal();
await invalidate(Dependencies.PAYMENT_METHODS);
onCardSubmit?.(card as PaymentMethodData);
onCardSubmit?.(card as Models.PaymentMethod);
addNotification({
type: 'success',
message: 'A new payment method has been added to your account'
Expand Down
Loading
Loading