I have a subscription route like this. My flow looks like this
User select a plan.
User enter card details through Stripe Card Element
User click Subscribe button.
As per docs,
I'm creating a customer.
Then creating a subscription for this customer.
But, my dashboard says payment incomplete and response object on creating subscriptions shows status: 'requires_confirmation'. What I am doing wrong?
router.post('/subscription', auth, async (req, res) => {
const { payment_method, price_id } = req.body;
const customer = await Stripe.customers.create({
email: 'test#gmail.com',
payment_method: payment_method,
description: 'New Customer',
invoice_settings: { default_payment_method: payment_method }
});
try {
const subscription = await Stripe.subscriptions.create({
customer: customer.id,
items: [
{
price: price_id
}
],
payment_behavior: 'default_incomplete',
expand: ['latest_invoice.payment_intent']
});
res.send({
status: subscription.latest_invoice.payment_intent.status,
subscriptionId: subscription.id,
});
} catch (error) {
return res.status(400).send({ error: { message: error.message } });
}
});
[![Stripe dashboard][1]][1]
[1]: https://i.stack.imgur.com/Sx0zN.png
It sounds like you’re doing things a bit out of order from the way the Stripe docs suggest. In this guide, the subscription object is created prior to collecting the payment method. The reason your invoice isn’t automatically paid is because you are explicitly passing in a payment_behavior of default_incomplete, which tells Stripe not to pay the invoice and allows you to collect payment details client-side and confirm the payment. Since you have already collected payment details, don’t pass in a payment_bevavior of default_incomplete.
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.
I'm using Stripe for the first time an I'm little confused about the different APIs they provide. There is the Payment Method API which is the recommended one for handling payment methods for a customer but currently it supports only credit cards if I understand it correctly...
But I need different payment methods for example bank accounts. So for that Stripe provides the Card, Bank and Source object. Whats the different between them?
I tried each of them and couldn't see any difference in their behaviour. My main problem is that I want to change the default source for the payment if customer wants. So the customer object provides a default_source parameter but it doesn't change the default source when I change it. I tried to change the default from card to bank but it doesn't work. So I think I misunderstood the concept of the Payment Method, Sources, Card and Bank objects.
So can anyone explain me how I have to use these different objects?
I provide you my code below.
My code for setting default source (doesn't change anything is Stripe dashboard):
const customer = req.body.customer;
const token = req.body.token;
stripe.customers.update(
customer,
{
default_source: token //token looks like btok_231disjaohq0dj21sp
}
).then(customer => {
res.send(customer);
}).catch(err => {
res.send(err);
});
Nothing changed in dashboard:
My code to create a bank account (this works):
stripe.tokens.create({
bank_account: {
country: 'US',
currency: 'usd',
account_holder_name: decoded.account_holder_name,
account_holder_type: 'individual',
routing_number: '110000000',
account_number: '000123456789'
}
}).then(token => {
stripe.customers.createSource( //there is .create and .createSource whats the difference?
decoded.userId,
{
source: token.id
}
).then(bank_account => {
res.send(bank_account);
}).catch(err => {
res.send(err);
})
}).catch(err => {
res.send(err);
});
My code to create a credit card (works):
stripe.paymentMethods.create({
type: "card",
card: {
number: decoded.number,
exp_month: decoded.month,
exp_year: decoded.year,
cvc: decoded.cvc
}
}).then(token => {
stripe.paymentMethods.attach(
token.id,
{
customer: decoded.customer,
}
).then(card => {
res.send(card);
}).catch(err => {
res.send(err);
});
}).catch(err => {
res.send(err);
});
These are merely different objects/APIs Stripe has created over time. Payment Methods are the current API where new product and feature development is focused.
If you want to learn some of the history and thinking behind the progression, this blog post is an excellent resource.
Hi I am trying to implement stripe connect where the platform takes an application fee but the connected user gets majority of charge.
Following this as my guide
https://stripe.com/docs/connect/shared-customers
I have this as my code. The user is saved w/ default credit card source in another view.
Parse.Cloud.define("chargeCard", function(req, res){
stripe.tokens.create({
customer: req.params.customer,
}, {
stripe_account: req.params.stripeAccount,
}).then((token) => {
console.log("successfully created token");
stripe.charges.create({
amount: req.params.amount,
currency: req.params.currency,
source: token.id,
application_fee: req.params.fee,
}, {
stripe_account: req.params.stripeAccount,
}).then((charge) => {
console.log("successfully charged card");
res.success(charge);
}).catch((error) => {
console.log(error);
res.error(error.message);
});
}).catch((error) => {
console.log(error);
res.error(error.message);
});
});
But receive the error:
"You provided a customer without specifying a source. The default source of the customer is a source and cannot be shared from existing customer".
Im not able to specify its default source in the create token body. any help?
This issue was with the users source. Although the user had a credit card source it is not "shareable" (as the error kinda states) , you need to create a shared source when using stripe-connect.
https://stripe.com/docs/sources/connect#creating-direct-charges
You want to use this instead of the above tokens.create for direct charges in stripe-connect.
stripe.sources.create({
customer: "cus_AFGbOSiITuJVDs",
usage: "reusable",
original_source: "src_19YP2AAHEMiOZZp1Di4rt1K6",
}, {
stripe_account: "{CONNECTED_STRIPE_ACCOUNT_ID}",
}).then(function(token) {
// asynchronously called
});
FYI: I do not save or attach this new source, kept the primary one and the regenerate a new token for each purchase since my application is 1 to many sellers.
When attempting to begin a Subscription for a newly created Customer, I receive the following error from Stripe:
invalid_request_error
Error: This customer has no attached payment source
The customer seems to be created just fine. I am using Stripe Checkout to collect the card token. For testing, I am using Stripe's 4242 4242 4242 4242 card number with random information. The token seems to be getting created and passed to my server just fine. Below is my server side code:
stripe.plans.retrieve(
"basic-monthly",
function(err, plan) {
if (err) {
console.error(err)
res.sendStatus(500)
} else {
stripe.customers.create({
email: owner,
source: token.id,
}, function(err, customer) {
if (err) {
console.error(err)
res.sendStatus(500)
} else {
stripe.subscriptions.create({
customer: customer.id,
items: [
{
plan: "basic-monthly",
quantity: 1
},
],
}, function(err, subscription) {
if (err) {
console.error(err)
console.log('##### UNABLE TO CREATE SUBSCRIPTION ####')
res.sendStatus(500)
} else {
console.log('Subscription created.')
console.dir(subscription)
res.sendStatus(200);
}
});
}
});
}
});
##### UNABLE TO CREATE SUBSCRIPTION #### is logged, along with the errors described above. I understand what the error means, but I am not sure how it is occurring. As you can see above, I am passing in the Token Id when creating a customer, source: token.id,.
What is the issue here?
The most likely cause here is that token.id is empty, so the Customer is being created without a Source. I'd suggest logging the contents of token and see what you get.