Skip to content

Commit 60838e0

Browse files
committed
fix(settings): handle 3DS redirect + replace cancel/retry with refresh for premium geoDB
Two missing pieces vs the BAA addon flow: 1. After Stripe redirects back from 3DS with ?type=confirm-addon&addonId=..., the page didn't read the query params or call the confirmations endpoint, so the addon stayed pending forever. Port the onMount handler from BAA.svelte. 2. The pending-state action was "Cancel & retry" which deleted the addon and forced a fresh 3DS flow. That's wasteful when the payment just needs a server-side status sync. Replace with "Refresh" — calls the confirmations endpoint, which live-queries Stripe and activates the addon if the PaymentIntent has transitioned to succeeded. Backend cleans up on 402.
1 parent afecbd2 commit 60838e0

1 file changed

Lines changed: 90 additions & 20 deletions

File tree

src/routes/(console)/project-[region]-[project]/settings/premiumGeoDB.svelte

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
<script lang="ts">
2+
import { onMount } from 'svelte';
23
import { Box, CardGrid } from '$lib/components';
34
import { Button } from '$lib/elements/forms';
45
import { getChangePlanUrl, plansInfo } from '$lib/stores/billing';
56
import { currentPlan, organization } from '$lib/stores/organization';
67
import { page } from '$app/state';
78
import { get } from 'svelte/store';
8-
import { invalidate } from '$app/navigation';
9+
import { goto, invalidate } from '$app/navigation';
10+
import { resolve } from '$app/paths';
911
import { Dependencies } from '$lib/constants';
1012
import { addNotification } from '$lib/stores/notifications';
1113
import { sdk } from '$lib/stores/sdk';
@@ -21,7 +23,7 @@
2123
let showEnable = false;
2224
let showDisable = false;
2325
let reEnabling = false;
24-
let cancelling = false;
26+
let refreshing = false;
2527
2628
$: planSupportsPremiumGeoDB = $currentPlan?.supportedAddons?.premiumGeoDB === true;
2729
$: canUpgradeToPremiumGeoDB =
@@ -45,22 +47,90 @@
4547
return false;
4648
}
4749
48-
async function handleCancelAndRetry() {
49-
cancelling = true;
50+
onMount(async () => {
51+
if (page.url.searchParams.get('type') === 'confirm-addon') {
52+
let addonId = page.url.searchParams.get('addonId');
53+
54+
// Fall back to listing pending addons if addonId is missing
55+
if (!addonId || addonId === 'undefined') {
56+
try {
57+
const addonList = await sdk
58+
.forConsoleIn(page.params.region)
59+
.projects.listAddons({ projectId: page.params.project });
60+
const pending = addonList.addons.find(
61+
(a) => a.key === 'premiumGeoDB' && a.status === 'pending'
62+
);
63+
addonId = pending?.$id ?? null;
64+
} catch (e) {
65+
addNotification({
66+
message:
67+
e?.message ??
68+
'Unable to verify Premium Geo DB addon status. Please retry.',
69+
type: 'error'
70+
});
71+
addonId = null;
72+
}
73+
}
74+
75+
if (addonId) {
76+
await confirmAddon(addonId);
77+
}
78+
79+
const settingsUrl = resolve('/(console)/project-[region]-[project]/settings', {
80+
region: page.params.region,
81+
project: page.params.project
82+
});
83+
await goto(settingsUrl, { replaceState: true });
84+
}
85+
});
86+
87+
async function confirmAddon(addonId: string) {
5088
try {
51-
await sdk.forConsoleIn(page.params.region).projects.deleteAddon({
89+
await sdk.forConsoleIn(page.params.region).projects.confirmAddonPayment({
5290
projectId: page.params.project,
53-
addonId: premiumGeoDBAddon.$id
91+
addonId
5492
});
55-
await invalidate(Dependencies.ADDONS);
56-
showEnable = true;
57-
} catch (e) {
93+
await Promise.all([invalidate(Dependencies.ADDONS), invalidate(Dependencies.PROJECT)]);
5894
addNotification({
59-
message: e.message,
60-
type: 'error'
95+
message: 'Premium Geo DB addon has been enabled',
96+
type: 'success'
6197
});
98+
} catch (e) {
99+
// Webhook may have already activated it — sync state and assume success
100+
if (e?.type === 'addon_not_found' || e?.code === 404) {
101+
await Promise.all([
102+
invalidate(Dependencies.ADDONS),
103+
invalidate(Dependencies.PROJECT)
104+
]);
105+
addNotification({
106+
message: 'Premium Geo DB addon has been enabled',
107+
type: 'success'
108+
});
109+
} else if (e?.code === 402) {
110+
// Payment failed — the backend has cleaned up; offer to retry from scratch
111+
await invalidate(Dependencies.ADDONS);
112+
addNotification({
113+
message:
114+
e?.message ??
115+
'Payment could not be authorized. Please try enabling the addon again.',
116+
type: 'error'
117+
});
118+
} else {
119+
addNotification({
120+
message: e.message,
121+
type: 'error'
122+
});
123+
}
124+
}
125+
}
126+
127+
async function handleRefresh() {
128+
if (!premiumGeoDBAddon) return;
129+
refreshing = true;
130+
try {
131+
await confirmAddon(premiumGeoDBAddon.$id);
62132
} finally {
63-
cancelling = false;
133+
refreshing = false;
64134
}
65135
}
66136
@@ -88,9 +158,9 @@
88158

89159
<CardGrid>
90160
<svelte:fragment slot="title">Premium Geo DB</svelte:fragment>
91-
Give your project richer location intelligence on every user. Tailor pricing by region, surface
92-
the right currency and language, comply with local regulations, and spot suspicious sign-ins
93-
before they cause damage — all without leaving Appwrite.
161+
Give your project richer location intelligence on every user. Tailor pricing by region, surface the
162+
right currency and language, comply with local regulations, and spot suspicious sign-ins before they
163+
cause damage — all without leaving Appwrite.
94164
<svelte:fragment slot="aside">
95165
<Box>
96166
<h6>
@@ -116,15 +186,15 @@
116186
<Badge variant="secondary" type="warning" content="Payment pending" />
117187
</div>
118188
<p class="text u-margin-block-start-8">
119-
A payment is awaiting confirmation. If the payment was interrupted, you can
120-
cancel and retry.
189+
A payment is awaiting confirmation. If you've completed authentication, click
190+
refresh to check the payment status.
121191
</p>
122192
<Button
123193
secondary
124194
class="u-margin-block-start-16"
125-
disabled={cancelling}
126-
on:click={handleCancelAndRetry}>
127-
<span class="text">Cancel & retry</span>
195+
disabled={refreshing}
196+
on:click={handleRefresh}>
197+
<span class="text">Refresh</span>
128198
</Button>
129199
{:else if isActive}
130200
<div class="u-flex u-cross-center u-gap-8 u-margin-block-start-8">

0 commit comments

Comments
 (0)