feat: choose country before
This commit is contained in:
parent
3cc4ea22a9
commit
4ce5be7a4c
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}, []);
|
||||
|
|
|
|||
|
|
@ -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 d’Ivoire' },
|
||||
{ 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' },
|
||||
];
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue