diff --git a/apps/backend/src/api/routes/marketplace.controller.ts b/apps/backend/src/api/routes/marketplace.controller.ts
index fa249726..a9d500e5 100644
--- a/apps/backend/src/api/routes/marketplace.controller.ts
+++ b/apps/backend/src/api/routes/marketplace.controller.ts
@@ -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')
diff --git a/apps/frontend/src/components/marketplace/seller.tsx b/apps/frontend/src/components/marketplace/seller.tsx
index 0f9307de..df6b761d 100644
--- a/apps/frontend/src/components/marketplace/seller.tsx
+++ b/apps/frontend/src/components/marketplace/seller.tsx
@@ -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 (
+
+ Please select your country where your business is registered.
+
+
+ THIS IS IRREVERSIBLE.
+
+
+
+
+ );
+};
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(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: ,
+ });
+ return;
+ }
+
+ connectBankAccountLink('');
+ }, [data, connectBankAccountLink]);
+
useEffect(() => {
loadItems();
}, []);
diff --git a/libraries/nestjs-libraries/src/services/stripe.country.list.ts b/libraries/nestjs-libraries/src/services/stripe.country.list.ts
new file mode 100644
index 00000000..8ced7d41
--- /dev/null
+++ b/libraries/nestjs-libraries/src/services/stripe.country.list.ts
@@ -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' },
+];
diff --git a/libraries/nestjs-libraries/src/services/stripe.service.ts b/libraries/nestjs-libraries/src/services/stripe.service.ts
index 406766d1..588292f3 100644
--- a/libraries/nestjs-libraries/src/services/stripe.service.ts
+++ b/libraries/nestjs-libraries/src/services/stripe.service.ts
@@ -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,