Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supersonic web #2077

Merged
merged 1 commit into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
51 changes: 30 additions & 21 deletions hosting-holium-com/src/pages/payment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import {
OnboardingPage,
OnboardingStorage,
PaymentDialog,
ThirdEarthPeriodicity,
ThirdEarthProduct,
ThirdEarthProductId,
ThirdEarthProductType,
} from '@holium/shared';

Expand All @@ -16,8 +18,7 @@ import { thirdEarthApi } from '../util/thirdEarthApi';
import { useNavigation } from '../util/useNavigation';

type ServerSideProps = {
products: ThirdEarthProduct[];
// We use underscore to highlight that this is a query param.
product: ThirdEarthProduct | undefined;
product_type: ThirdEarthProductType;
back_url?: string;
};
Expand All @@ -28,17 +29,22 @@ export const getServerSideProps: GetServerSideProps = async ({ query }) => {
const product_type = (query.product_type ??
'planet') as ThirdEarthProductType;

const product = products.find((p) => {
if (product_type === 'planet') return p.id === ThirdEarthProductId.PLANET;
else return p.id === ThirdEarthProductId.BYOP_P;
});

return {
props: {
products,
product,
product_type,
back_url,
} as ServerSideProps,
};
};

export default function Payment({
products: unfilteredProducts,
product,
product_type,
back_url,
}: ServerSideProps) {
Expand All @@ -51,11 +57,7 @@ export default function Payment({
const [stripe, setStripe] = useState<Stripe>();
const [clientSecret, setClientSecret] = useState<string>();

const products = unfilteredProducts.filter(
(product) => product.product_type === product_type
);

const [productId, setProductId] = useState(products[0].id);
const [periodicity, setPeriodicity] = useState(ThirdEarthPeriodicity.MONTH);
const [invoiceId, setInvoiceId] = useState<string>();

useEffect(() => {
Expand All @@ -72,9 +74,17 @@ export default function Payment({
setToken(token);

const getSecretAndSetupStripe = async () => {
if (!product) return;

const priceOption = product.price_options.find(
(po) => po.periodicity === periodicity
);
if (!priceOption) return;

const response = await thirdEarthApi.stripeMakePayment(
token,
productId.toString(),
product.id.toString(),
priceOption.stripe_price_id,
// Don't pass serverId for byop-p.
product_type !== 'byop-p' && serverId ? serverId : undefined
);
Expand All @@ -90,7 +100,7 @@ export default function Payment({
} catch (e) {
console.error(e);
}
}, [productId]);
}, [periodicity]);

const stripeOptions: StripeElementsOptions = {
clientSecret: clientSecret,
Expand All @@ -112,15 +122,15 @@ export default function Payment({
};

const onNext = async () => {
if (!token || !invoiceId || !productId) return false;
if (!token || !invoiceId || !product) return false;

if (product_type === 'byop-p') {
await thirdEarthApi.log(token, {
file: 'purchases',
type: 'info',
subject: 'FRONTEND: payment step (email notify)',
message: `Succesful stripe purchase of byop-p by ${email}.`,
productId: productId.toString(),
productId: product.id.toString(),
auditTrailCode: 1000,
});

Expand All @@ -136,7 +146,7 @@ export default function Payment({
// This server call should be non-blocking since the user has already paid.
await thirdEarthApi.provisionalShipEntry({
token,
product: productId.toString(),
product: product.id.toString(),
invoiceId,
shipType: 'provisional',
});
Expand All @@ -156,7 +166,7 @@ export default function Payment({
type: 'info',
subject: 'FRONTEND: payment step (email notify)',
message: `Succesful stripe purchase of planet by ${email}.`,
productId: productId.toString(),
productId: product.id.toString(),
auditTrailCode: 1000,
});

Expand All @@ -174,7 +184,7 @@ export default function Payment({
await thirdEarthApi.provisionalShipEntry({
token,
patp: serverId,
product: productId.toString(),
product: product.id.toString(),
invoiceId,
shipType: 'planet',
});
Expand All @@ -191,14 +201,13 @@ export default function Payment({
return (
<Page title="Payment" isProtected>
<PaymentDialog
productType={product_type}
products={products}
productId={productId}
patp={serverId}
priceOptions={product?.price_options ?? []}
periodicity={periodicity}
setPeriodicity={setPeriodicity}
patp={product_type === 'planet' ? serverId : undefined}
email={email}
stripe={stripe as any}
stripeOptions={stripeOptions as any}
setProductId={setProductId}
onBack={onBack}
onNext={onNext}
/>
Expand Down
40 changes: 16 additions & 24 deletions shared/src/onboarding/dialogs/Payment/PaymentDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,30 @@ import { Flex, Spinner } from '@holium/design-system/general';
import { OnboardDialog } from '../../components/OnboardDialog';
import { OnboardDialogTitle } from '../../components/OnboardDialog.styles';
import { PaymentIcon } from '../../icons/PaymentIcon';
import { ThirdEarthProduct, ThirdEarthProductType } from '../../types';
import { ThirdEarthPeriodicity, ThirdEarthPriceOption } from '../../types';
import { AccountInformation } from './AccountInformation';
import { PaymentForm } from './PaymentForm';
import { ProductCards } from './ProductCards';

type Props = {
productType: ThirdEarthProductType;
products: ThirdEarthProduct[];
productId: number;
patp: string;
priceOptions: ThirdEarthPriceOption[];
periodicity: ThirdEarthPeriodicity;
setPeriodicity: (periodicity: ThirdEarthPeriodicity) => void;
patp: string | undefined;
email: string;
stripe: Stripe | undefined;
stripeOptions: StripeElementsOptions | undefined;
setProductId: (productId: number) => void;
onBack: () => void;
onNext: () => Promise<boolean>;
};

const PaymentDialogPresenter = ({
productType,
products,
productId,
priceOptions,
periodicity,
setPeriodicity,
patp,
email,
stripeOptions,
setProductId,
onBack,
onNext,
}: Props) => {
Expand Down Expand Up @@ -84,14 +82,11 @@ const PaymentDialogPresenter = ({
<>
<OnboardDialogTitle>Payment</OnboardDialogTitle>
<ProductCards
products={products}
productId={productId}
setProductId={setProductId}
/>
<AccountInformation
patp={productType === 'planet' ? patp : undefined}
email={email}
priceOptions={priceOptions}
periodicity={periodicity}
setPeriodicity={setPeriodicity}
/>
<AccountInformation patp={patp} email={email} />
<PaymentForm />
</>
}
Expand All @@ -111,14 +106,11 @@ export const PaymentDialog = ({ stripe, stripeOptions, ...props }: Props) => {
<>
<OnboardDialogTitle>Payment</OnboardDialogTitle>
<ProductCards
products={props.products}
productId={props.productId}
setProductId={props.setProductId}
/>
<AccountInformation
patp={props.productType === 'planet' ? props.patp : undefined}
email={props.email}
priceOptions={props.priceOptions}
periodicity={props.periodicity}
setPeriodicity={props.setPeriodicity}
/>
<AccountInformation patp={props.patp} email={props.email} />
<Flex justifyContent="center" alignItems="center" my={30}>
<Spinner size={3} />
</Flex>
Expand Down
59 changes: 28 additions & 31 deletions shared/src/onboarding/dialogs/Payment/ProductCards.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,41 @@
import { useEffect } from 'react';

import { Flex } from '@holium/design-system/general';

import { ThirdEarthProduct } from '../../types/index';
import {
ThirdEarthPeriodicity,
ThirdEarthPriceOption,
} from '../../types/index';
import { ProductCard } from './ProductCard';

type Props = {
products: ThirdEarthProduct[];
productId: number;
setProductId: (productId: number) => void;
priceOptions: ThirdEarthPriceOption[];
periodicity: ThirdEarthPeriodicity;
setPeriodicity: (periodicity: ThirdEarthPeriodicity) => void;
};

export const ProductCards = ({ products, productId, setProductId }: Props) => {
const byopProduct = products.find(
(product) => product.product_type === 'byop-p'
);

useEffect(() => {
if (byopProduct) {
setProductId(byopProduct.id);
}
}, [byopProduct, setProductId]);
export const ProductCards = ({
priceOptions,
periodicity,
setPeriodicity,
}: Props) => (
<Flex gap={16}>
{priceOptions.sort().map((priceOption) => {
const isMonthly = priceOption.periodicity === ThirdEarthPeriodicity.MONTH;
const isSelected = priceOption.periodicity === periodicity;

return (
<Flex gap={16}>
{/* Assume the cheapest is monthly and the second yearly. */}
{products.sort().map((product, index) => (
return (
<ProductCard
key={product.id}
h2Text={`$${product.subscription_price}.00`}
bodyText={index === 0 ? 'Monthly' : 'Yearly'}
key={priceOption.stripe_price_id}
h2Text={`$${priceOption.recurring_price}.00`}
bodyText={isMonthly ? 'Monthly' : 'Yearly'}
hintText={
index === 0
? `$${product.subscription_price * 12} yearly`
isMonthly
? `$${priceOption.recurring_price * 12} yearly`
: undefined
}
isSelected={productId === product.id}
onClick={() => setProductId(product.id)}
isSelected={isSelected}
onClick={() => setPeriodicity(priceOption.periodicity)}
/>
))}
</Flex>
);
};
);
})}
</Flex>
);
24 changes: 23 additions & 1 deletion shared/src/onboarding/dialogs/util.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { ReactNode } from 'react';

import { Flex } from '@holium/design-system/general';
import { ThirdEarthProduct, ThirdEarthShip } from '@holium/shared';
import {
ThirdEarthPeriodicity,
ThirdEarthProduct,
ThirdEarthShip,
} from '@holium/shared';

export const OnboardingDialogWrapper = ({
children,
Expand Down Expand Up @@ -43,6 +47,24 @@ export const thirdEarthMockProduct: ThirdEarthProduct = {
long_description: 'Monthly subscription',
price_id: '11',
subscription_price: 15,
price_options: [
{
unit: 'usd',
description: 'monthly subscription',
periodicity: 'month' as ThirdEarthPeriodicity,
one_time_price: 0,
recurring_price: 15,
stripe_price_id: 'price_1MDqoIHhoM3uGGuYAZZN23Yr',
},
{
unit: 'usd',
description: 'yearly subscription',
periodicity: 'year' as ThirdEarthPeriodicity,
one_time_price: 0,
recurring_price: 150,
stripe_price_id: 'price_1MDqoIHhoM3uGGuY00mWc29l',
},
],
};

export const thirdEarthMockProducts = [
Expand Down
2 changes: 2 additions & 0 deletions shared/src/onboarding/onboarding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ export type {
RealmOnboardingStep,
ThirdEarthAlert,
ThirdEarthPortalSession,
ThirdEarthPriceOption,
ThirdEarthProduct,
ThirdEarthProductType,
ThirdEarthShip,
} from './types';
export { ThirdEarthPeriodicity, ThirdEarthProductId } from './types/index';
8 changes: 7 additions & 1 deletion shared/src/onboarding/services/ThirdEarthApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,20 @@ export class ThirdEarthApi {
);
}

stripeMakePayment(token: string, productId: string, patp?: string) {
stripeMakePayment(
token: string,
productId: string,
priceId: string,
patp?: string
) {
return http<StripeMakePaymentResponse>(
`${this.apiBaseUrl}/stripe-make-payment`,
{
method: 'POST',
headers: this.getHeaders(token),
body: JSON.stringify({
productId,
priceId,
patp,
}),
}
Expand Down
Loading