考慮到沒有太多支付處理商提供它,創建一個市場可能太難了,或者是不可能的,如果他們不提供它,那麼你很可能會在他們聽到風聲的那一刻就被踢出平台,即使沒有如果您沒有堅實的基礎來處理使用該平台的賣家的付款、退款和付款,那麼創建市場是有風險的。
Stripe Connect 解決了這些問題,它將使我們能夠創建一個基本的市場,您可以在其中註冊成為賣家,並且客戶可以輕鬆地從這些賣家那裡購買商品。作為平台所有者,您還可以設定服務費,因此當用戶 X 從商店 Y 購買商品時,我們將獲得該交易的 X% 分成,但稍後會詳細介紹。
為了處理資料庫連接,我們使用 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 帳戶,但尚未加入,那麼我們也會將他們重定向到加入連結。
從那裡賣家將輸入他的電子郵件/電話號碼,然後入職流程將開始,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 的事件,但讓我們看看加入是否有效。
一旦你到達最後一步,它可能會說你的帳戶詳細資料不完整,最後一步是身份驗證,因為這是測試模式,我們可以模擬
好的,一旦我們完成了,我們將返回到上一頁,我們可以按提交,按提交,我們將進入控制台
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,這意味著我們已成功入門,我們可以開始建立產品。
好吧,讓我們先讓賣家儀表板按鈕發揮作用,然後再繼續討論產品,創建一條位於 /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 帳戶門戶,用戶可以在那裡看到他的分析、付款等...
這標誌著我們使用 Stripe Connect 創建市場的第一部分的結束,第二部分將處理產品、付款和支出,第三部分將是最終部分,我們將處理專案面向客戶的方面.
您可以在https://github.com/ddm50/stripe-connect-howto-seller查看該專案的原始碼
以上是使用 Stripe Connect 創建市場:上線流程的詳細內容。更多資訊請關注PHP中文網其他相關文章!