mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
feat: stripe
This commit is contained in:
Generated
+35
-10
@@ -14,10 +14,12 @@
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@sentry/svelte": "^7.36.0",
|
||||
"@sentry/tracing": "^7.36.0",
|
||||
"@stripe/stripe-js": "^1.47.0",
|
||||
"analytics": "^0.8.1",
|
||||
"echarts": "^5.4.1",
|
||||
"pretty-bytes": "^6.1.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"stripe": "^11.11.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
"web-vitals": "^3.1.1"
|
||||
},
|
||||
@@ -1927,6 +1929,11 @@
|
||||
"@sinonjs/commons": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@stripe/stripe-js": {
|
||||
"version": "1.47.0",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-1.47.0.tgz",
|
||||
"integrity": "sha512-jKSClqEIKS2MbPCXlSsseDSZyJ3dVrfUrYMz5LBY1o9iS2tfKbpTZACt8r2g+xyQozI+uHr76pVTyFsmBKA4Mg=="
|
||||
},
|
||||
"node_modules/@sveltejs/adapter-static": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-1.0.6.tgz",
|
||||
@@ -2267,8 +2274,7 @@
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.11.19",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.19.tgz",
|
||||
"integrity": "sha512-YUgMWAQBWLObABqrvx8qKO1enAvBUdjZOAWQ5grBAkp5LQv45jBvYKZ3oFS9iKRCQyFjqw6iuEa1vmFqtxYLZw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-YUgMWAQBWLObABqrvx8qKO1enAvBUdjZOAWQ5grBAkp5LQv45jBvYKZ3oFS9iKRCQyFjqw6iuEa1vmFqtxYLZw=="
|
||||
},
|
||||
"node_modules/@types/prettier": {
|
||||
"version": "2.7.2",
|
||||
@@ -3035,7 +3041,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
|
||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"get-intrinsic": "^1.0.2"
|
||||
@@ -4311,8 +4316,7 @@
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"node_modules/functions-have-names": {
|
||||
"version": "1.2.3",
|
||||
@@ -4356,7 +4360,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
@@ -4496,7 +4499,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1"
|
||||
},
|
||||
@@ -4538,7 +4540,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@@ -6597,7 +6598,6 @@
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
@@ -7182,6 +7182,20 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
@@ -7575,7 +7589,6 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.0",
|
||||
"get-intrinsic": "^1.0.2",
|
||||
@@ -7913,6 +7926,18 @@
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/stripe": {
|
||||
"version": "11.11.0",
|
||||
"resolved": "https://registry.npmjs.org/stripe/-/stripe-11.11.0.tgz",
|
||||
"integrity": "sha512-CT8TZJ+bUaMmqCOljHdKwHTWym0bscrCHrKmvK2KoTaz/9gDIzG+lXTJOFUOb3K7Jwaoaori4yEVzOgA7U46+g==",
|
||||
"dependencies": {
|
||||
"@types/node": ">=8.1.0",
|
||||
"qs": "^6.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.*"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
|
||||
@@ -25,10 +25,12 @@
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@sentry/svelte": "^7.36.0",
|
||||
"@sentry/tracing": "^7.36.0",
|
||||
"@stripe/stripe-js": "^1.47.0",
|
||||
"analytics": "^0.8.1",
|
||||
"echarts": "^5.4.1",
|
||||
"pretty-bytes": "^6.1.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"stripe": "^11.11.0",
|
||||
"tippy.js": "^6.3.7",
|
||||
"web-vitals": "^3.1.1"
|
||||
},
|
||||
|
||||
@@ -9,6 +9,7 @@ export const VARS = {
|
||||
| string
|
||||
| undefined,
|
||||
CONSOLE_MODE: import.meta.env?.VITE_CONSOLE_MODE?.toString() as string | undefined,
|
||||
CONSOLE_TIER: import.meta.env?.VITE_CONSOLE_TIER?.toString() as string | undefined,
|
||||
VERCEL_ENV: import.meta.env?.VITE_VERCEL_ENV?.toString() as string | undefined,
|
||||
GOOGLE_ANALYTICS: import.meta.env?.VITE_GA_PROJECT?.toString() as string | undefined
|
||||
};
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { Container } from '$lib/layout';
|
||||
import AvailableCredit from './availableCredit.svelte';
|
||||
import BillingAddress from './billingAddress.svelte';
|
||||
import PaymentHistory from './paymentHistory.svelte';
|
||||
import PaymentMethods from './paymentMethods.svelte';
|
||||
import PaymentModal from './paymentModal.svelte';
|
||||
import PaymentSummary from './paymentSummary.svelte';
|
||||
|
||||
let showPayment = false;
|
||||
</script>
|
||||
|
||||
<Container>
|
||||
<PaymentSummary />
|
||||
<PaymentHistory />
|
||||
<PaymentMethods />
|
||||
<PaymentMethods bind:showPayment />
|
||||
<BillingAddress />
|
||||
<AvailableCredit />
|
||||
</Container>
|
||||
|
||||
<PaymentModal bind:show={showPayment} />
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
let coupon: string = null;
|
||||
|
||||
async function redeem() {
|
||||
const response = await fetch('/v1/account/credit', {
|
||||
const response = await fetch('/v1/billing/credit', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { CardGrid, Empty, Heading } from '$lib/components';
|
||||
export let showPayment = false;
|
||||
</script>
|
||||
|
||||
<CardGrid>
|
||||
@@ -7,7 +8,7 @@
|
||||
|
||||
<p class="text">View or update your organization payment methods here.</p>
|
||||
<svelte:fragment slot="aside">
|
||||
<Empty>
|
||||
<Empty on:click={() => (showPayment = true)}>
|
||||
<p class="text">Add a payment method</p>
|
||||
</Empty>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<script lang="ts">
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { Modal } from '$lib/components';
|
||||
import { InputText, Button, FormList } from '$lib/elements/forms';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
|
||||
import { publicKey } from './stripe';
|
||||
import { loadStripe } from '@stripe/stripe-js';
|
||||
import { onMount } from 'svelte';
|
||||
import FormItem from '$lib/elements/forms/formItem.svelte';
|
||||
import { app } from '$lib/stores/app';
|
||||
|
||||
export let show = false;
|
||||
|
||||
let name: string;
|
||||
let error: string;
|
||||
let isCreating = false;
|
||||
let stripe;
|
||||
let elements;
|
||||
let card: HTMLDivElement;
|
||||
let address: HTMLDivElement;
|
||||
let complete = false;
|
||||
const appearance = {
|
||||
theme: $app.themeInUse === 'dark' ? 'night' : 'stripe',
|
||||
variables: {
|
||||
colorPrimary: $app.themeInUse === 'dark' ? '#ffffff' : '#000000'
|
||||
}
|
||||
};
|
||||
|
||||
let paymentIntent;
|
||||
let clientSecret;
|
||||
|
||||
onMount(async () => {
|
||||
stripe = await loadStripe(publicKey);
|
||||
elements = stripe.elements(appearance);
|
||||
// paymentIntent = await createIntent();
|
||||
// clientSecret = paymentIntent.client_secret;
|
||||
createForm();
|
||||
});
|
||||
|
||||
async function createForm() {
|
||||
const cardElement = elements.create('card');
|
||||
cardElement.mount(card);
|
||||
|
||||
const addressElement = elements.create('address');
|
||||
addressElement.mount(address);
|
||||
|
||||
cardElement.on('change', (e) => (complete = e.complete));
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
isCreating = true;
|
||||
|
||||
trackEvent(Submit.ProjectCreate);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `${name} has been created`
|
||||
});
|
||||
} catch (e) {
|
||||
isCreating = false;
|
||||
error = e.message;
|
||||
trackError(e, Submit.ProjectCreate);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Modal {error} on:submit={handleSubmit} size="big" bind:show>
|
||||
<svelte:fragment slot="header">Add Payment Method</svelte:fragment>
|
||||
This payment method will be added to the Acme Org billing details.
|
||||
<FormList>
|
||||
<InputText
|
||||
id="name"
|
||||
label="Cardholder name"
|
||||
bind:value={name}
|
||||
required
|
||||
autofocus={true}
|
||||
disabled={isCreating} />
|
||||
<FormItem>
|
||||
<div class="input-text-wrapper">
|
||||
<div class="input-text" bind:this={card} />
|
||||
<div bind:this={address} />
|
||||
</div>
|
||||
</FormItem>
|
||||
</FormList>
|
||||
<svelte:fragment slot="footer">
|
||||
<Button secondary on:click={() => (show = false)}>Cancel</Button>
|
||||
<Button submit disabled={isCreating}>Create</Button>
|
||||
</svelte:fragment>
|
||||
</Modal>
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Box, CardGrid, Heading } from '$lib/components';
|
||||
import { VARS } from '$lib/system';
|
||||
</script>
|
||||
|
||||
<CardGrid>
|
||||
@@ -13,5 +14,11 @@
|
||||
</svelte:fragment>
|
||||
<p class="u-trim-1">X projects</p>
|
||||
</Box>
|
||||
{#if VARS.CONSOLE_TIER && VARS.CONSOLE_TIER !== 'base'}
|
||||
<div class="u-flex">
|
||||
<p class="u-bold">Next billing data</p>
|
||||
<p class="text u-margin-inline-start-auto">XX DATE</p>
|
||||
</div>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import Stripe from 'stripe';
|
||||
|
||||
export const publicKey = import.meta.env?.VITE_STRIPE_PUBLIC_KEY?.toString() as string | undefined;
|
||||
export const secretKey = import.meta.env?.VITE_STRIPE_SECRET_KEY?.toString() as string | undefined;
|
||||
export const stripe = new Stripe(secretKey, undefined);
|
||||
|
||||
function createCustomerStore() {
|
||||
const { subscribe, set } = writable<Stripe.Customer | Stripe.DeletedCustomer>(undefined);
|
||||
return {
|
||||
subscribe,
|
||||
set,
|
||||
load: async (id: string) => {
|
||||
const customer: Stripe.Customer | Stripe.DeletedCustomer =
|
||||
await stripe.customers.retrieve(id);
|
||||
set(customer);
|
||||
},
|
||||
create: async (params: Stripe.CustomerCreateParams) => {
|
||||
const customer: Stripe.Customer = await stripe.customers.create(params);
|
||||
set(customer);
|
||||
},
|
||||
update: async (id: string, params: Stripe.CustomerUpdateParams) => {
|
||||
const customer: Stripe.Customer = await stripe.customers.update(id, params);
|
||||
set(customer);
|
||||
},
|
||||
remove: async (id: string) => {
|
||||
const customer: Stripe.DeletedCustomer = await stripe.customers.del(id);
|
||||
set(customer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const customer = createCustomerStore();
|
||||
Reference in New Issue
Block a user