How to save card details (payment method) in Stripe using backend? - stripe-payments

The docs only covers the case where the payment method is created by the frontend (JS). But there is a risk that the user leaves the website before the frontend sends the information to the backend that the card has been added (and its ID).
In order to make a payment, I need ID of the payment method. I don't want to query Stripe's API for user cards IDs every time before making a payment, so I want to save the payment method ID in my local database. I also want to allow the user to define more payment methods and choose the default one.

Is there a reason you are using the non-recommended workflow you linked to? The most up-to-date version can be found here
Have you checked out using webhook listeners? I use them to create/update my local records.
In the workflow you reference, 3 webhook events potentially fire. First the setup_intent.created event is triggered when your server code generates the SetupIntent and its client_secret. Then, when the user fills out whatever Payment or Card element you instantiate and your frontend code calls the stripe.confirmCardSetup() (or stripe.confirmSetup() in the case of a PaymentElement), both the setup_intent.succeeded and payment_method.attached events will fire.
This last one will POST the payment_method object that was just attached to your customer back to your system. This object will have both the Payment Method ID as well as the associated Customer ID. You can use these to update your local records to map payment methods to customers in your server and avoid unnecessary API calls.

Related

Stripe webhooks to monitor whether customer has an active payment method

Customers on my site use the Stripe Billing portal on user sign up to enter a payment method (card or ACH). What webhook can I use to monitor whether they have at least one active payment method?
I know I can use customer.source.created when a payment method is created and customer.source.deleted when a source is deleted. However a user can initially create two methods and delete one but still have an active method. I just need to know whether they have at least one available
Whenever you receive customer.source.created OR customer.source.deleted events, you typically receive card object as the payload. Since the customer property on the card object is expandable, you may not see it included by default on the card object.
In which case, you'd likely need to retrieve the card object again by calling the API and expanding customer property on it. Once you get the customer ID, you can call this API to list customer's payment methods. This should allow you to see if they have any other PaymentMethods or not.

Adyen session needs to be created after order creation

I am working on a store where we need to integrate Adyen React drop-in components.
I have followed and integrated the guide from here: https://docs.adyen.com/online-payments/web-drop-in
So the workflow that we need to implement is the following:
User adds products to cart
User checkouts and is redirected to checkout page
User adds billing details as well as payment details (card details)
After validation of data, user clicks "Pay"
Order is created
If payment was OK, then order is moved to confirmed status
Else, if payment was refused, order is moved to awaiting payment status.
But right now the integration with Adyen forces us to the following steps:
User adds products to cart
User checks out and is redirected to checkout page
User adds billing details
Adyen session is initiated (with 'reference' to an order ID which is not created at this time), so that the payment details can be filled with user data
User adds payment details
....rest
When creating an Adyen SESSION, it is mandatory to provide a reference, and this reference will be the link between an order and a payment. From the above workflows, you can see that we are forced to have an order ID as soon as the user fills payment data and we don't want that.
We want to create the order only at the end of transaction.
Is it possible to achieve this?
I have tried following the React and NodeJS guides available on git repository but they are just generating a random UUID for every payment
Unfortunately it is not possible to modify the payment reference after creating the session.
The only way around would be to re-create a new session (using the OrderId) but still before performing the payment.

payPal subscription transactions not showing up immediately

I'm creating recurring payments through payPal subscriptions flow. On the frontend, I have a button auto-generated by payPal which redirects me to their domain. After that, it correctly returns the subscriptionID; after that, the frontend calls a backend API which should verify that subscriptionID, to check fields such "status" and so on.
In this backend API, I need to store some "paymentID" (one per month, always unique, if the subscription is automatically renewed monthly), which I found to be called "transactionID" on payPal. I use the REST API GET /v1/billing/subscriptions/{id}/transactions, which should returns an object with a field transactions, which should be an array of transactions. The problem is that sometimes, just after the successful payment, this array is empty. If I wait some time (from few ms to minutes), then every subscription has it's own transactions, in the end.
Is there a way to fix this? Or, alternatively, is there something like transactions on the PayPal environment which can uniquely represents a specific payment?
When a subscription starts it takes time for the first transaction to be created.
Instead of querying the subscriptions API, create a webhook to be notified of PAYMENT.SALE.COMPLETEDevents. This will be useful for the initial transaction as well as all future transactions.

With Stripe, how to attach a single payment method created from Setup Intent API for multiple customers

With Stripe, how to use the payment method created from setup intent for multiple customers?
According tp the Create Setup Intent API, it only let us define one customer ID.
param name: customer (optional)
description of this param:
attached to the Customer on successful setup. Payment methods attached
to other Customers cannot be used with this SetupIntent.ID of the Customer this SetupIntent belongs to, ifone exists. If present, the SetupIntent’s payment method will be
TLDR
I want to let the same user to reuse same payment method to subscribe items with different presentment currencies,
I assume I should solve this requirement by directly creating multiple customer objects for a single user, and using create Setup Intent API to attach his/her same payment method for multiple "customer objects".
But, I am not sure if this is possible, therefore I welcome any recommendation for alternative approaches, such as using attach payment method to customers API, or any workarounds.
PS: Detail info (for why I am interested to ask this question)
Business requirement:
My business requirement is that customers have to subscribe to international services that must be presented with different presentment currencies. To makes thing even harder, each customer has to use the same list of saved payment methods to subscribe to services with different presentment currencies in the future. In other words, users should not have to re-enter card info again if they subscribe to items in different presentment currencies.
Limitation of the Stripe API:
Why not just use single customer object for subscribing to services (aka "price" objects) with multiple presentment currency?
I tried and it results in an error. According to this Stripe documentation, it indeed says that each customer can only be used for transactions in single currency.
If the user in my app want to subscribe to another service with another
currency, I need to create new "Customer" object in Stripe.
Why not just use /v1/payment_methods/:pm_id/attach api to attch payment method to multiple customer object?
Because according to this Attact payment method to customer API doc, Stripe does not recommend, yet I do not fully understand the risk of not performing any necessary steps as it warns. Therefore, it is just my risk averse instinct to avoid this.
To attach a new PaymentMethod to a customer for future payments, we
recommend you use a SetupIntent or a PaymentIntent with
setup_future_usage. These approaches will perform any necessary steps
to ensure that the PaymentMethod can be used in a future payment.
This is not supported. A payment method can only be consumed once if not attached, and attaching to a customer counts as a consumption. A PM can only be attached to a single customer, so you'll need to collect the payment details for each.
Stripe recommends using that customer's currency for all presentment to optimize for their experience, though that may not be suitable for all use cases.

How to identify Stripe webhooks type?

I am using Stripe and I need to understand how to only process webhooks that are generated by Stripe behind the scenes. When my server sends something to Stripe (new subscription, new individual charge) Stripe will generate events that are sent via the webhooks I provide. Well, I don't need to process those since it will create a mess. I only want to process Stripe generated webhooks in situations like: failed charge at subscription renewal, manual modifications via the Stripe dashboard, refunds generated in the dashboard, etc).
I went through the events generated and I cannot find anything that would make a difference from those my API calls generate or those generated behind the scenes.
Is there something I am missing?
Update
- API call: event has a request id
- Stripe behind the scenes: event has null request_id
- Stripe dashboard: event has a request id (This still remains a problem)
If I discard all webhook events that are not null I will also discard Stripe dashboard events. I need to process Stripe behind the scenes and also Stripe dashboard generated events.
First of all, Stripe currently does not support identifying the incoming webhook event type. Looking in the Dashboard I indeed can see what initiated the event (API, Dashboard or Automatic) but Stripe's people said they don't support it.
However, there is a workaround. For anyone struggling with this I will describe what I did. An automatic Stripe generated event is easy to differentiate. It contains a null request field. Any other type of event will have a request id (ex: re_123h2kj18321hjk3218). The problem remains with differentiating between API and Stripe Dashboard generated requests. Therefore, the solution is to capture the request id for every request generated by the API. Whenever a webhook arrives to your server, you check for the request field NOT to be in the Storage System (DB, etc) OR that the request is null. This means that the event was generated by either the Dashboard or Automatically by Stripe (subscription renewal).
Steps:
Hook into the CurlClient provided by Stripe. Extend that class and
override the request() method. The request method returns the
response generated by Stripe servers. Capture the headers of the
response which would contain a Request-id. Store that in your
Storage (in my case a DB)
In your configuration files you need to specify that Stripe should use your own CurlClient. ApiRequestor::setHttpClient(new CurlClient()); (I've named my CurlClient too but you can name it whatever)
When a webhook arrives, you have three options to identify the type:
Automatic: if the event has a request=null
Dashboard: the request is not null and the request is not in you Storage
API: you're left with one situation. The request is not null but exists in your DB
As you can see, there's quite a lot of work for something really easy. All Stripe needs to do is provide another field in their webhook event name something like request-type with three options (api, automatic, dashboard). They already have this build but they don't allow it to be shown in the webhook event.
On the event object documentation, you'll see the request property documented. This property is set whenever the event came from an API request. Otherwise, if it's null it means there was no API request associated with it and it was what we call an Automatic event in the dashboard.
You need to discard any event where request is not null on your end!

Resources