We are building a Platform.
In our Platform we create Custom Connect Accounts in Stripe.
For these Custom Connect Account we create customer accounts. Essentially the customer accounts are end-customers of our Custom Connect (Company)accounts in the Platform.
Now we would like to store credit card information of the customer accounts (for a particular custom connect account).
We followed the instructions here to create a setupIntent. The code is as below, here the stripe_account is the account_id of the custom connect (Company) account and customer['id'] is the id of the customer account -
intent = stripe.SetupIntent.create(
customer=customer['id'],
stripe_account = stripe_account
)
We pass this intent.client_secret to our front end. In the Javascript we are calling this -
setupForm.addEventListener('submit', function(ev) {
ev.preventDefault();
stripe.confirmCardSetup(
clientSecret,
{stripe_account : stripe_account},
{
payment_method: {
card: cardElement,
billing_details: {
name: cardholderName.value,
},
},
}
).then(function(result) {
if (result.error) {
// Display error.message in your UI.
} else {
// The setup has succeeded. Display a success message.
}
});
});
But we are getting the error, No such setupintent: 'seti_1IBkyZ4ZQzThevDR3MR433aI'. Clearly the setupintent that was generated from Stripe is not being accepted here. What are we doing wrong?
The likely cause of this error is that you're not initializing Stripe.js with the stripeAccount the Setup Intent exists on. Your Stripe.js initialization code should look something like this:
var stripe = Stripe('pk_test_YOUR_PUBLISHABLE_KEY', {
stripeAccount: 'acct_CONNECTED_ACCOUNT_ID'
});
That will allow Stripe.js to make requests on behalf of the connected account (which is where the Setup Intent exists).
Related
I'm trying to follow this document to setup Stripe Connect: https://stripe.com/docs/connect/enable-payment-acceptance-guide?platform=web&ui=checkout#create-account-link
At the account link phase it redirects me to Stripe for creating an account etc - for development purposes I would like to skip this instead of entering real data every time.
I came accross this document for testing Stripe: https://stripe.com/docs/connect/testing
It says there is a way to force skip but I don't see anything pop up. Is there a special value I need to pass in to enable the force skip option?
Here are the code snippets I've been using to test account linking
const stripe = new Stripe(secrets.STRIPE_SECRET_KEY, {
apiVersion: "2022-11-15"
});
export class StripeClient {
/**
* Create a Stripe account for a user. The account will be associated with the ZCal account
*/
static accountCreationRequest = async (): Promise<Stripe.Response<Stripe.Account>> => {
const account: Stripe.Response<Stripe.Account> = await stripe.accounts.create({
type: "standard"
});
return account;
};
static accountLinkRequest = async (stripeAccountId: string): Promise<Stripe.Response<Stripe.AccountLink>> => {
const accountLink: Stripe.Response<Stripe.AccountLink> = await stripe.accountLinks.create({
account: stripeAccountId,
refresh_url: `${config.CLIENT_BASE_URL}/account/integrations`,
return_url: `${config.CLIENT_BASE_URL}/account/integrations`,
type: "account_onboarding"
});
return accountLink;
};
}
The "force skip" line on testing Doc is for OAuth flow, which is unrelated to your Onboarding flow, so first let's ignore it.
When you make your first request to create the account, you can prefill as much information as you want, and it will be automatically populated in the flow later. Here is the full list of fillable fields.
You can pre-fill any information on the account, including personal
and business information, external account information, and more.
I have a flow using nodejs and reactjs to let users subscribe to my site.
So when user logs in he can see a few packages.
When the user selects a package and enters card details, I do the following:
1 - Create a new customer, based on user details (name, email etc, fetched since user logged in)
2 - Create a subscription for the newly create customer according to price_id selected
3 - Collect in the frontend the card number/cvc/expire date with:
const cardElement = elements.getElement(CardElement);
4 - Make a call to stripe from frontend with:
const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: cardElement,
billing_details: {
name: name,
}
}
});
Im not sure if this was the best flow. However, it is working.
I also managed updating subscription and canceling it.
However, Im having an hard time in changing credit card details.
What I understood from docs I should use the same call as I did when I create the card payment.
So I collect again the credit card number and call again the same function:
const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: cardElement,
billing_details: {
name: name,
}
}
});
However, the call is done to:
https://api.stripe.com/v1/payment_intents/pi_XXXX/confirm
and returns a 400 with this info:
type: "invalid_request_error", code: "payment_intent_unexpected_state", doc_url: "https://stripe.com/docs/error-codes/payment-intent-unexpected-state"
Should I use something else to update credit card info? Or am I calling it in the wrong way?
Your initial flow of calling confirmCardPayment() is correct, that is what is recommended in Stripe's docs too: https://stripe.com/docs/payments/accept-a-payment?platform=web&ui=elements.
hard time in changing credit card details. What I understood from docs I should use the same call as I did when I create the card payment.
To just collect card details and create a PaymentMethod, you should call createPaymentMethod() [0] from Stripe.js. That will convert a customer's card into a PaymentMethod like pm_123.
You will then send that PaymentMethod to your backend server, where (using your server-side Stripe API library like stripe-node) you'll attach it to a Stripe Customer [1] and also update as the Customer's default PaymentMethod for recurring payments [2].
[0] https://stripe.com/docs/js/payment_methods/create_payment_method
[1] https://stripe.com/docs/api/payment_methods/attach
[2] https://stripe.com/docs/api/customers/update#update_customer-invoice_settings-default_payment_method
I'm using expressjs with sapper/svelte on the front.
I'm attempting to use stripe SetupIntent api and stripe.confirmCardSetup to save card details and attach it to a customer for future charges. I believe after reading the documentation I understand the flow of the process and my code is based on their example from the custom payment flow section
After submitting the credit card form, I get nothing. No card attached to the customer, no console log output and no errors in my application. The setupIntent is successfulLY created and I'm able to view it in my stripe dashboard but no card (paymentMethod) is created and attached to the customer. Here is my steps:
1- When a user create an account, I create a customer in stripe and
save the stripe customer's id in my db
2- I use that stripe customerid to create a setupIntent on the server
const intent = stripe.setupIntents.create({
customer: customer
}).then(intent => {
return res.end(JSON.stringify({ info: intent.client_secret }));
});
At this stage, I need the client_secret to use it on the front with my card-element to submit the card details securely to stripe.
I confirm the setupIntent is indeed in my stripe dashboard. So this part is fine.
Now to the front part of my code :
I get the client_secret on the front of my application and use stripe.js to collect and submit the card details to be attached as a paymentMethod to the customer (which the client_secret has the customer.id), so the code is :
let clientsecret
let stripe = null
let cardelement = null
stripe = await loadStripe('pk_test_mykeys');
var elements = await stripe.elements();
cardelement = await elements.create('card');
cardelement.on('ready', ()=>{});
cardelement.on('change', ({error}) => {
const displayError = document.getElementById('card-errors');
if (error) {
displayError.textContent = error.message;
} else {
displayError.textContent = '';
}
}); //end of card.on fn
cardelement.mount("#card-element");
return{
stripe : stripe,
cardelement : cardelement
}
function clickhandle(){
stripe = stripe
stripe.confirmCardSetup( csec , {
payment_method: {
card: cardelement,
billing_details: { user : "testing setupIntent"
}
},
setup_future_usage : 'on_session'
})
.then(function(result) {
if (result.error) {
console.log("there is an error")
}
if(result.setupIntent){
console.log("here is the setupintent success result")
}
});
}
Stripe.js works because I tested a paymentIntent and charging credit card on a different part of the application and the executed charges are showing in the stripe dashboard, so it is not the stripe.js script.
Is my process flow correct? Use stripe customer id to create setupIntent on my server and on the front the above code which collects card details and submit it using stripe.confirmCardSetup which will attach the paymentMethod/card details to the customer from the setupIntent client_secret?
Any help would be appreciated as I read the documentation and it is clear and straightforward. I followed it step by step. What am I missing?
The correct process is described here: https://stripe.com/docs/payments/save-and-reuse#web-create-setup-intent and you're doing things a bit different that may be causing some issues.
I suspect that your inclusion of future_usage - which isn't actually a parameter to that function call - is likely the problem.
After reading #floatingLomas comments, I went back and read carefully the documentation again. Here are the issues that need to be clear:
1- the form where you will mount the #card-elements needs to include the client_secret, whether in a hidden field or using data- attribute.
read here if you don't know about it
So --note the data-secret attribute --my form code now is :
<form data-secret={customersecret} on:submit={stripehandle} id="payment-form">
<label for="card-element">Enter Your Credit Card Details:</label>
<div id="card-element"></div>
<div id="card-errors" role="alert"></div>
<button id="submit">Pay -svelte2 component</button>
</form>
2- remove future_usage as #floatingLomas suggested. The correct parameter is "Usage" : "on_session" because the future_usage is for the paymentMethod api not the setupIntent api
3- The parameter billing_details: { "user" : "testing setupIntent" } was wrong.
The api has parameter for name, phone, address but not user ;)
So the confirmCardSetup() code is the following:
stripe.confirmCardSetup( csec , {
payment_method: {
card: cardelement,
billing_details: { "name" : "testing setupIntent" }
}
})
.then(function(result) {
console.log("result :", result)
})
I've been reading the documentation for how to retrieve the Stripe fee from a given payment here:
// Set your secret key. Remember to switch to your live secret key in production!
// See your keys here: https://dashboard.stripe.com/account/apikeys
const stripe = require('stripe')('sk_test_xyz');
const paymentIntent = await stripe.paymentIntents.retrieve(
'pi_1Gpl8kLHughnNhxyIb1RvRTu',
{
expand: ['charges.data.balance_transaction'],
}
);
const feeDetails = paymentIntent.charges.data[0].balance_transaction.fee_details;
However I want to retrieve the Stripe fee for a payment made to a connected account. If I try the code above with a payment intent from a linked account I get the error:
Error: No such payment_intent: 'pi_1Gpl8kLHughnNhxyIb1RvRTu'
However, I can actually see the payment intent listed when I receive the posted data from the webhook:
{ id: 'evt_1HFJfyLNyLwMDlAN7ItaNezN',
object: 'event',
account: 'acct_1FxPu7LTTTTMDlAN',
api_version: '2019-02-11',
created: 1597237650,
data:
{ object:
{ id: 'pi_1Gpl8kLHughnNhxyIb1RvRTu',
object: 'payment_intent',
Any tips?
I want to retrieve the Stripe fee for a payment made to a connected
account. If I try the code above with a payment intent from a linked
account I get the error:
In order to retrieve the Stripe fee for a payment made on behalf of a connected account (using a direct Charge) you need to make the retrieve request as the connected account by specifying the special Stripe-Account header in the request. When using stripe-node we'll add that header for you automatically if you pass in the account ID as part of the request options. For example:
const paymentIntent = await stripe.paymentIntents.retrieve(
"pi_1HCSheKNuiVAYpc7siO5HkJC",
{
expand: ["charges.data.balance_transaction"],
},
{
stripeAccount: "acct_1GDvMqKNuiVAYpc7",
}
);
You can read more about making requests on behalf of connected accounts in stripe-node and our other libraries here: https://stripe.com/docs/api/connected_accounts
I'm building a subscription plan in node.js, I read the documentation about how to subscribe a user to a plan, and it was successful.
Stripe's doc states that I have to store an active_until field in the database. It says when something changes use webhook, i know that webhook is like an event.
The real questions are
1) how do I do a repeat the bill every month using active_until?
2) How do I use webhook, I really don't understand.
Here's the code so far.
var User = new mongoose.Schema({
email: String,
stripe: {
customerId: String,
plan: String
}
});
//payment route
router.post('/billing/:plan_name', function(req, res, next) {
var plan = req.params.plan_name;
var stripeToken = req.body.stripeToken;
console.log(stripeToken);
if (!stripeToken) {
req.flash('errors', { msg: 'Please provide a valid card.' });
return res.redirect('/awesome');
}
User.findById({ _id: req.user._id}, function(err, user) {
if (err) return next(err);
stripe.customers.create({
source: stripeToken, // obtained with Stripe.js
plan: plan,
email: user.email
}).then(function(customer) {
user.stripe.plan = customer.plan;
user.stripe.customerId = customer.id;
console.log(customer);
user.save(function(err) {
console.log("Success");
if (err) return next(err);
return next(null);
});
}).catch(function(err) {
// Deal with an error
});
return res.redirect('/');
});
});
How do i Implement the active_until timestamps and the webhook event?
active_until is just the name of a database column that you can create on your users table to store the timestamp representing when the user's account expires. The name of the column doesn't matter. You can use any name you want.
In order to verify if a user's subscription is current, Stripe is suggesting that you use logic like this:
If today's date <= user.active_until
allow them access
Else
show them an account expired message
The webhook is a request that Stripe's servers make to your server to tell you that something has happened. In this case, the event you are most interested in is invoice.payment_succeeded.
Your webhook will include logic like this:
if event type is "invoice.payment_succeeded"
then update user.active_until to be equal to today's date + 1 month
You will also want to respond to other events in case the payment fails, etc.
You don't need to repeat the bill every month. Stripe will do it for you.
Once you subscribed your user to a plan, stripe will charge him till the paid period is over.
Every time stripe charges a customer it will generate a webhook, that is a request on your server to some specified URL. Stripe can generate different webhooks for different reasons.
For example when customer gets charged by subscription, Stripe will send you info about payment.
router.post('/billing/catch_paid_invoice', function(req, res) {
// Here you parse JSON data from Stripe
}):
I don't have access to Stripe settings right now, but a remember setting urls for webhooks manually.
Select your account name > Account Settings > Webhooks
active_until is just a reminder that customer is still active and have paid services in your system. It needs to be updated when getting webhooks.
Stripe docs are really good, so go through them one more time.
https://stripe.com/docs/guides/subscriptions