背景:
我要做的是建立一个客户可以从其那里获得卖方服务的市场。该项目是一个确切的MERN堆栈旅行应用程序。我想让客户在希望获得服务(例如酒店客房)时支付平台(与Stripe连接的我的网站)。客户在指定的时间停留在酒店,当他结帐时,平台会保留一些客户的申请费,然后将其余部分转移给服务提供商(在这种情况下为酒店)。
目前的努力:
我使用STRIPE CONNECT
实现了所需的功能。
(Note
:你们不需要看到下面的所有代码,只需标题和描述就可以使您了解我已经做了什么以及我想问什么,但是请阅读问题部分)
我在卖家登录我的网站时为卖家创建了Connect account
创建连接帐户
const express = require("express");
const router = express.Router();
router.post("/createAccount", async (req, res) => {
const { name, email } = req.body; //Data Passed from the FrontEnd
stripe.accounts.create(
{
type: "custom",
country: "US",
email: email,
requested_capabilities: ["card_payments", "transfers"],
},
function (err, account) {
res.json({ account: account });
}
);
});
登录Seller Portal
后,当卖方提供其余的所需详细信息(包括银行帐户)时,我将创建一个bank_account
,更新已创建的Connect Account
并链接bank_account
和Connect Account
(希望这可以说是合乎情理的)
创建银行帐户
router.post("/createBankAccount", async (req, res) => {
const { account_holder_name, routing_number, account_number } = req.body;
stripe.tokens.create(
{
bank_account: {
country: "US",
currency: "USD",
account_holder_name,
account_holder_type: "individual",
routing_number,
account_number,
},
},
function (err, token) {
res.send(token);
}
);
});
更新帐户:
router.post("/updateAccount", async (req, res) => {
const {
AccountID,
Day,
Month,
Year,
first_name,
last_name,
email,
BankAccountID,
} = req.body;
const FrontFilePath = fs.readFileSync("PathToFileHere");
const FrontPhotoIDUpload = await stripe.files.create({
file: {
data: FrontFilePath,
name: "FrontPhotoID.jpg",
type: "application.octet-stream",
},
purpose: "identity_document",
});
const BackFilePath = fs.readFileSync("PathToFileHere");
const BackPhotoIDUpload = await stripe.files.create({
file: {
data: BackFilePath,
name: "BackPhotoID.jpg",
type: "application.octet-stream",
},
purpose: "identity_document",
});
stripe.accounts.update(
AccountID,
{
business_type: "individual",
individual: {
dob: { day: Day, month: Month, year: Year },
first_name: first_name,
last_name: last_name,
id_number: "006-20-8311",
phone: "605-628-6049",
address: {
city: "Half Way",
line1: "2467 Twin House Lane",
postal_code: "65663",
state: "MO",
},
email,
ssn_last_4: "8311",
verification: {
document: {
front: FrontPhotoIDUpload.id,
back: BackPhotoIDUpload.id,
},
},
},
business_profile: {
mcc: "4722",
url: "http://www.baoisne.com",
},
tos_acceptance: {
date: Math.floor(Date.now() / 1000),
ip: req.connection.remoteAddress,
},
},
function (err, account) {
console.log(err);
console.log(account);
}
);
//Connect External Account
stripe.accounts.createExternalAccount(
AccountID,
{
external_account: BankAccountID,
},
function (err, bankAccount) {
console.log(err);
res.send(bankAccount);
}
);
});
然后,当客户提供其帐户详细信息时,我会向客户收费,保留一些钱作为申请费,然后将其余款项移至Service Providers Connect帐户。
收费客户
router.post("/charge", async (req, res) => {
const { TokenID, CustomerID, Amount, AccountID } = req.body;
let PaymentAmount = Amount * 100;
let application_fee_amount = 400;
try {
const payment = await stripe.paymentIntents.create({
amount: PaymentAmount,
currency: "USD",
description: "We did it boss",
payment_method_data: {
type: "card",
card: {
token: TokenID,
},
},
receipt_email: "abdullahabid427@gmail.com",
customer: CustomerID,
application_fee_amount,
transfer_data: {
destination: AccountID,
},
confirm: true,
});
return res.status(200).json({
confirm: "Payment Succeeded",
});
} catch (error) {
console.log(error);
return res.status(400).json({
message: error.message,
});
}
});
通过执行上述过程,将创建一个关联帐户,并将金额移入关联帐户。
问题
上述过程虽然可以正常运行,但是在向客户收费之后,我希望将金额直接移到关联服务提供商帐户中,我希望客户付款给平台,并且在服务提供商提供其服务后,平台向服务提供商付款,我考虑过删除
application_fee_amount,
transfer_data: {
destination: AccountID,
}
Charge
或Stripe.paymentIntents.create
端点中的上述参数,在服务提供商完成其服务后,我使用Stripe Transfer API转移了金额
router.post("/transfer", async (req, res) => {
try {
console.log("TRANSFER=");
const { AccountID, amount } = req.body;
const transfer = await stripe.transfers.create({
amount,
currency: "USD",
destination: AccountID,
});
res.send(transfer);
} catch (error) {
res.send(error);
}
});
这里的问题是转账端点返回“ 您的目标帐户需要至少启用以下功能之一:转账,legacy_payments ”,我已经在Stripe仪表板和“功能”部分的Card_Payment
和Transfers
都设置为“有效”,并且“付款”和“付款”都已启用,并且连接帐户的状态为“完成”
因此,如果有人能指出正确的方向,我将不胜感激,干杯:)
答案 0 :(得分:3)
好的-我们同意Stripe可以正常工作。您收到的错误消息是因为您从付款意图创建功能中删除了目标帐户ID。这就是问题所在,在您的收费客户标题下。
让我们看一下:(简化版)
const payment = await stripe.paymentIntents.create({
amount: PaymentAmount,
currency: "USD",
...
customer: CustomerID,
application_fee_amount,
transfer_data: {
destination: AccountID,
},
confirm: true,
});
最后一个属性confirm: true
等效于在同一呼叫中创建并确认付款意向。默认值为false
-使用该值,新创建的付款意图的状态将为requires_confirmation
。准备好后,您可以按照以下方式确认付款意向:
const confirmedPayment = await stripe.paymentIntents.confirm(
'payment_intent_id',
{payment_method: 'card'},
function(err, paymentIntent) {
}
});
关于出现问题的一些常规评论
当付款人在线上为某些商品付款时,应用程序开发人员有责任实施逻辑,根据该逻辑来发送和接收金钱和商品:可以预付,后付或部分支付。没有逻辑是万无一失的。一般而言,如果我们担心客户会利用我们的付款政策,则可以要求所有付款方预先支付所有费用,并包括公平退款政策。在这种情况下,Stripe支持退款付款意向,但更重要的是:它可以跟踪付款状态。
创建付款意图但未确认时,其状态为requires_confirmation
。那里没什么大不了的。但是在确认付款意向后,状态将为processing
-可能需要几天的时间。您可以随时决定取消付款。但是,如果一切顺利,状态将更改为succeeded
,这意味着资金已在目标帐户中。但是,如果由于某种原因付款失败,状态将返回到requires_payment_method
。即使在这种情况下,也无需创建新的付款或转账对象。您可以随时调用stripe.retrievePaymentIntent(clientSecret)
并查看状态,以检索付款意向。但是我认为,使用配置为接收状态更改事件的Webhook监视状态更改要容易得多。即使状态更改后没有立即采取任何措施,我们也可以将状态存储在可用的数据库中。
根据经验,我已经看到付款失败的普遍性。这并不意味着双方都有欺诈行为,但这确实意味着该应用应该准备好应对这两种情况。要添加到webhook配置的事件是payment_intent.succeeded
和payment_intent.payment_failed
。这些事件的处理方式针对每个应用程序。
答案 1 :(得分:-1)
创建一个Webhook(条带配置),其中包括:
customer.created
,customer.source.created
,customer.source.updated
因此,您需要先将待处理的付款存储在数据库中。然后在webhook中,在数据库中找到它并完成传输。