Merge pull request #17098 from supabase/go-live-org-billing

This commit is contained in:
Kevin Grüneberg authored and GitHub committed 2023-09-04 13:32:13 +02:00
commit 3b9560f237
7 files changed
+135 -221

No files matched your search

@@ -8,21 +8,23 @@ export const meta = {
Every project on the Supabase Platform comes with its own dedicated Postgres instance running inside a virtual machine (VM). The following table describes the base instance with additional compute add-ons available if you need extra performance when scaling up Supabase.
| Plan | Pricing | CPU | Memory | Connections: Direct | Connections: Pooler |
| --------------- | ------- | ----------------------- | ------ | ------------------- | ------------------- |
| Free (Included) | $0 | 2-core ARM (shared) | 1 GB | 60 | 200 |
| Small | $5 | 2-core ARM (shared) | 2 GB | 90 | 200 |
| Medium | $50 | 2-core ARM (shared) | 4 GB | 120 | 200 |
| Large | $100 | 2-core ARM (dedicated) | 8 GB | 160 | 300 |
| XL | $200 | 4-core ARM (dedicated) | 16 GB | 240 | 700 |
| 2XL | $400 | 8-core ARM (dedicated) | 32 GB | 380 | 1500 |
| 4XL | $950 | 16-core ARM (dedicated) | 64 GB | 480 | 3000 |
| 8XL | $1,860 | 32-core ARM (dedicated) | 128 GB | 490 | 6000 |
| 12XL | $2,790 | 48-core ARM (dedicated) | 192 GB | 500 | 9000 |
| 16XL | $3,720 | 64-core ARM (dedicated) | 256 GB | 500 | 12,000 |
| Plan | Hourly Price USD | Monthly Price USD | CPU | Memory | Connections: Direct | Connections: Pooler |
| ------- | ---------------- | ----------------- | ----------------------- | ------ | ------------------- | ------------------- |
| Starter | $0.01344 | ~$10 | 2-core ARM (shared) | 1 GB | 60 | 200 |
| Small | $0.0206 | ~$15 | 2-core ARM (shared) | 2 GB | 90 | 200 |
| Medium | $0.0822 | ~$60 | 2-core ARM (shared) | 4 GB | 120 | 200 |
| Large | $0.1517 | ~$110 | 2-core ARM (dedicated) | 8 GB | 160 | 300 |
| XL | $0.2877 | ~$210 | 4-core ARM (dedicated) | 16 GB | 240 | 700 |
| 2XL | $0.562 | ~$410 | 8-core ARM (dedicated) | 32 GB | 380 | 1500 |
| 4XL | $1.32 | ~$960 | 16-core ARM (dedicated) | 64 GB | 480 | 3000 |
| 8XL | $2.562 | ~$1,870 | 32-core ARM (dedicated) | 128 GB | 490 | 6000 |
| 12XL | $3.836 | ~$2,800 | 48-core ARM (dedicated) | 192 GB | 500 | 9000 |
| 16XL | $5.12 | ~$3,730 | 64-core ARM (dedicated) | 256 GB | 500 | 12,000 |
Number of connections above are recommended values.
We charge hourly for additional compute based on your usage. Read more about [usage-based billing for compute](/docs/guides/platform/org-based-billing#usage-based-billing-for-compute).
[Contact us](https://supabase.com/contact/enterprise) if you require a custom plan.
## Dedicated vs. shared CPU
@@ -37,18 +39,18 @@ When considering compute upgrades, assess whether your bottlenecks are hardware-
SSD Disks are attached to your servers and the disk performance depends on the compute add-on of your instance. Disk IO refers to two metrics: throughput (Megabits per Second) and IOPS (Input/Output Operations per Second).
| Plan | Pricing | Max Disk Throughput | Baseline Disk Throughput | Max IOPS | Baseline IOPS |
| --------------- | ------- | ------------------- | ------------------------ | ----------- | ------------- |
| Free (Included) | $0 | 2,085 Mbps | 87 Mbps | 11,800 IOPS | 500 IOPS |
| Small | $5 | 2,085 Mbps | 174 Mbps | 11,800 IOPS | 1,000 IOPS |
| Medium | $50 | 2,085 Mbps | 347 Mbps | 11,800 IOPS | 2,000 IOPS |
| Large | $100 | 4,750 Mbps | 630 Mbps | 20,000 IOPS | 3,600 IOPS |
| XL | $200 | 4,750 Mbps | 1,188 Mbps | 20,000 IOPS | 6,000 IOPS |
| 2XL | $400 | 4,750 Mbps | 2,375 Mbps | 20,000 IOPS | 12,000 IOPS |
| 4XL | $950 | 4,750 Mbps | 4,750 Mbps | 20,000 IOPS | 20,000 IOPS |
| 8XL | $1,860 | 9,500 Mbps | 9,500 Mbps | 40,000 IOPS | 40,000 IOPS |
| 12XL | $2,790 | 14,250 Mbps | 14,250 Mbps | 50,000 IOPS | 50,000 IOPS |
| 16XL | $3,720 | 19,000 Mbps | 19,000 Mbps | 80,000 IOPS | 80,000 IOPS |
| Plan | Max Disk Throughput | Baseline Disk Throughput | Max IOPS | Baseline IOPS |
| ------- | ------------------- | ------------------------ | ----------- | ------------- |
| Starter | 2,085 Mbps | 87 Mbps | 11,800 IOPS | 500 IOPS |
| Small | 2,085 Mbps | 174 Mbps | 11,800 IOPS | 1,000 IOPS |
| Medium | 2,085 Mbps | 347 Mbps | 11,800 IOPS | 2,000 IOPS |
| Large | 4,750 Mbps | 630 Mbps | 20,000 IOPS | 3,600 IOPS |
| XL | 4,750 Mbps | 1,188 Mbps | 20,000 IOPS | 6,000 IOPS |
| 2XL | 4,750 Mbps | 2,375 Mbps | 20,000 IOPS | 12,000 IOPS |
| 4XL | 4,750 Mbps | 4,750 Mbps | 20,000 IOPS | 20,000 IOPS |
| 8XL | 9,500 Mbps | 9,500 Mbps | 40,000 IOPS | 40,000 IOPS |
| 12XL | 14,250 Mbps | 14,250 Mbps | 50,000 IOPS | 50,000 IOPS |
| 16XL | 19,000 Mbps | 19,000 Mbps | 80,000 IOPS | 80,000 IOPS |
[Contact us](https://supabase.com/contact/enterprise) if you require a custom plan.
@@ -1,9 +1,10 @@
import React, { Fragment, useMemo } from 'react'
import { useRouter } from 'next/router'
import { useTheme } from 'common/Providers'
import { IconCheckCircle, IconXCircle, Modal } from 'ui'
import { IconXCircle, Modal } from 'ui'
import pricingAddOn from '~/data/PricingAddOnTable.json'
import { IconPricingIncludedCheck, IconPricingMinus } from './PricingIcons'
import Link from 'next/link'
interface Props {
showComputeModal: boolean
@@ -47,12 +48,33 @@ export default function ComputePricingModal({ showComputeModal, setShowComputeMo
</div>
<div className="max-w-4xl prose">
<h2 className="text-lg">Choose best compute setup for you</h2>
<p className="text-sm lg:max-w-3xl">
<p className="text-sm">
Every project on the Supabase Platform comes with its own dedicated Postgres
instance running inside a virtual machine (VM). The following table describes the
base instance with additional compute add-ons available if you need extra
performance when scaling up Supabase.
</p>
<p className="text-sm">
Compute instances are billed hourly and you can scale up or down at any time if you
need extra performance. You'll only be charged at the end of the month for the hours
you've used. Paid plans come with $10 in Compute Credits to cover one Starter
instance or parts of any other instance. Read more on{' '}
<Link
href="https://supabase.com/docs/guides/platform/org-based-billing#usage-based-billing-for-compute"
passHref
>
<a target="_blank" className="transition text-brand hover:text-brand-600">
usage-based billing for compute
</a>
</Link>{' '}
or{' '}
<Link href="https://supabase.com/docs/guides/platform/compute-add-ons" passHref>
<a target="_blank" className="transition text-brand hover:text-brand-600">
Compute Add-ons
</a>
</Link>
.
</p>
</div>
</div>
</div>
@@ -73,7 +95,7 @@ export default function ComputePricingModal({ showComputeModal, setShowComputeMo
{i === 0 && (
<tr className="">
<td className="pb-1 bg-scale-700 px-3 py-1 -mr-1 border-l-4 border-scale-700">
<span className="">Included in Free and Pro plan</span>
<span className="">First instance is free on paid plans</span>
</td>
</tr>
)}
+10 -10
View File
@@ -11,7 +11,7 @@
{
"columns": [
{ "key": "plan", "title": "Plan", "value": "Starter" },
{ "key": "pricing", "title": "Pricing", "value": "$0" },
{ "key": "pricing", "title": "Price USD", "value": "$10" },
{ "key": "cpu", "title": "CPU", "value": "2-core ARM" },
{ "key": "dedicated", "title": "Dedicated", "value": false },
{ "key": "memory", "title": "Memory", "value": "1 GB" },
@@ -36,7 +36,7 @@
{
"columns": [
{ "key": "plan", "title": "Plan", "value": "Small" },
{ "key": "pricing", "title": "Pricing", "value": "$5" },
{ "key": "pricing", "title": "Price USD", "value": "$15" },
{ "key": "cpu", "title": "CPU", "value": "2-core ARM" },
{ "key": "dedicated", "title": "Dedicated", "value": false },
{ "key": "memory", "title": "Memory", "value": "2 GB" },
@@ -61,7 +61,7 @@
{
"columns": [
{ "key": "plan", "title": "Plan", "value": "Medium" },
{ "key": "pricing", "title": "Pricing", "value": "$50" },
{ "key": "pricing", "title": "Price USD", "value": "$60" },
{ "key": "cpu", "title": "CPU", "value": "2-core ARM" },
{ "key": "dedicated", "title": "Dedicated", "value": false },
{ "key": "memory", "title": "Memory", "value": "4 GB" },
@@ -86,7 +86,7 @@
{
"columns": [
{ "key": "plan", "title": "Plan", "value": "Large" },
{ "key": "pricing", "title": "Pricing", "value": "$100" },
{ "key": "pricing", "title": "Price USD", "value": "$110" },
{ "key": "cpu", "title": "CPU", "value": "2-core ARM" },
{ "key": "dedicated", "title": "Dedicated", "value": true },
{ "key": "memory", "title": "Memory", "value": "8 GB" },
@@ -111,7 +111,7 @@
{
"columns": [
{ "key": "plan", "title": "Plan", "value": "XL" },
{ "key": "pricing", "title": "Pricing", "value": "$200" },
{ "key": "pricing", "title": "Price USD", "value": "$210" },
{ "key": "cpu", "title": "CPU", "value": "4-core ARM" },
{ "key": "dedicated", "title": "Dedicated", "value": true },
{ "key": "memory", "title": "Memory", "value": "16 GB" },
@@ -136,7 +136,7 @@
{
"columns": [
{ "key": "plan", "title": "Plan", "value": "2XL" },
{ "key": "pricing", "title": "Pricing", "value": "$400" },
{ "key": "pricing", "title": "Price USD", "value": "$410" },
{ "key": "cpu", "title": "CPU", "value": "8-core ARM" },
{ "key": "dedicated", "title": "Dedicated", "value": true },
{ "key": "memory", "title": "Memory", "value": "32 GB" },
@@ -161,7 +161,7 @@
{
"columns": [
{ "key": "plan", "title": "Plan", "value": "4XL" },
{ "key": "pricing", "title": "Pricing", "value": "$950" },
{ "key": "pricing", "title": "Price USD", "value": "$960" },
{ "key": "cpu", "title": "CPU", "value": "16-core ARM" },
{ "key": "dedicated", "title": "Dedicated", "value": true },
{ "key": "memory", "title": "Memory", "value": "64 GB" },
@@ -186,7 +186,7 @@
{
"columns": [
{ "key": "plan", "title": "Plan", "value": "8XL" },
{ "key": "pricing", "title": "Pricing", "value": "$1,860" },
{ "key": "pricing", "title": "Price USD", "value": "$1,870" },
{ "key": "cpu", "title": "CPU", "value": "32-core ARM" },
{ "key": "dedicated", "title": "Dedicated", "value": true },
{ "key": "memory", "title": "Memory", "value": "128 GB" },
@@ -211,7 +211,7 @@
{
"columns": [
{ "key": "plan", "title": "Plan", "value": "12XL" },
{ "key": "pricing", "title": "Pricing", "value": "$2,790" },
{ "key": "pricing", "title": "Price USD", "value": "$2,800" },
{ "key": "cpu", "title": "CPU", "value": "48-core ARM" },
{ "key": "dedicated", "title": "Dedicated", "value": true },
{ "key": "memory", "title": "Memory", "value": "192 GB" },
@@ -240,7 +240,7 @@
{
"columns": [
{ "key": "plan", "title": "Plan", "value": "16XL" },
{ "key": "pricing", "title": "Pricing", "value": "$3,720" },
{ "key": "pricing", "title": "Price USD", "value": "$3,730" },
{ "key": "cpu", "title": "CPU", "value": "64-core ARM" },
{ "key": "dedicated", "title": "Dedicated", "value": true },
{ "key": "memory", "title": "Memory", "value": "256 GB" },
+55 -14
View File
@@ -1,4 +1,4 @@
import { Accordion, Button, IconCheck, Select } from 'ui'
import { Accordion, Badge, Button, IconCheck, Select } from 'ui'
import Solutions from 'data/Solutions'
import { NextSeo } from 'next-seo'
import Link from 'next/link'
@@ -13,6 +13,7 @@ import pricingFaq from '~/data/PricingFAQ.json'
import { useTheme } from 'common/Providers'
import ComputePricingModal from '~/components/Pricing/ComputePricingModal'
import { plans } from 'shared-data/plans'
import { ArrowNarrowRightIcon } from '@heroicons/react/outline'
export default function IndexPage() {
const router = useRouter()
@@ -121,7 +122,7 @@ export default function IndexPage() {
<p className="p">{priceDescription}</p>
</div>
<p className="p">{description}</p>
<Link href="https://supabase.com/dashboard" passHref>
<Link href="https://supabase.com/dashboard/new" passHref>
<a>
<Button size="medium" block>
Get started
@@ -158,6 +159,46 @@ export default function IndexPage() {
<p className="p text-lg">
Start building for free, collaborate with a team, then scale to millions of users.
</p>
<div className="w-full flex justify-center items-center opacity-0 !animate-[fadeIn_0.5s_cubic-bezier(0.25,0.25,0,1)_0.5s_both]">
<Link href="/blog/organization-based-billing" passHref>
<a
target="_blank"
className="
group
relative
flex flex-row
items-center
pr-3 p-1
text-sm
w-auto
gap-2
text-left
rounded-full
bg-opacity-20
border
border-background-surface-100
hover:border-background-surface-300
overflow-hidden
focus:outline-none focus:ring-brand-600 focus:ring-2 focus:rounded-full
"
>
<Badge color="brand" size="large" className="py-1">
Update
</Badge>
<span className="text-foreground">Changes to how we bill</span>
<ArrowNarrowRightIcon className="h-4 ml-2 -translate-x-1 transition-transform group-hover:translate-x-0" />
<div
className="absolute inset-0 -z-10 bg-gradient-to-br
opacity-70
overflow-hidden rounded-full
from-background-surface-100
to-background-surface-300
backdrop-blur-md
"
/>
</a>
</Link>
</div>
</div>
</div>
</div>
@@ -404,7 +445,7 @@ export default function IndexPage() {
<p className="mt-3 prose lg:max-w-lg">
The Pro plan has a usage quota included and a spend cap turned on by default. If you
need to go beyond the inclusive limits, simply switch off your spend cap to pay for
additional usage and scale seamlessly. Note that your project will run into
additional usage and scale seamlessly. Note that your projects will run into
restrictions if you have the spend cap enabled and exhaust your quota.
</p>
</div>
@@ -456,7 +497,7 @@ export default function IndexPage() {
<MobileHeader
plan="Free"
price={'0'}
priceDescription={'/mo'}
priceDescription={'/month'}
description={'Perfect for hobby projects and experiments'}
/>
<PricingTableRowMobile
@@ -516,7 +557,7 @@ export default function IndexPage() {
plan="Pro"
from={false}
price={'25'}
priceDescription={'/mo + additional use'}
priceDescription={'/month + additional use'}
description={'Everything you need to scale your project into production'}
/>
<PricingTableRowMobile
@@ -568,7 +609,7 @@ export default function IndexPage() {
plan="Team"
from={false}
price={'599'}
priceDescription={'/mo + additional use'}
priceDescription={'/month + additional use'}
description={'Collaborate with different permissions and access patterns'}
/>
<PricingTableRowMobile
@@ -744,10 +785,10 @@ export default function IndexPage() {
{plan.priceMonthly}
</span>
{['Pro', 'Free'].includes(plan.name) && (
<p className="p text-[13px] leading-4 mt-1">per month</p>
<p className="p text-[13px] leading-4 mt-1">/ month / org</p>
)}
{['Team'].includes(plan.name) && (
<p className="p text-[13px] leading-4 mt-1">per month</p>
<p className="p text-[13px] leading-4 mt-1">/ month / org</p>
)}
</>
@@ -827,8 +868,8 @@ export default function IndexPage() {
<td className="px-6 pt-5">
<Link
href="https://supabase.com/dashboard"
as="https://supabase.com/dashboard"
href="https://supabase.com/dashboard/new?plan=free"
as="https://supabase.com/dashboard/new?plan=free"
>
<a>
<Button size="tiny" type="primary" block>
@@ -840,8 +881,8 @@ export default function IndexPage() {
<td className="px-6 pt-5">
<Link
href="https://supabase.com/dashboard"
as="https://supabase.com/dashboard"
href="https://supabase.com/dashboard/new?plan=pro"
as="https://supabase.com/dashboard/new?plan=pro"
>
<a>
<Button size="tiny" type="primary" block>
@@ -852,10 +893,10 @@ export default function IndexPage() {
</td>
<td className="px-6 pt-5">
<Link href="https://forms.supabase.com/team">
<Link href="https://supabase.com/dashboard/new?plan=team">
<a>
<Button size="tiny" type="primary" block>
Contact us
Get Started
</Button>
</a>
</Link>
+14 -29
View File
@@ -11,7 +11,6 @@ export interface PricingInformation {
description: string
preface: string
features: string[]
featuresOrg?: string[]
footer?: string
cta: string
}
@@ -21,12 +20,12 @@ export const plans: PricingInformation[] = [
id: 'tier_free',
name: 'Free',
nameBadge: '',
costUnit: '/ month / project',
costUnitOrg: '/ month',
href: 'https://supabase.com/dashboard/new/new-project',
costUnit: '/ month / org',
costUnitOrg: '/ month / org',
href: 'https://supabase.com/dashboard/new?plan=free',
priceLabel: '',
priceMonthly: 0,
warning: 'Limit of 2 free projects',
warning: 'Limit of 2 free organizations',
description: 'Perfect for passion projects & simple websites.',
preface: 'Get started with:',
features: [
@@ -34,7 +33,7 @@ export const plans: PricingInformation[] = [
'Social OAuth providers',
'Up to 500MB database space',
'Up to 1GB file storage',
'Up to 2GB bandwidth',
'Up to 5GB bandwidth',
'Up to 50MB file uploads',
'Up to 50,000 monthly active users',
'Up to 500K Edge Function invocations',
@@ -50,34 +49,19 @@ export const plans: PricingInformation[] = [
id: 'tier_pro',
name: 'Pro',
nameBadge: '',
costUnit: '/ month / project',
costUnitOrg: '/ month',
href: 'https://supabase.com/dashboard/new/new-project',
costUnit: '/ month / org',
costUnitOrg: '/ month / org',
href: 'https://supabase.com/dashboard/new?plan=pro',
priceLabel: 'From',
warning: 'Usage-based plan',
priceMonthly: 25,
description: 'For production applications with the option to scale.',
featuresOrg: [
'$10 Compute Credits',
'No project pausing',
'Daily backups stored for 7 days',
'8GB database space included',
'100GB file storage included',
'50GB bandwidth included',
'5GB file uploads included',
'100,000 monthly active users included',
'2M Edge Function invocations included',
'500 concurrent Realtime connections included',
'5 million Realtime messages included',
'7-day log retention',
'Email support',
],
features: [
'No project pausing',
'Daily backups stored for 7 days',
'8GB database space included',
'100GB file storage included',
'50GB bandwidth included',
'250GB bandwidth included',
'5GB file uploads included',
'100,000 monthly active users included',
'2M Edge Function invocations included',
@@ -94,9 +78,9 @@ export const plans: PricingInformation[] = [
id: 'tier_team',
name: 'Team',
nameBadge: 'New',
costUnit: '/ month',
costUnitOrg: '/ month',
href: 'https://forms.supabase.com/team',
costUnit: '/ month / org',
costUnitOrg: '/ month / org',
href: 'https://supabase.com/dashboard/new?plan=team',
priceLabel: 'From',
warning: 'Usage-based plan',
priceMonthly: 599,
@@ -113,7 +97,7 @@ export const plans: PricingInformation[] = [
],
footer: 'Additional fees apply for usage beyond included usage.',
preface: 'Everything in the Pro plan, plus:',
cta: 'Contact Us',
cta: 'Get Started',
},
{
id: 'tier_enterprise',
@@ -125,6 +109,7 @@ export const plans: PricingInformation[] = [
`SSO/SAML`,
`On-premise support`,
`24×7×365 premium enterprise support`,
'Custom Security Questionnaires',
`Private Slack channel`,
],
priceLabel: '',
+5 -18
View File
@@ -112,14 +112,14 @@ export const pricing: Pricing = {
usage_based: false,
},
{
title: 'Database egress',
title: 'Bandwidth',
tooltips: {
main: 'Billing is based on the total sum of outgoing traffic of your database in GB throughout your billing period.',
main: 'Billing is based on the total sum of outgoing traffic (includes Database, Storage, Realtime, Auth) in GB throughout your billing period.',
},
plans: {
free: '2GB included',
pro: '50 GB included, then $0.09 per GB',
team: '50 GB included, then $0.09 per GB',
free: '5GB included',
pro: '250 GB included, then $0.09 per GB',
team: '250 GB included, then $0.09 per GB',
enterprise: 'Unlimited',
},
usage_based: true,
@@ -234,19 +234,6 @@ export const pricing: Pricing = {
},
usage_based: true,
},
{
title: 'Storage egress',
tooltips: {
main: 'All requests to view and download your storage items go through our CDN. We sum up all outgoing traffic (egress) for storage related requests through our CDN. We do not differentiate between cache and no cache hits.\nBilling is based on the total amount of egress in GB throughout your billing period.',
},
plans: {
free: '2 GB included',
pro: '200 GB included, then $0.09 per GB',
team: '200 GB included, then $0.09 per GB',
enterprise: 'Unlimited',
},
usage_based: true,
},
{
title: 'Custom access controls',
plans: {
-123
View File
@@ -1,123 +0,0 @@
import HCaptcha from '@hcaptcha/react-hcaptcha'
import { Elements } from '@stripe/react-stripe-js'
import { loadStripe } from '@stripe/stripe-js'
import { useTheme } from 'common'
import { observer } from 'mobx-react-lite'
import { useRouter } from 'next/router'
import { useCallback, useEffect, useState } from 'react'
import { NewOrgForm } from 'components/interfaces/Organization'
import { WizardLayout } from 'components/layouts'
import { useSetupIntent } from 'data/stripe/setup-intent-mutation'
import { useFlag } from 'hooks'
import { STRIPE_PUBLIC_KEY } from 'lib/constants'
import { useIsHCaptchaLoaded } from 'stores/hcaptcha-loaded-store'
import { NextPageWithLayout } from 'types'
const stripePromise = loadStripe(STRIPE_PUBLIC_KEY)
/**
* No org selected yet, create a new one
*/
const Wizard: NextPageWithLayout = () => {
const router = useRouter()
const { isDarkMode } = useTheme()
const [intent, setIntent] = useState<any>()
const captchaLoaded = useIsHCaptchaLoaded()
const orgCreationV2 = useFlag('orgcreationv2')
const [captchaToken, setCaptchaToken] = useState<string | null>(null)
const [captchaRef, setCaptchaRef] = useState<HCaptcha | null>(null)
const { mutate: setupIntent } = useSetupIntent({ onSuccess: (res) => setIntent(res) })
const captchaRefCallback = useCallback((node) => {
setCaptchaRef(node)
}, [])
useEffect(() => {
if (orgCreationV2 === false) {
router.push('/new')
}
}, [orgCreationV2])
const initSetupIntent = async (hcaptchaToken: string | undefined) => {
if (!hcaptchaToken) return console.error('Hcaptcha token is required')
// Force a reload of Elements, necessary for Stripe
setIntent(undefined)
setupIntent({ hcaptchaToken })
}
const options = {
clientSecret: intent ? intent.client_secret : '',
appearance: { theme: isDarkMode ? 'night' : 'flat', labels: 'floating' },
} as any
const loadPaymentForm = async () => {
if (captchaRef && captchaLoaded) {
let token = captchaToken
try {
if (!token) {
const captchaResponse = await captchaRef.execute({ async: true })
token = captchaResponse?.response ?? null
}
} catch (error) {
return
}
await initSetupIntent(token ?? undefined)
resetCaptcha()
}
}
useEffect(() => {
loadPaymentForm()
}, [captchaRef, captchaLoaded])
const resetSetupIntent = () => {
return loadPaymentForm()
}
const onLocalCancel = () => {
setIntent(undefined)
}
const resetCaptcha = () => {
setCaptchaToken(null)
captchaRef?.resetCaptcha()
}
return (
<>
<HCaptcha
ref={captchaRefCallback}
sitekey={process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY!}
size="invisible"
onVerify={(token) => {
setCaptchaToken(token)
}}
onClose={onLocalCancel}
onExpire={() => {
setCaptchaToken(null)
}}
/>
{intent && (
<Elements stripe={stripePromise} options={options}>
<NewOrgForm onPaymentMethodReset={() => resetSetupIntent()} />
</Elements>
)}
</>
)
}
Wizard.getLayout = (page) => (
<WizardLayout organization={null} project={null}>
{page}
</WizardLayout>
)
export default observer(Wizard)