Stripe connect share card source not working - node.js

I am saving customers and their sources on my platform, and trying to create charges for my connected accounts. I am able to successfully create destination charges, but I'm having trouble with creating direct charges.
I've tried creating a token per: https://stripe.com/docs/connect/shared-customers
If I create a token using just the customer the example, the error is:
'You provided a customer without specifying a source. The default source of the customer is a source and cannot be shared from existing customers.'
Even though the documentation says that you need "The card or bank account ID for that customer, if you want to charge a specific card or bank account rather than the default".
I cannot find a parameter that lets me specify a source as well or instead of a customer.
I've tried sharing the customer's chosen source with the connected account per: https://stripe.com/docs/sources/connect#shared-card-sources
which results in the error:
'Sending credit card numbers directly to the Stripe API is generally unsafe. We suggest you use test tokens that map to the test card you are using, see https://stripe.com/docs/testing.'
I tried attaching a test token (tok_visa) to my customer, and creating a charge with that, but that had the same result.
Every time I have some success I end up with the error about sending numbers being unsafe even though I'm only ever sending Stripe's provided token or source ID's.
I've tried this:
const newToken = await stripe.tokens.create({
customer: customerId,
}, {
stripe_account: stripeAccountId,
}).catch((e) => {
console.log(e);
});
and this:
const newToken = await stripe.sources.create({
customer: customerId,
usage: 'single_use',
original_source: sourceId,
}, {
stripe_account: stripeAccountId,
}).catch((e) => {
console.log(e);
});
The only success I've had is creating the direct charge with a test token (tok_visa), completely bypassing the sharing of tokens/sources. No other combination seems to work. But that leaves me at a loss as to how to get a real shared token when I need it.

I found out I should be using sources, not tokens. And it turns out I was accidentally using the whole newToken as the source for the charge. It's working now that I'm passing the newToken.id to the charge.

Related

Have one stripe connect checkout session with multiple products trigger correct payouts to multiple different connected accounts

I am building a marketplace for people to sell digital products with nextjs, firebase and firebase cloud functions. I am able to credit one account with a cart of one or multiple products by following the documentation here https://stripe.com/docs/connect/collect-then-transfer-guide. But I would say it would be important to be able to put items from different authors into your cart.
This is my code that works well for one or multiple products and one connected account.
export const checkoutSessionSingular = functions.https.onCall(async (data, context) => {
const session:any = await stripe.checkout.sessions.create({
line_items: data.line_items,
mode: "payment",
success_url: "***",
cancel_url: "***",
payment_intent_data: {
application_fee_amount: data.fee_amount,
transfer_data: {
destination: data.connectId,
},
},
});
return session;
});
I have tried putting payment data, transfer data or destination into an array, but that always breaks it.
I tried using transfers and transfer groups, but I seem to lose all the valuable data in the stripe dashboard about which product the money comes from and I can't manage to make it work properly with the very limited documentation.
I also tried using destination charges but couldn't make it work.
If something like transfer groups are the solution, I would welcome a link to a proper example implementation or something more helpful.
What should I do to just have a normal working cart for a multi vendor marketplace? I already looked at every page of their documentation 30 times. Thanks.
If you want to send the money to multiple connected accounts, then you have to use Separate Charges & Transfers. So in your case you need to:
Create the Checkout Session and collect the payment, without any application_fee_amount or transfer_data
Then manually create Transfers between your main Stripe account and the connected accounts, with this endpoint

Creating a subscription using bank account number using stripe js in node js

I have created subscription by accespting credit/debit card number, exp month, exp year and cvc from front end angular app and creating
Customer
Product
Product Price
Payment Method
Subscription
In payment method i provide following details:
async function createPaymentMethod(data) {
let body = {
type: 'card',
card: {
number: data.card?.number,
exp_month: data.card?.exp_month,
exp_year: data.card?.exp_year,
cvc: data.card?.cvc
}
};
return await stripe.paymentMethods.create(body);
}
Now i want to create payment method but want to use customer's bank account number but i am unable to find a way to do it. I want to add "type: bank_account"
Note: I am using Strip JS and Node JS. All of this I want to do on server side i.e node js
Collecting Credit Card server-side is a bad idea, since you will expose yourself to the burden on PCI-Compliance. You would want to review Stripe's Integration Security Guide. Generally you would want to use client-side SDK to collect credit card information instead.
To the question of a bank account, it depends on which specific PaymentMethod you're gonna use (which country's bank transfer?) Ie. If you are talking about ACH in the US, you should follow the ACH Guide. Similarly it will collect bank account information client-side in exchange of a token, then passing it up to your back end.

Angular/node.js Stripe Checkout Integration (1 account to facilitate payments from third party payer to third party receiver)

Man, I have been at this for some days and can't find a good solution to this. I want to use my account (via public key and private key) to facilitate payments from one account to another (nothing will be going into my account). On the front-end I have:
checkout(amount) {
const strikeCheckout = (<any>window).StripeCheckout.configure({
key: environment.PRODUCTION==='true'?stripeLiveClientId:stripeTestClientId,
locale: 'auto',
token: (stripeToken: any) => {
if(stripeToken){
console.log(stripeToken)
this.createCharge(stripeToken,amount)
}
}
});
strikeCheckout.open({
name: 'Camel Stripe',
description: 'Stripe Checkout',
amount: amount
});
}
Just a small snipet, but essentially this just captures the credit card and email and ensures it is a valid credit card and then makes a stripe token. I then pass this token to the node backend and do:
stripe.paymentIntents.create({
amount: priceInPence,
currency: 'usd',
source: stripeTokenId,
capture: false, // note that capture: false
}).then(charge => {
return res.json({ success: true, message: 'Success', result: charge })
}).catch(error => {
res.status(400).json({ success: false, status: 400, message: error });
});
//})
};
No matter how I structure it, it always end's up getting the payment to my account, the account with the public/private key. Does anyone know a way I can use this token since it has the necessary information, and send the money to another account?
You can't accept payments and move the funds into a completely different Stripe account unless you're using Connect.
To piggy back on Paul's comment, after much research on Stripe Connect I wanted to share my findings. Please note this is specific to my use case and is using angular/express.
Connect offers multi-party payments, meaning you can receive funds from 1 user, then route them into another users account. You will need a stripe connect account. After that you connect an account, aka create account. They offer 3 account types all these account types need to be
Custom: basically is if you don't want the account you're routing to to see anything stripe related, meaning it is white labeled and the user has no dashboard or view of what is going on in the background.
Standard(the one I chose): this account will have a full stripe dashboard, it has all the functionality that an account would have that you make on your own. You will get a unique account id relevant to your specific connect account environment, on their end, they will have multiple account ids depending on how many other connects they are connected to, or if it is an already existing account. You will need to use this api to get the link to have them verify information before they are integrated into your connect environment account link api
Express: they will have a slimmed down version of a dashboard, it is similar to standard with less functionality
At this point you're ready to start routing payments. I did angular on the front-end, here is an example basically this simply creates a stripeToken, which is a verification that the card data is valid. You need to pass this to a backend to make use of it, this will not create any charges.
On the backend, I used express, you use the charge api You pass the stripeToken as the source attribute. One important step is to add the stripe account id you see in your connected accounts dashboard. It should look similar to:
stripe.charges.create({
amount: priceInPence,
currency: 'usd',
source: stripeTokenId,
capture: false, // note that capture: false
},{
stripeAccount:'{{acctId}}'
})
This should be sufficient to get your payments routing to the necessary account while leveraging a simple UI checkout.
One thing to note about the charges API, this is an older API and has less functionality. For my use case (US CA only with only card payments) it is sufficient. If you have a more complicated use case, or maybe your app is for heavy prod use to many users you should look into payment intents.

How to prevent creating duplicate subscription in stripe when trial period is set on subscription

I am encoutering a problem with stripe. let me explain my working
scenario.my requirement is do not charge user for 14 days with card up front
1)user enter card details
2)sca popup appear
3)regardless of user complete the authentication or not a subscription is created in stripe because i set trial_end_date=>now()+14 days
4)user payment fails in some reason and attempt again, another subscription created
i am worried about the duplicate subscription as the stripe will attempt to pay after the 14 days for both of these subscription as it send a Stripe-hosted link for cardholders for both of these subscription
let me give a snapshot of what i have so far
$data['customer']='customerId';
$data['items']=[];
$data['items'][0]['plan']='stripe_plan_id'
$data['default_payment_method']='pm_xxxx'
$data['trial_end']= strtotime('+14 days');
$data['prorate']=true;
$data['tax_percent']=env('VAT_PERCENTAGE');
$data['expand']= ['latest_invoice.payment_intent', 'pending_setup_intent'];
try {
$subscription=\Stripe\Subscription::create($data);
}
catch(Exception $e) {
return response()->json(['success' => false,'message'=>$e->getMessage()]);
}
what i am missing? how to prevent the duplicate subscription scenario.please expain with the correct example which is i am missing.thanks in advance
I think the problem is in your payment flow, there is not a stripe api that explicitly detects duplicate subscription. You are after all allowed to assign more then 1 subscription per customer id. You can create idempotent keys, but that isn't for the same thing you're talking about, idempotent keys are for accidently hitting the submit button twice within the same timeframe.
The solution would be to attach a payment method to your stripe customer id before your subscription trial is over. For example if you are using stripe elements you would call
Node/JS Example below :
const result = await stripe.createPaymentMethod({
type: 'card',
card: card
})
then pass that result to your backend
const result = await stripe.paymentMethods.attach(paymentMethodId, {customerId})
You do not need to create a new subscription, as one has already been created for that user. Credit cards are assigned to customer Ids, and not to subscriptions. Stripe will do the rest.
You do also need to update the customer with the default payment method as follows :
const customer_update = await stripe.customers.update(stripeCustomerId,{invoice_settings: {default_payment_method:paymentMethodId}});
Now when you visit your dashboard you will see a default card assigned to your customer. Once the subscriptions falls out of the trail period, the default card will be charged.
So in this case there wont be a duplicate subscription created, as you are not calling stripe.subscriptions.create again.

New Customer created by Checkout, then create Subscription on Customer results in Error: This customer has no attached payment source

New Customer created by Checkout, then create a new Subscription on the same Customer by Node SDK results in Error: This customer has no attached payment source.
However if I look at the Customer at the dashboard, there is a Card, but not set as Default. Once it is "Set as Default" by clicking the ... it works.
Here is the code I used to create a new Subscription on a Customer:
const customer = 'cus_xxxxxx'
const plan = 'plan_xxxxxx'
stripe.subscriptions.create({
customer,
items: [
{
plan
}
]
})
I'm not sure if this is a limitation of Checkout since https://stripe.com/docs/payments/checkout says
Better support for saving customer details and reusing saved payment methods
Right now my workaround is to use webhook to update Customer's invoice_settings.default_payment_method on payment_method.attached.
This works but it feels strange. Did I miss something? Why does Checkout not set the only Card as invoice_settings.default_payment_method?
This behavior seems intentional on Stripe's part, the card from Checkout is attached to the Customer as a Payment Method, and is not set as default.
The same thing happens if you create a Customer directly with a PM,
let cust = await stripe.customers.create({ payment_method: "pm_card_visa" });
Also, fwiw, one can create their subscription directly from Checkout, passing a plan instead of sku https://stripe.com/docs/stripe-js/reference#stripe-redirect-to-checkout
From Stripe support:
Checkout does not currently support the ability to reuse saved payment
methods. We are aware that this is a feature request for a lot of our
users, and we are working on implementing this in the future.
If you'd like, you can see a roadmap of the updates we'll be making to
Checkout in the document below.
https://stripe.com/docs/payments/checkout#checkout-roadmap
That said, the work around you're doing for the moment is the same
work around that we're suggesting to users for the time being.
After a lot of digging I realized there is one step that is easy to miss in the docs: take the attached payment method and set it up as a default.
Here is my full server node.js code for creating a subscription:
const customerResponse = await stripe.customers.create({ email, name })
const customer = customerResponse.id
await stripe.paymentMethods.attach(paymentMethodId, { customer })
await stripe.customers.update(customer, {
invoice_settings: { default_payment_method: paymentMethodId }
})
await stripe.subscriptions.create({
customer,
items: [{ price: 'price_XXXXXX' }]
})
paymentMethodId name and email come from the client.

Resources