feat: choose country before

This commit is contained in:
Nevo David 2024-05-15 23:40:59 +07:00
parent 3cc4ea22a9
commit 4ce5be7a4c
4 changed files with 265 additions and 56 deletions

View File

@ -53,8 +53,11 @@ export class MarketplaceController {
}
@Get('/bank')
connectBankAccount(@GetUserFromRequest() user: User) {
return this._stripeService.createAccountProcess(user.id, user.email);
connectBankAccount(
@GetUserFromRequest() user: User,
@Query('country') country: string
) {
return this._stripeService.createAccountProcess(user.id, user.email, country);
}
@Post('/item')

View File

@ -4,12 +4,53 @@ import { Slider } from '@gitroom/react/form/slider';
import { Button } from '@gitroom/react/form/button';
import { tagsList } from '@gitroom/nestjs-libraries/database/prisma/marketplace/tags.list';
import { Options } from '@gitroom/frontend/components/marketplace/buyer';
import { ChangeEvent, useCallback, useEffect, useState } from 'react';
import { ChangeEvent, FC, useCallback, useEffect, useState } from 'react';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR from 'swr';
import { Input } from '@gitroom/react/form/input';
import { useDebouncedCallback } from 'use-debounce';
import { OrderList } from '@gitroom/frontend/components/marketplace/order.list';
import { useModals } from '@mantine/modals';
import { Select } from '@gitroom/react/form/select';
import { countries } from '@gitroom/nestjs-libraries/services/stripe.country.list';
export const AddAccount: FC<{ openBankAccount: (country: string) => void }> = (
props
) => {
const { openBankAccount } = props;
const [country, setCountry] = useState('');
return (
<div className="bg-sixth p-[32px] text-[20px] w-full max-w-[600px] mx-auto flex flex-col gap-[24px] rounded-[4px] border border-[#172034] relative">
Please select your country where your business is registered.
<br />
<span className="bg-red-400 text-red-950 font-[600]">
THIS IS IRREVERSIBLE.
</span>
<Select
label="Country"
name="country"
disableForm={true}
value={country}
onChange={(e) => setCountry(e.target.value)}
>
<option value="">--SELECT COUNTRY--</option>
{countries.map((country) => (
<option key={country.value} value={country.value}>
{country.label}
</option>
))}
</Select>
<Button
className="w-full"
disabled={!country}
type="button"
onClick={() => openBankAccount(country)}
>
Connect Bank Account
</Button>
</div>
);
};
export const Seller = () => {
const fetch = useFetch();
@ -20,6 +61,7 @@ export const Seller = () => {
const [connectedLoading, setConnectedLoading] = useState(false);
const [state, setState] = useState(true);
const [audience, setAudience] = useState<number>(0);
const modals = useModals();
const accountInformation = useCallback(async () => {
const account = await (
@ -43,10 +85,10 @@ export const Seller = () => {
});
}, []);
const connectBankAccount = useCallback(async () => {
const connectBankAccountLink = useCallback(async (country: string) => {
setConnectedLoading(true);
const { url } = await (
await fetch('/marketplace/bank', {
await fetch(`/marketplace/bank?country=${country}`, {
method: 'GET',
})
).json();
@ -98,6 +140,22 @@ export const Seller = () => {
const { data } = useSWR('/marketplace/account', accountInformation);
const connectBankAccount = useCallback(async () => {
if (!data?.account) {
modals.openModal({
size: '100%',
classNames: {
modal: 'bg-transparent text-white',
},
withCloseButton: false,
children: <AddAccount openBankAccount={connectBankAccountLink} />,
});
return;
}
connectBankAccountLink('');
}, [data, connectBankAccountLink]);
useEffect(() => {
loadItems();
}, []);

View File

@ -0,0 +1,111 @@
export const countries = [
{ value: 'AL', label: 'Albania' },
{ value: 'AG', label: 'Antigua & Barbuda' },
{ value: 'AR', label: 'Argentina' },
{ value: 'AM', label: 'Armenia' },
{ value: 'AU', label: 'Australia' },
{ value: 'AT', label: 'Austria' },
{ value: 'BS', label: 'Bahamas' },
{ value: 'BH', label: 'Bahrain' },
{ value: 'BE', label: 'Belgium' },
{ value: 'BJ', label: 'Benin' },
{ value: 'BO', label: 'Bolivia' },
{ value: 'BA', label: 'Bosnia & Herzegovina' },
{ value: 'BW', label: 'Botswana' },
{ value: 'BN', label: 'Brunei' },
{ value: 'BG', label: 'Bulgaria' },
{ value: 'KH', label: 'Cambodia' },
{ value: 'CA', label: 'Canada' },
{ value: 'CL', label: 'Chile' },
{ value: 'CO', label: 'Colombia' },
{ value: 'CR', label: 'Costa Rica' },
{ value: 'HR', label: 'Croatia' },
{ value: 'CY', label: 'Cyprus' },
{ value: 'CZ', label: 'Czech Republic' },
{ value: 'CI', label: 'Côte dIvoire' },
{ value: 'DK', label: 'Denmark' },
{ value: 'DO', label: 'Dominican Republic' },
{ value: 'EC', label: 'Ecuador' },
{ value: 'EG', label: 'Egypt' },
{ value: 'SV', label: 'El Salvador' },
{ value: 'EE', label: 'Estonia' },
{ value: 'ET', label: 'Ethiopia' },
{ value: 'FI', label: 'Finland' },
{ value: 'FR', label: 'France' },
{ value: 'GM', label: 'Gambia' },
{ value: 'DE', label: 'Germany' },
{ value: 'GH', label: 'Ghana' },
{ value: 'GI', label: 'Gibraltar' },
{ value: 'GR', label: 'Greece' },
{ value: 'GT', label: 'Guatemala' },
{ value: 'GY', label: 'Guyana' },
{ value: 'HK', label: 'Hong Kong SAR China' },
{ value: 'HU', label: 'Hungary' },
{ value: 'IS', label: 'Iceland' },
{ value: 'IN', label: 'India' },
{ value: 'ID', label: 'Indonesia' },
{ value: 'IE', label: 'Ireland' },
{ value: 'IL', label: 'Israel' },
{ value: 'IT', label: 'Italy' },
{ value: 'JM', label: 'Jamaica' },
{ value: 'JP', label: 'Japan' },
{ value: 'JO', label: 'Jordan' },
{ value: 'KE', label: 'Kenya' },
{ value: 'KW', label: 'Kuwait' },
{ value: 'LV', label: 'Latvia' },
{ value: 'LI', label: 'Liechtenstein' },
{ value: 'LT', label: 'Lithuania' },
{ value: 'LU', label: 'Luxembourg' },
{ value: 'MO', label: 'Macao SAR China' },
{ value: 'MG', label: 'Madagascar' },
{ value: 'MY', label: 'Malaysia' },
{ value: 'MT', label: 'Malta' },
{ value: 'MU', label: 'Mauritius' },
{ value: 'MX', label: 'Mexico' },
{ value: 'MD', label: 'Moldova' },
{ value: 'MC', label: 'Monaco' },
{ value: 'MN', label: 'Mongolia' },
{ value: 'MA', label: 'Morocco' },
{ value: 'NA', label: 'Namibia' },
{ value: 'NL', label: 'Netherlands' },
{ value: 'NZ', label: 'New Zealand' },
{ value: 'NG', label: 'Nigeria' },
{ value: 'MK', label: 'North Macedonia' },
{ value: 'NO', label: 'Norway' },
{ value: 'OM', label: 'Oman' },
{ value: 'PK', label: 'Pakistan' },
{ value: 'PA', label: 'Panama' },
{ value: 'PY', label: 'Paraguay' },
{ value: 'PE', label: 'Peru' },
{ value: 'PH', label: 'Philippines' },
{ value: 'PL', label: 'Poland' },
{ value: 'PT', label: 'Portugal' },
{ value: 'QA', label: 'Qatar' },
{ value: 'RO', label: 'Romania' },
{ value: 'RW', label: 'Rwanda' },
{ value: 'SA', label: 'Saudi Arabia' },
{ value: 'SN', label: 'Senegal' },
{ value: 'RS', label: 'Serbia' },
{ value: 'SG', label: 'Singapore' },
{ value: 'SK', label: 'Slovakia' },
{ value: 'SI', label: 'Slovenia' },
{ value: 'ZA', label: 'South Africa' },
{ value: 'KR', label: 'South Korea' },
{ value: 'ES', label: 'Spain' },
{ value: 'LK', label: 'Sri Lanka' },
{ value: 'LC', label: 'St. Lucia' },
{ value: 'SE', label: 'Sweden' },
{ value: 'CH', label: 'Switzerland' },
{ value: 'TW', label: 'Taiwan' },
{ value: 'TZ', label: 'Tanzania' },
{ value: 'TH', label: 'Thailand' },
{ value: 'TT', label: 'Trinidad & Tobago' },
{ value: 'TN', label: 'Tunisia' },
{ value: 'TR', label: 'Turkey' },
{ value: 'AE', label: 'United Arab Emirates' },
{ value: 'GB', label: 'United Kingdom' },
{ value: 'US', label: 'United States' },
{ value: 'UY', label: 'Uruguay' },
{ value: 'UZ', label: 'Uzbekistan' },
{ value: 'VN', label: 'Vietnam' },
];

View File

@ -135,32 +135,44 @@ export class StripeService {
expand: ['data.prices'],
});
const findProduct = allProducts.data.find(
(product) => product.name.toUpperCase() === body.billing.toUpperCase()
) || await stripe.products.create({
active: true,
name: body.billing,
});
const findProduct =
allProducts.data.find(
(product) => product.name.toUpperCase() === body.billing.toUpperCase()
) ||
(await stripe.products.create({
active: true,
name: body.billing,
}));
const pricesList = await stripe.prices.list({
active: true,
product: findProduct!.id,
});
const findPrice = pricesList.data.find(
(p) =>
p?.recurring?.interval?.toLowerCase() === (body.period === 'MONTHLY' ? 'month' : 'year') &&
p?.unit_amount === (body.period === 'MONTHLY' ? priceData.month_price : priceData.year_price) * 100
) || await stripe.prices.create({
active: true,
product: findProduct!.id,
currency: 'usd',
nickname: body.billing + ' ' + body.period,
unit_amount: (body.period === 'MONTHLY' ? priceData.month_price : priceData.year_price) * 100,
recurring: {
interval: body.period === 'MONTHLY' ? 'month' : 'year',
},
});
const findPrice =
pricesList.data.find(
(p) =>
p?.recurring?.interval?.toLowerCase() ===
(body.period === 'MONTHLY' ? 'month' : 'year') &&
p?.unit_amount ===
(body.period === 'MONTHLY'
? priceData.month_price
: priceData.year_price) *
100
) ||
(await stripe.prices.create({
active: true,
product: findProduct!.id,
currency: 'usd',
nickname: body.billing + ' ' + body.period,
unit_amount:
(body.period === 'MONTHLY'
? priceData.month_price
: priceData.year_price) * 100,
recurring: {
interval: body.period === 'MONTHLY' ? 'month' : 'year',
},
}));
const proration_date = Math.floor(Date.now() / 1000);
@ -269,29 +281,39 @@ export class StripeService {
return { url };
}
async createAccountProcess(userId: string, email: string) {
async createAccountProcess(userId: string, email: string, country: string) {
const account =
(await this._subscriptionService.getUserAccount(userId))?.account ||
(await this.createAccount(userId, email));
(await this.createAccount(userId, email, country));
return { url: await this.addBankAccount(account) };
}
async createAccount(userId: string, email: string) {
async createAccount(userId: string, email: string, country: string) {
const account = await stripe.accounts.create({
controller: {
stripe_dashboard: {
type: 'express',
type: 'custom',
// controller: {
// stripe_dashboard: {
// type: 'express',
// },
// fees: {
// payer: 'application',
// },
// losses: {
// payments: 'application',
// },
// },
capabilities: {
transfers: {
requested: true,
},
fees: {
payer: 'application',
},
losses: {
payments: 'application',
card_payments: {
requested: true,
},
},
metadata: {
service: 'gitroom',
},
country,
email,
});
@ -306,6 +328,9 @@ export class StripeService {
refresh_url: process.env['FRONTEND_URL'] + '/marketplace/seller',
return_url: process.env['FRONTEND_URL'] + '/marketplace/seller',
type: 'account_onboarding',
collection_options: {
fields: 'eventually_due',
},
});
return accountLink.url;
@ -378,32 +403,44 @@ export class StripeService {
expand: ['data.prices'],
});
const findProduct = allProducts.data.find(
(product) => product.name.toUpperCase() === body.billing.toUpperCase()
) || await stripe.products.create({
active: true,
name: body.billing,
});
const findProduct =
allProducts.data.find(
(product) => product.name.toUpperCase() === body.billing.toUpperCase()
) ||
(await stripe.products.create({
active: true,
name: body.billing,
}));
const pricesList = await stripe.prices.list({
active: true,
product: findProduct!.id,
});
const findPrice = pricesList.data.find(
(p) =>
p?.recurring?.interval?.toLowerCase() === (body.period === 'MONTHLY' ? 'month' : 'year') &&
p?.unit_amount === (body.period === 'MONTHLY' ? priceData.month_price : priceData.year_price) * 100
) || await stripe.prices.create({
active: true,
product: findProduct!.id,
currency: 'usd',
nickname: body.billing + ' ' + body.period,
unit_amount: (body.period === 'MONTHLY' ? priceData.month_price : priceData.year_price) * 100,
recurring: {
interval: body.period === 'MONTHLY' ? 'month' : 'year',
},
});
const findPrice =
pricesList.data.find(
(p) =>
p?.recurring?.interval?.toLowerCase() ===
(body.period === 'MONTHLY' ? 'month' : 'year') &&
p?.unit_amount ===
(body.period === 'MONTHLY'
? priceData.month_price
: priceData.year_price) *
100
) ||
(await stripe.prices.create({
active: true,
product: findProduct!.id,
currency: 'usd',
nickname: body.billing + ' ' + body.period,
unit_amount:
(body.period === 'MONTHLY'
? priceData.month_price
: priceData.year_price) * 100,
recurring: {
interval: body.period === 'MONTHLY' ? 'month' : 'year',
},
}));
const currentUserSubscription = await stripe.subscriptions.list({
customer,