Stripe propagate paymentIntent description to Connected Account's transfer payment - stripe-payments

I successfully created payment intents from a customer's card paying to a Standard Connected account using destination charge. In that payment intent created, I am specifying a description:
const paymentIntent = await stripe.paymentIntents.create({
amount: calculate_payment_amount(duration_minutes),
customer: "somet id",
currency: "usd",
description: `chat duration: 100`,
payment_method: paymentMethods.data[0].id,
payment_method_types: ['card'],
off_session: true,
confirm: true,
application_fee_amount: calculate_fee_amount(duration_minutes),
transfer_data: {
destination: "standard connected account ID",
},
metadata: {
metadata: "something
},
});
This payment displays the description if I view it from my platform's dashboard.
But if the standard connected account were to view this payment, the user won't be able to see the description (the same payment viewed from the standard account):
Is it possible to pass or propagate the same description of the payment intent to the same payment of the standard connected account?

Unfortunately, it doesn't look like it's currently possible to propagate the PaymentIntent description when creating destination charges. You would need to manually update the payment on the connected account with the description:
To do so, first, create the payment intent like you are doing now, except save the description as a standalone variable:
const paymentDescription = `chat duration: 100`;
const connectedAccountId = "acct_xyz";
const paymentIntent = await stripe.paymentIntents.create({
amount: calculate_payment_amount(duration_minutes),
customer: "somet id",
currency: "usd",
description: paymentDescription,
payment_method: paymentMethods.data[0].id,
payment_method_types: ['card'],
off_session: true,
confirm: true,
application_fee_amount: calculate_fee_amount(duration_minutes),
transfer_data: {
destination: connectedAccountId,
},
metadata: {
metadata: "something
},
});
Then, after the payment is confirmed, get the payment resulting from the transfer to the connected account:
const transfers = await stripe.transfers.list({
destination: connectedAccountId,
transfer_group: paymentIntent.transfer_group,
});
const paymentId = transfers.data[0].destination_payment;
The key above is that when you do a destination charge, Stripe will create a transfer and a payment (in the form of a Charge) to the connected account in the background. Here we're getting the transfer associated with the payment intent, and from that getting the associated payment. This is the py_xyz ID that you see when viewing the payment in the connected account's dashboard.
Then, update the payment to include the description:
const updatedPayment = await stripe.charges.update(
paymentId,
{
description: paymentDescription,
},
{
stripeAccount: connectedAccountId,
}
);
At that point the description will show in the standard account's payment view:

Related

Stripe Payment Method shows payment incomplete "required_payment_method"

Here is my implementation
const paymentIntent = await stripe.paymentIntents.create({
amount: body.amount,
currency: 'gbp',
customer: customerId,
automatic_payment_methods: {
enabled: true,
},
application_fee_amount: applicationFeeData,
receipt_email: `${user.getDataValue('emailId')}`,
transfer_data: {
destination: merchant,
},
metadata: {
title: 'title',
startDate: 'startAt',
endDate: 'endAt',
actualAmount: body.actualAmount,
coupon: couponCode,
},
setup_future_usage: 'off_session',
});
I have passed "automatic_payment_methods" as I am not getting Payment Method Id as it is an instant payment, but it always fails and the dashboard shows me required_payment_method. But according to docs payment_method is an optional parameter. How to proceed without payment_method parameter in paymetnIntent?
A Payment Method object must be provided either during creation or confirmation (with Stripe.js) of the Payment Intent in order to facilitate the payment.
How you do this will depend on your integration, but the general recommendation is to collect payment information from your customer using the Payment Element which can then be used with the confirmPayment function.
I'd recommend following the Accept a payment guide which shows you how to build a full payment integration.

Stripe difference between stripe.charges.create and stripe.transfers.create

I have a platform account and some connected user account. So, I have account_id for each user. I want to send funds to some of the users. I searched in the stripe docs and saw two methods for doing this (maybe).
What is the different between stripe.charges.create and stripe.transfers.create ?
The first one:
const charge = await stripe.charges.create({
amount: 1000,
currency: "eur",
source: "tok_visa",
transfer_data: {
destination: "{{CONNECTED_STRIPE_ACCOUNT_ID}}",
},
});
The second one:
const transfer = await stripe.transfers.create({
amount: 7000,
currency: 'eur',
destination: '{{CONNECTED_STRIPE_ACCOUNT_ID}}',
transfer_group: '{ORDER10}',
});
A Charge [0] moves funds from a payment method (e.g. a Card) to a Stripe account. A Transfer [1] moves funds from one Stripe account to a connected Stripe account.
[0] https://stripe.com/docs/payments/charges-api
[1] https://stripe.com/docs/connect/destination-charges

How to attach a payment method with Stripe?

I'm struggling to get Stripe to work on my server.
So, on the client side, I have this code (after much struggle to get element working) :
this.props.stripe.createPaymentMethod({ type: 'card', card: cardElement, billing_details: { name: name } }).then((paymentMethod) => {
// server request with customer_id and paymentMethod.id);
});
This works fine. Now, on the server (NodeJS), I want to add a new on-going subscription with a fixed fee for my customer.
Here's what I have :
const paymentIntent = await stripe.paymentIntents.create({
amount: 1000,
currency: 'usd',
payment_method_types: ['card'],
customer: req.body.customer_id,
metadata: { integration_check: 'accept_a_payment' },
});
const paymentIntentConfirmation = await stripe.paymentIntents.confirm(paymentIntent.id, { payment_method: req.body.paymentMethod_id });
const newSubscription = await stripe.subscriptions.create({
customer: req.body.customer_id,
items: [{ plan: premium_plan.id, quantity: 1 }],
default_payment_method: req.body.paymentMethod_id,
});
const attachPaymentToCustomer = await stripe.paymentMethods.attach(req.body.paymentMethod_id, { customer: req.body.customer_id });
const updateCustomerDefaultPaymentMethod = await stripe.customers.update(req.body.customer_id, {
invoice_settings: {
default_payment_method: req.body.paymentMethod_id,
},
});
So, if I don't attach the payment to customer, I get the following error message :
'The customer does not have a payment method with the ID pm_1Gx9m1HVGJbiGjghYhrkt6j. The payment method must be attached to the customer.'
If I do, I get the following error message :
'This PaymentMethod was previously used without being attached to a Customer or was detached from a Customer, and may not be used again.'
So, how do I add the damn payment method, so when I retrieve my customer, it shows this customer has been updated with a new subscription to the service he just subscribed to, together with his payment method (a CC in this case).
Any help here for a frustrated user is very appreciated !
On a more general note, implementing Stripe has been a very painful experience so far. Nothing seems to work. I use Typescript and there are so many bugs. The documentation is not very helpful and not well explained. "Create a source", "create a token", "create a payment intent", "create a setup intent", how am i supposed to understand the difference between all these things ? I want to add a god damn online subscription, which should be quite a standard procedure for an Internet service. Why are there so many different guidelines, with tokens, with sources, etc....
There's a few changes you can make here to get it working, in order to start a Subscription [1] you don't need to create and confirm a PaymentIntent. That is created automatically inside the Invoice(s) as they're created for payment. So the steps roughly are (you've done a lot of this already but just to have an end to end example):
Create a customer
Collect the payment information securely using Stripe.js
Attach the PaymentMethod to the Customer
(Optionally) save that as the invoice settings default payment method (because you can pass the PaymentMethod to the Subscription creation as a default payment method, but it's good practice so that you can start Subscriptions for that Customer with the same payment method)
Create a Subscription
Provision your service
Take into account SCA/3DS and handle authentication [2]
That's outlined in detail on [1]. Here's some sample code to get the Subscription started, you can replace the calls that create products and prices with your own IDs of course:
const customer = await stripe.customers.create({
name: "Foo Bartley"
});
const paymentMethod = await stripe.paymentMethods.create(
{
type: 'card',
card: {
number: '4242424242424242',
exp_month: 6,
exp_year: 2021,
cvc: '314',
},
}
);
const product = await stripe.products.create(
{name: 'Gold Special'}
);
const price = await stripe.prices.create(
{
unit_amount: 1111,
currency: 'eur',
recurring: {interval: 'month'},
product: product.id,
}
);
// Everything above here is just setting up this demo
const attachPaymentToCustomer = await stripe.paymentMethods.attach(
paymentMethod.id, // <-- your payment method ID collected via Stripe.js
{ customer: customer.id } // <-- your customer id from the request body
);
const updateCustomerDefaultPaymentMethod = await stripe.customers.update(
customer.id, { // <-- your customer id from the request body
invoice_settings: {
default_payment_method: paymentMethod.id, // <-- your payment method ID collected via Stripe.js
},
});
const newSubscription = await stripe.subscriptions.create({
customer: customer.id, // <-- your customer id from the request body
items: [{ plan: price.id, quantity: 1 }], // <-- plans and prices are compatible Prices is a newer API
default_payment_method: paymentMethod.id, // <-- your payment method ID collected via Stripe.js
});
Hope this helps!
[1] https://stripe.com/docs/billing/subscriptions/fixed-price#create-subscription
[2] https://stripe.com/docs/billing/subscriptions/fixed-price#manage-payment-authentication

Can't Transfer Amount to Connect Stripe Account

BackGround:
What i'm trying to do is set-up a marketplace where the customer can acquire services of a seller,The Project is a MERN Stack Travel Application to be exact. What i would like is for the customer to Pay the Platform(My Website connected with Stripe) when he wishes to acquire a service e.g a hotel Room. The Customer stays at the hotel for the allotted time and when he checksout the platform keeps some of the customers amount as application fee and transfers the rest to the service provider,in this case the hotel.
Current Effort:
I Used STRIPE CONNECT to acheive the required functionality.
(Note: you guys don't need to see all of the code below just the heading and description would give you an idea of what i have done and what i'm trying to ask,but please do read the issue section)
i create a Connect account for the seller when he signs up on my Website
Create 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 });
}
);
});
When the Seller Provides the rest of the required details(including bank Account) after logging-in to the Seller Portal i create a bank_account,update the already created Connect Account and link the bank_account with the Connect Account (Hopefully, that somehow makes sense)
Create Bank 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);
}
);
});
Update Account:
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);
}
);
});
Then when the customers provides his account details i charge the customer,keep some money as application fee and move the rest to the Service Providers Connect account.
Charge Customer
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,
});
}
});
By doing the above procedure a connect account is created and the amount is moved into the connected account.
Issue
The Above procedure although works correctly, it moves the amount into the Connected Service Provider Account directly after the customer is charged, what i would like is for the customer to pay the platform and after the Service Provider has provided his services , the Platform pays the Service Provider, i thought about removing
application_fee_amount,
transfer_data: {
destination: AccountID,
}
the above parameters in the Charge or Stripe.paymentIntents.create endpoint, and after Service Provider has completed his services i transfer the amount using the 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);
}
});
the issue here is that transfer endpoint returns "Your destination account needs to have at least one of the following capabilities enabled: transfers, legacy_payments" , i have checked the Connected Account in Stripe Dashboard and in the Capabilities section Card_Payment and Transfers are both set to Active, plus Payments and Payouts are both Enabled and the status of the connect account is "Complete"
So if anyone could point in the right direction i would really Appreciate it,Cheers :)
Ok - we'll agree that Stripe works as intended. You get the error message that you get because you remove the destination account ID from the payment intent creating function. That's where the problem lies, under your heading Charge Customer.
Let's look at it: (a shortened version)
const payment = await stripe.paymentIntents.create({
amount: PaymentAmount,
currency: "USD",
...
customer: CustomerID,
application_fee_amount,
transfer_data: {
destination: AccountID,
},
confirm: true,
});
The last property confirm: true is equivalent to creating and confirming the payment intent in the same call. The default value is false -- using that the status of the newly created payment intent will be requires_confirmation. And when you're ready, you confirm the payment intent along these lines:
const confirmedPayment = await stripe.paymentIntents.confirm(
'payment_intent_id',
{payment_method: 'card'},
function(err, paymentIntent) {
}
});
A few general comments on things going wrong
When a payer pays money for some goods online, it is the responsibility of the app developer to implement the logic, according to which the money and goods are sent and received: it can be prepaid, postpaid, or partially both. No logic is foolproof. In general, if we worry about customers taking advantage of our payment policy, we can require everything to be prepaid by all paying parties and include a fair refund policy. In this case, Stripe supports refunds of payment intents but what's more important: it keeps track of the status of the payment.
When the payment intent is created but not confirmed, the status is requires_confirmation. Not much can go wrong there. But after the payment intent has been confirmed, the status will be processing - this may take days. You may decide to cancel the payment at any time. But if things go fine, the status will change to succeeded which means that the funds are in the destination account. But if the payment fails for whatever reason, the status will return to requires_payment_method. Even in this case, there's no need to create a new payment or transfer object. You can retrieve the payment intent any time by calling stripe.retrievePaymentIntent(clientSecret) and check the status. But in my opinion, it's much easier to monitor the status changes with a webhook that is configured to receive the status changing events. Even if no action takes place immediately when the status changes, we can store the status in the DB where it's available.
From experience, I've seen how common it is for payments to fail. It doesn't mean that there's any fraud going on on either side but it does mean that the app should be prepared to handle both cases. The events to add to the webhook config are payment_intent.succeeded and payment_intent.payment_failed. How these events are handled is specific to each and every application.
Create a webhook (Stripe config) which includes:
Events sent to the webhook: in this case customer.created, customer.source.created, customer.source.updated
URL = the route that handles the events when they arrive
So you need to store the pending payment in your DB first. Then in the webhook, find it in the DB and complete the transfer.

Fund cannot be sent to accounts located in Us because its outside of your platform region

await stripe.transfers.create({
amount: amount,
currency: account.default_currency,
source_transaction: chargeId,
destination: accountId
});
from stripe account (uk) to Us stripe connected account
Stripe doesn't allow transfer to the outside of the platform region
You can use the following method to manage a business in multiple countries
First use stripe create charge API with destination charges
const charge = await stripe.charges.create({
customer: data.customer,
source: data.cardId,
amount: amount,
currency: user.currency,
description: process.env.APP_NAME,
statement_descriptor: process.env.APP_NAME,
application_fee: platformFee,
destination: accountId
});
After creating the charge, You can transfer that payout to that connected account using create payout API
await stripe.payouts.create({
amount: amount,
currency: currency,
}, {
stripeAccount: accountId,
});
This flow is working for me

Resources