Stripe update subscription and add new payment method with 3d secure - stripe-payments

I created a subscription as described on https://stripe.com/docs/billing/subscriptions/elements but now I want to give the user the choice to change the plan on the subscription and use another payment method, ex 3d Secure card. However if I update the subscription to get a client secret for a new payment intent as following:
func (c *Client) UpdateSubscription(s *models.Subscription) (*models.Subscription, error) {
sps := &stripe.SubscriptionParams{
DefaultPaymentMethod: stripe.String(s.PaymentMethodId),
CancelAtPeriodEnd: stripe.Bool(false),
ProrationBehavior: stripe.String(string(stripe.SubscriptionProrationBehaviorAlwaysInvoice)),
}
if s.CreatePaymentIntent {
s.PaymentBehavior = "allow_incomplete"
sps.PaymentBehavior = stripe.String(s.PaymentBehavior)
sps.AddExpand("latest_invoice.payment_intent")
} else if s.ItemID != "" {
sps.Items = []*stripe.SubscriptionItemsParams{
{Price: stripe.String(s.PriceID)},
{ID: stripe.String(s.ItemID), Deleted: stripe.Bool(true)},
}
}
ss, err := sub.Update(s.ID, sps)
if ss.LatestInvoice != nil && ss.LatestInvoice.PaymentIntent != nil {
s.PaymentIntentClientSecret = ss.LatestInvoice.PaymentIntent.ClientSecret
}
return s, err
}
the PaymentIntentClientSecret is the same for the subscription which means it is already processed.
Stripe 'confirm card' API is throwing error payment_intent_unexpected_state https://stripe.com/docs/error-codes/payment-intent-unexpected-state and that's probably because I used that payment intent previously to create the subscription. However I still need a new payment intent to authorise the new card.

What is the status of the PaymentIntent that you are getting these errors on?
It looks like you are properly updating the Subscription’s Price and PaymentMethod as well as creating a new Invoice for this proration. If that is the case, the Invoice may automatically be getting paid on that update. So this error might be happening because you are trying to confirm a PaymentIntent that has already successfully charged your customer.
I think it would be good to check the status of the LatestInvoice’s PaymentIntent when you get the updated Subscription object in your function.
If the status is succeeded you can simply send the customer back that updating their subscription was successful.
If the status is requires_action you should send the PaymentIntent’s client secret back to your client side and call handleCardAction[1] to allow your customer to resolve whatever action may need to be taken (3DS Auth, etc)
If the status is requires_payment_method the new card was declined and you should have your customer input new credit card info to try to update the subscription’s PaymentMethod again.
[1]https://stripe.com/docs/js/payment_intents/handle_card_action

Related

Creating Stripe transfers at the same time as creating Payment intent

I'm using Stripe and am trying to implement the scenario described here
The frontend is making a call to the backend that has this code
var service = new PaymentIntentService();
var createOptions = new PaymentIntentCreateOptions
{
PaymentMethodTypes = new List<string>
{
"card",
},
Amount = (long?)(payment.Amount * 100),
Currency = "EUR"
};
var result = await service.CreateAsync(createOptions);
return result.ClientSecret
In the documentation it says that the below code should be run "later" but it doesn't specify when. In my case I would prefer submitting the transfers to Stripe as soon as possible and preferably connecting them to the above payment so the transfers would be automatically handled by Stripe when the payment is done.
As I read the documentation (https://stripe.com/docs/api/transfers/create#create_transfer-source_transaction) SourceTransaction could be used for this.
var transferService = new TransferService();
var transfer1Options = new TransferCreateOptions
{
Amount = 100,
Currency = "eur",
Destination = "{{CONNECTED_STRIPE_ACCOUNT_ID}}",
SourceTransaction = result.Id
};
var transfer1 = transferService.Create(transfer1Options);
The result -variable contains a Charges -list but that is empty and when doing the above, i.e. adding the payment intent's id to SourceTransaction property, I get an error that the charge does not exist.
In the documentation it says that the below code should be run "later" but it doesn't specify when
It's whenever you are ready to make the transfer, after the payment has succeeded, essentially.
As I read the documentation (https://stripe.com/docs/api/transfers/create#create_transfer-source_transaction) SourceTransaction could be used for this.
Yep, you should definitely use source_transaction here! Otherwise the transfers won't succeed since the funds would not be available yet.
The result -variable contains a Charges -list but that is empty and when doing the above, i.e. adding the payment intent's id to SourceTransaction property, I get an error that the charge does not exist.
The value to pass to source_transaction is indeed the intent.charges.data[0].id , of a PaymentIntent that has succeeded and has a Charge.
You don't have a Charge ID because you haven't actually processed a payment yet. Creating a PaymentIntent doesn't process a payment.
You have to do the normal full PaymentIntent integration by building a form that collects payment details and 'confirms' the PaymentIntent, that's the action that processes the payment, and after that succeeds, the charges array will have a Charge in it that can be used for the transfer. For example you might create the transfer as part of handling the payment_intent.payment_succeeded webhook event.
https://stripe.com/docs/payments/accept-a-payment?platform=web&ui=elements

How to update credit card payment in stripe

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

Saving credit card of a Custom Account's customer in Stripe

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).

How to retrieve the Stripe fee for a payment from a connected account (Node.js)

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

Stripe Webhook Response

Why i got Customer id and invoice id cus_0000000000000 and in_0000000000000 like these? in stripe webhook response.
I am checking my events and response in my account in this i got response 200 ok,but got customer id and invoice id got like these.why? i am checking send test webhook event at this i got response like these.
public function actionStripeHook() {
if (Yii::app()->request->isPostRequest) {
try {
$postdata = file_get_contents("php://input");
$event = json_decode($postdata);
switch ($event->type) {
case 'invoice.payment_succeeded':
Yii::log('', 'trace', 'stripe');
Yii::log('==================================', 'trace', 'stripe');
Yii::log('==== Event (' . $event->type . ') ====', 'trace', 'stripe');
Yii::log('==================================', 'trace', 'stripe');
$customer_id = $event->data->object->customer;
$customer = Stripe_Customer::retrieve($customer_id);
$invoice = Stripe_Invoice::retrieve($event->data->object->id);
}
What's Wrong in my code Its my action in Webhook endpoint in stripe,I got event type invoice.payment_succeeeed its coming from that case,but cant get customer id and invoice id correctly in my Response.why?
Nothing is wrong with your code -- the "Send test webhook" button sends a correctly formatted JSON event, but all the resources IDs are xxx_000000000.
The "Send test webhook" button is mostly useful for testing connectivity between Stripe and your webhook handler. If you want to test actual functionality, you should generate the events "organically", i.e. by sending the requests that will trigger the events you want.
For instance, to generate an invoice.payment_succeeded event, you should:
create a customer, with a valid card (e.g. 4242424242424242)
create a subscription for this customer
Upon creating the subscription, an invoice will be created and payment will be immediately attempted. If the payment is successful, then an invoice.payment_succeeded event will be emitted (with valid IDs).

Resources