首页 > web前端 > js教程 > 使用 Stripe Connect 创建市场:上线流程

使用 Stripe Connect 创建市场:上线流程

Susan Sarandon
发布: 2024-11-28 09:55:13
原创
307 人浏览过

介绍

考虑到没有多少支付处理商提供它,创建一个市场可能太难了,或者是不可能的,如果他们不提供它,那么你很可能会在他们听到风声的那一刻就被踢出平台,即使没有如果您没有坚实的基础来处理使用该平台的卖家的付款、退款和付款,那么创建一个市场是有风险的。

Stripe Connect 解决了这些问题,它将使我们能够创建一个基本的市场,您可以在其中注册成为卖家,并且客户可以轻松地从这些卖家那里购买商品。作为平台所有者,您还可以设置服务费,因此当用户 X 从商店 Y 购买商品时,我们将获得该交易的 X% 分成,但稍后会详细介绍。

Creating a marketplace with Stripe Connect: The onboard process

设置项目

为了处理数据库连接,我们使用 Prisma,身份验证由 remix-auth 处理,对于这部分,我们仅处理市场的卖家端。

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Store {
  id         String   @id // This will be the store's subdomain
  name       String
  updated_at DateTime @default(now()) @updatedAt
  seller     Seller?
}

model Seller {
  id           Int      @id @default(autoincrement())
  email        String
  password     String
  store        Store    @relation(fields: [store_id], references: [id])
  date_created DateTime @default(now())
  date_updated DateTime @updatedAt
  store_id     String   @unique
}
登录后复制
登录后复制
登录后复制

这就是我们的 schema.prisma 文件的样子,我们有一个卖家模型和一个与之相关的商店模型,“id”字段将用作子域,因此当我们到达买家一侧时,我将能够访问 store.localhost.com 并从那里的卖家那里购买产品。
我们还将添加一个 Stripe 模型,它将存储有关卖家 Connect 帐户的数据。

model Stripe {
  account_id String @id
  is_onboarded Boolean @default(false)
  user Users @relation(fields: [user_id], references: [discord_id])
  user_id String @unique
  created_at DateTime @default(now())
  updated_at DateTime @updatedAt
}

model Seller {
  id           Int      @id @default(autoincrement())
  email        String
  password     String
  store        Store    @relation(fields: [store_id], references: [id])
  date_created DateTime @default(now())
  date_updated DateTime @updatedAt
  store_id     String   @unique
  stripe       Stripe?
}
登录后复制
登录后复制
登录后复制

现在我们可以处理用户入门问题了,所以让我们在 .env 文件中定义另一个变量。

STRIPE_SK=your stripe secret key here
登录后复制
登录后复制

您可以通过在 Stripe 的开发页面中生成 Stripe 密钥来获取它,最好创建一个目前仅允许使用 Stripe Connect 的受限密钥。

然后您需要创建一个新文件来导出 Stripe 客户端,以便我们的路由可以使用它

// app/libs/stripe.server.ts
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SK)
登录后复制
登录后复制

我们将创建一条位于“/onboarding”的新路线

// app/routes/onboarding.tsx

export default function Onboarding() {
    const {stripe} = useLoaderData();

    return <div className={'text-center pt-[6%]'}>
    <h1 className={'text-xl'}>Account onboarded: {stripe?.is_onboarded ? stripe?.account_id : '? Not connected'}</h1>
        <div className={'flex items-center  text-white text-sm  mt-5 justify-center gap-3'}>
            {!stripe ? <>
                <Form method={'post'}>
                    <button type={'submit'} className={'bg-blue-600 hover:cursor-pointer  rounded-[6px] px-4 py-1.5'}>Setup your seller
                        account
                    </button>

                </Form>
            </> : <>
                <div className={'bg-blue-600 rounded-[6px] px-4 py-1.5'}>Seller dashboard</div>

            </>}
        </div>
    </div>
}
登录后复制
登录后复制

我们将添加一个加载器函数,该函数将传递有关卖家入职状态的数据

export async function loader({request}: LoaderFunctionArgs) {
    const user = await authenticator.isAuthenticated(request, {
        failureRedirect: '/login'
    })

    const seller = await prisma.seller.findFirst({
        where: {
            id: user.id
        }, include: {
            stripe: true
        }
    })

    return {
        stripe: seller?.stripe
    }
}
登录后复制
登录后复制

现在,如果您转到/onboarding,它会说您尚未连接,您将能够按下按钮进行注册,这就是我们的操作功能的用武之地

export async function action({request}: ActionFunctionArgs) {
    const authenticated = await authenticator.isAuthenticated(request, {
        failureRedirect: '/login'
    })
    const seller = await prisma.seller.findFirst({
        where: {
            id: authenticated.id
        }, include: {
            stripe: true
        }
    })
    if (seller && seller.stripe?.is_onboarded) {
        return json({
            message: 'User is onboarded already',
            error: true
        }, {
            status: 400
        })
    }
    const account = seller?.stripe?.account_id ? {
        id: seller.stripe?.account_id
    } : await stripe.accounts.create({
        email: seller?.email,
        controller: {
            fees: {
                payer: 'application',
            },
            losses: {
                payments: 'application',
            },
            stripe_dashboard: {
                type: 'express',
            },
        },
    });
    if (!seller?.stripe?.account_id) {
        await prisma.seller.update({
            where: {
                id: authenticated.id
            },
            data: {
                stripe: {
                    create: {
                        account_id: account.id
                    }
                }
            }, include: {
                stripe: true
            }
        })
    }
    const accountLink = await stripe.accountLinks.create({
        account: account.id,
        refresh_url: 'http://localhost:5173/onboarding',
        return_url: 'http://localhost:5173/onboarding',
        type: 'account_onboarding',
        collection_options: {
            fields: 'eventually_due',
        },
    });
    console.debug(`[ACCOUNT ID = ${account.id}] CREATED ACCOUNT ONBOARDING LINK, REDIRECTING...`)

    return redirect(accountLink.url)
}
登录后复制
登录后复制

当卖家按下按钮时,我们将使用他们注册时使用的电子邮件创建一个帐户,然后我们将创建一个帐户链接,将他们重定向到入职页面(如果卖家已经附加了 Stripe 帐户,但尚未加入,那么我们也会将他们重定向到加入链接。

Creating a marketplace with Stripe Connect: The onboard process

从那里卖家将输入他的电子邮件/电话号码,然后入职流程将开始,Stripe 通常会询问卖家企业位置、企业详细信息、银行账户等...

现在我们可以监听 Stripe Connect 事件的 webhook,因此当卖家成功加入后,我们会将这些属性添加到数据库中的卖家记录中。

为了进行测试,您可以简单地下载 Stripe CLI,然后您可以将任何事件转发到我们现在将创建的新路由 /api/notifications

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Store {
  id         String   @id // This will be the store's subdomain
  name       String
  updated_at DateTime @default(now()) @updatedAt
  seller     Seller?
}

model Seller {
  id           Int      @id @default(autoincrement())
  email        String
  password     String
  store        Store    @relation(fields: [store_id], references: [id])
  date_created DateTime @default(now())
  date_updated DateTime @updatedAt
  store_id     String   @unique
}
登录后复制
登录后复制
登录后复制

当您运行该命令时,您将获得一个 Webhook 签名,以便我们可以验证 Stripe 发送给我们的每个 Webhook 的完整性,同样,如果您在 Stripe 上的开发人员门户上创建一个 Webhook,您将拥有一个秘密.

model Stripe {
  account_id String @id
  is_onboarded Boolean @default(false)
  user Users @relation(fields: [user_id], references: [discord_id])
  user_id String @unique
  created_at DateTime @default(now())
  updated_at DateTime @updatedAt
}

model Seller {
  id           Int      @id @default(autoincrement())
  email        String
  password     String
  store        Store    @relation(fields: [store_id], references: [id])
  date_created DateTime @default(now())
  date_updated DateTime @updatedAt
  store_id     String   @unique
  stripe       Stripe?
}
登录后复制
登录后复制
登录后复制

我们还将在 .env 文件中添加一个新变量

STRIPE_SK=your stripe secret key here
登录后复制
登录后复制

现在我们可以编写代码来处理 Stripe 发送给我们的这些事件

// app/libs/stripe.server.ts
import Stripe from 'stripe';
export const stripe = new Stripe(process.env.STRIPE_SK)
登录后复制
登录后复制

我们验证是否是 Stripe 发送了请求,如果是,那么我们继续,现在我们要关注的事件是 account.updated,该事件与我们在重定向卖家之前创建的帐户相关。

当卖家开始入职流程、添加电话号码、输入电子邮件或最终完成入职流程时,我们将收到“account.updated”事件,并且会发送此数组

account.requirements.currently_due

当“currently_due”数组的长度为零时,我们知道用户已完全注册,能够接受付款,因此从我们这边我们可以更新数据库并允许用户创建产品,但在此之前让我们添加'/api/notifications' 操作中的逻辑

// app/routes/onboarding.tsx

export default function Onboarding() {
    const {stripe} = useLoaderData();

    return <div className={'text-center pt-[6%]'}>
    <h1 className={'text-xl'}>Account onboarded: {stripe?.is_onboarded ? stripe?.account_id : '? Not connected'}</h1>
        <div className={'flex items-center  text-white text-sm  mt-5 justify-center gap-3'}>
            {!stripe ? <>
                <Form method={'post'}>
                    <button type={'submit'} className={'bg-blue-600 hover:cursor-pointer  rounded-[6px] px-4 py-1.5'}>Setup your seller
                        account
                    </button>

                </Form>
            </> : <>
                <div className={'bg-blue-600 rounded-[6px] px-4 py-1.5'}>Seller dashboard</div>

            </>}
        </div>
    </div>
}
登录后复制
登录后复制

一旦到位,我们就可以尝试加入并看看它是否有效。例如,一旦您输入地址,您就会在项目的控制台中看到一条消息,例如

export async function loader({request}: LoaderFunctionArgs) {
    const user = await authenticator.isAuthenticated(request, {
        failureRedirect: '/login'
    })

    const seller = await prisma.seller.findFirst({
        where: {
            id: user.id
        }, include: {
            stripe: true
        }
    })

    return {
        stripe: seller?.stripe
    }
}
登录后复制
登录后复制

这意味着主体已经过验证,并且我们已成功接收来自 Stripe 的事件,但让我们看看加入是否有效。

一旦你到达最后一步,它可能会说你的帐户详细信息不完整,最后一步是身份验证,因为这是测试模式,我们可以模拟

Creating a marketplace with Stripe Connect: The onboard process

好的,一旦我们完成了,我们将返回到上一页,我们可以按提交,按提交,我们将进入控制台

export async function action({request}: ActionFunctionArgs) {
    const authenticated = await authenticator.isAuthenticated(request, {
        failureRedirect: '/login'
    })
    const seller = await prisma.seller.findFirst({
        where: {
            id: authenticated.id
        }, include: {
            stripe: true
        }
    })
    if (seller && seller.stripe?.is_onboarded) {
        return json({
            message: 'User is onboarded already',
            error: true
        }, {
            status: 400
        })
    }
    const account = seller?.stripe?.account_id ? {
        id: seller.stripe?.account_id
    } : await stripe.accounts.create({
        email: seller?.email,
        controller: {
            fees: {
                payer: 'application',
            },
            losses: {
                payments: 'application',
            },
            stripe_dashboard: {
                type: 'express',
            },
        },
    });
    if (!seller?.stripe?.account_id) {
        await prisma.seller.update({
            where: {
                id: authenticated.id
            },
            data: {
                stripe: {
                    create: {
                        account_id: account.id
                    }
                }
            }, include: {
                stripe: true
            }
        })
    }
    const accountLink = await stripe.accountLinks.create({
        account: account.id,
        refresh_url: 'http://localhost:5173/onboarding',
        return_url: 'http://localhost:5173/onboarding',
        type: 'account_onboarding',
        collection_options: {
            fields: 'eventually_due',
        },
    });
    console.debug(`[ACCOUNT ID = ${account.id}] CREATED ACCOUNT ONBOARDING LINK, REDIRECTING...`)

    return redirect(accountLink.url)
}
登录后复制
登录后复制

成功了,现在 Stripe 将使我们返回到入门页面,它会向我们显示我们的帐户 ID,这意味着我们已成功入门,我们可以开始创建产品。

Creating a marketplace with Stripe Connect: The onboard process

好吧,让我们先让卖家仪表板按钮发挥作用,然后再继续讨论产品,创建一条位于 /portal 的新路线

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Store {
  id         String   @id // This will be the store's subdomain
  name       String
  updated_at DateTime @default(now()) @updatedAt
  seller     Seller?
}

model Seller {
  id           Int      @id @default(autoincrement())
  email        String
  password     String
  store        Store    @relation(fields: [store_id], references: [id])
  date_created DateTime @default(now())
  date_updated DateTime @updatedAt
  store_id     String   @unique
}
登录后复制
登录后复制
登录后复制

非常基本的功能,因此现在当您登录 /portal 时,您将被重定向到我们为 Stripe 帐户生成的一次性链接。

在入职路线中,我们将使用链接包裹卖家仪表板 div。

model Stripe {
  account_id String @id
  is_onboarded Boolean @default(false)
  user Users @relation(fields: [user_id], references: [discord_id])
  user_id String @unique
  created_at DateTime @default(now())
  updated_at DateTime @updatedAt
}

model Seller {
  id           Int      @id @default(autoincrement())
  email        String
  password     String
  store        Store    @relation(fields: [store_id], references: [id])
  date_created DateTime @default(now())
  date_updated DateTime @updatedAt
  store_id     String   @unique
  stripe       Stripe?
}
登录后复制
登录后复制
登录后复制

当我们访问 /portal 或按下按钮时,我们将被重定向到 Stripe 的 Connect 帐户门户,用户可以在那里看到他的分析、付款等...

Creating a marketplace with Stripe Connect: The onboard process

这标志着我们使用 Stripe Connect 创建市场的第一部分的结束,第二部分将处理产品、付款和支出,第三部分将是最终部分,我们将处理项目面向客户的方面.

您可以在https://github.com/ddm50/stripe-connect-howto-seller查看该项目的源代码

以上是使用 Stripe Connect 创建市场:上线流程的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板