Skip to content

Commit 129f8a4

Browse files
Merge pull request #1760 from appwrite/fix-live-payment-validate
Feat: validate live payment
2 parents 9ced8ce + bd2aa1a commit 129f8a4

File tree

8 files changed

+92
-17
lines changed

8 files changed

+92
-17
lines changed

src/lib/components/heading.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
<svelte:element
1616
this={tag}
17+
class:target={!!id}
1718
class={`heading-level-${size} u-min-width-0 ${classes}`}
1819
class:u-trim-1={trimmed}
1920
class:u-trim-2={trimmedSecondLine}
@@ -23,7 +24,7 @@
2324
</svelte:element>
2425

2526
<style>
26-
:target {
27+
.target {
2728
padding-top: 120px;
2829
margin-top: -120px;
2930
}

src/lib/sdk/billing.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,14 @@ export class Billing {
739739
);
740740
}
741741

742+
async updateInvoiceStatus(organizationId: string, invoiceId: string): Promise<Invoice> {
743+
const path = `/organizations/${organizationId}/invoices/${invoiceId}/status`;
744+
const uri = new URL(this.client.config.endpoint + path);
745+
return await this.client.call('PATCH', uri, {
746+
'content-type': 'application/json'
747+
});
748+
}
749+
742750
async retryPayment(
743751
organizationId: string,
744752
invoiceId: string,

src/lib/stores/billing.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import { canSeeBilling } from './roles';
3333
import { sdk } from './sdk';
3434
import { user } from './user';
3535
import BudgetLimitAlert from '$routes/(console)/organization-[organization]/budgetLimitAlert.svelte';
36+
import TeamReadonlyAlert from '$routes/(console)/organization-[organization]/teamReadonlyAlert.svelte';
3637

3738
export type Tier = 'tier-0' | 'tier-1' | 'tier-2' | 'auto-1' | 'cont-1' | 'ent-1';
3839

@@ -59,6 +60,9 @@ export const roles = [
5960
}
6061
];
6162

63+
export const teamStatusReadonly = 'readonly';
64+
export const billingLimitOutstandingInvoice = 'outstanding_invoice';
65+
6266
export const paymentMethods = derived(page, ($page) => $page.data.paymentMethods as PaymentList);
6367
export const addressList = derived(page, ($page) => $page.data.addressList as AddressesList);
6468
export const plansInfo = derived(page, ($page) => $page.data.plansInfo as PlansMap);
@@ -270,27 +274,35 @@ export function calculateTrialDay(org: Organization) {
270274
}
271275

272276
export async function checkForUsageLimit(org: Organization) {
273-
if (!org?.billingLimits) {
277+
if (org?.status === teamStatusReadonly && org?.remarks === billingLimitOutstandingInvoice) {
278+
headerAlert.add({
279+
id: 'teamReadOnlyFailedInvoices',
280+
component: TeamReadonlyAlert,
281+
show: true,
282+
importance: 11
283+
});
284+
readOnly.set(true);
285+
return;
286+
}
287+
if (!org?.billingLimits && org?.status !== teamStatusReadonly) {
274288
readOnly.set(false);
275289
return;
276290
}
277291
if (org?.billingPlan !== BillingPlan.FREE) {
278292
const { budgetLimit } = org?.billingLimits ?? {};
279293

280-
if (!budgetLimit || budgetLimit < 100) {
294+
if (budgetLimit && budgetLimit >= 100) {
281295
readOnly.set(false);
296+
headerAlert.add({
297+
id: 'budgetLimit',
298+
component: BudgetLimitAlert,
299+
show: true,
300+
importance: 10
301+
});
302+
303+
readOnly.set(true);
282304
return;
283305
}
284-
285-
headerAlert.add({
286-
id: 'budgetLimit',
287-
component: BudgetLimitAlert,
288-
show: true,
289-
importance: 10
290-
});
291-
292-
readOnly.set(true);
293-
return;
294306
}
295307
const { bandwidth, documents, executions, storage, users } = org?.billingLimits ?? {};
296308
const resources = [

src/lib/stores/organization.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ export type Organization = Models.Team<Record<string, unknown>> & {
3232
billingPlanDowngrade?: Tier;
3333
billingAggregationId: string;
3434
billingInvoiceId: string;
35+
status: string;
36+
remarks: string;
3537
};
3638

3739
export type OrganizationList = {

src/routes/(console)/organization-[organization]/billing/+page.svelte

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import RetryPaymentModal from './retryPaymentModal.svelte';
2020
import { selectedInvoice, showRetryModal } from './store';
2121
import { Button } from '$lib/elements/forms';
22-
import { goto } from '$app/navigation';
22+
import { goto, invalidate } from '$app/navigation';
23+
import { Dependencies } from '$lib/constants';
24+
import { base } from '$app/paths';
2325
2426
export let data;
2527
@@ -50,10 +52,21 @@
5052
await confirmPayment(
5153
$organization.$id,
5254
invoice.clientSecret,
53-
$organization.paymentMethodId
55+
$organization.paymentMethodId,
56+
`${base}/organization-${$organization.$id}/billing?type=validate-invoice&invoice=${invoice.$id}`
5457
);
5558
}
5659
60+
if (
61+
$page.url.searchParams.has('type') &&
62+
$page.url.searchParams.get('type') === 'validate-invoice'
63+
) {
64+
const invoiceId = $page.url.searchParams.get('invoice');
65+
await sdk.forConsole.billing.updateInvoiceStatus($organization.$id, invoiceId);
66+
invalidate(Dependencies.INVOICES);
67+
invalidate(Dependencies.ORGANIZATION);
68+
}
69+
5770
if (
5871
$page.url.searchParams.has('invoice') &&
5972
$page.url.searchParams.get('type') === 'retry'

src/routes/(console)/organization-[organization]/billing/paymentHistory.svelte

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@
5151
]);
5252
}
5353
54+
$: if ($page.url.searchParams.get('type') === 'validate-invoice') {
55+
window.history.replaceState({}, '', $page.url.pathname);
56+
request();
57+
}
58+
5459
function retryPayment(invoice: Invoice) {
5560
$selectedInvoice = invoice;
5661
$showRetryModal = true;
@@ -62,7 +67,7 @@
6267
</script>
6368

6469
<CardGrid>
65-
<Heading tag="h2" size="6">Payment history</Heading>
70+
<Heading id="payment-history" tag="h2" size="6">Payment history</Heading>
6671

6772
<p class="text">
6873
Transaction history for this organization. Download invoices for more details about your

src/routes/(console)/organization-[organization]/billing/retryPaymentModal.svelte

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import { onMount } from 'svelte';
2121
import { getApiEndpoint, sdk } from '$lib/stores/sdk';
2222
import { formatCurrency } from '$lib/helpers/numbers';
23+
import { base } from '$app/paths';
2324
2425
export let show = false;
2526
export let invoice: Invoice;
@@ -79,8 +80,12 @@
7980
await confirmPayment(
8081
$organization.$id,
8182
clientSecret,
82-
paymentMethodId ? paymentMethodId : $organization.paymentMethodId
83+
paymentMethodId ? paymentMethodId : $organization.paymentMethodId,
84+
`${base}/organization-${$organization.$id}/billing?type=validate-invoice&invoice=${invoice.$id}`
8385
);
86+
87+
await sdk.forConsole.billing.updateInvoiceStatus($organization.$id, invoice.$id);
88+
8489
invalidate(Dependencies.ORGANIZATION);
8590
invalidate(Dependencies.INVOICES);
8691
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<script lang="ts">
2+
import { page } from '$app/stores';
3+
import { Button } from '$lib/elements/forms';
4+
import { organization } from '$lib/stores/organization';
5+
import { HeaderAlert } from '$lib/layout';
6+
import {
7+
billingLimitOutstandingInvoice,
8+
hideBillingHeaderRoutes,
9+
readOnly,
10+
teamStatusReadonly
11+
} from '$lib/stores/billing';
12+
import { base } from '$app/paths';
13+
14+
$: redirectUrl = `${base}/organization-${$organization?.$id}/billing#payment-history`;
15+
</script>
16+
17+
{#if $organization?.$id && $organization?.status === teamStatusReadonly && $organization?.remarks === billingLimitOutstandingInvoice && $readOnly && !hideBillingHeaderRoutes.includes($page.url.pathname)}
18+
<HeaderAlert type="error" title="Access restricted">
19+
<svelte:fragment>
20+
Your organization’s access to resources has been restricted due to unpaid invoices.
21+
Payment is required to restore access.
22+
</svelte:fragment>
23+
<svelte:fragment slot="buttons">
24+
<Button secondary fullWidthMobile href={redirectUrl}>
25+
<span class="text">View invoices</span>
26+
</Button>
27+
</svelte:fragment>
28+
</HeaderAlert>
29+
{/if}

0 commit comments

Comments
 (0)