openfoodfoundation / openfoodnetwork

Connect suppliers, distributors and consumers to trade local produce.
https://www.openfoodnetwork.org
GNU Affero General Public License v3.0
1.12k stars 724 forks source link

Make new Stripe SCA payment method (Stripe Payment Intents API) work with cards already stored through the old api (Stripe Charges API) #4685

Closed luisramos0 closed 4 years ago

luisramos0 commented 4 years ago

Description

This is a part of #4173 - this will make the new Stripe SCA payment method able to replace the existing Stripe payment method.

When a user stores a card in the account section, the add card form uses the old stripe charges api to store the card. Currently the new Stripe SCA payment method is not working well with these cards. We need to make this work.

This is what is currently happening in the spree_credit_cards DB right now.

gateway_customer_profile_id |  gateway_payment_profile_id
----------------------------+------------------------------
                            | tok_1G1vH8GzkMsqbAZX26ai42HZ -  checkout with **Stripe**  not saving card
 cus_GZ3Pl7DWFo5fpg         | card_1G1vIVGzkMsqbAZXegrNIusw - checkout with **Stripe**  saving card
 cus_GZ3VpwwHg64tVO         | card_1G1vOJGzkMsqbAZX7JaD2M9Q - add card in account section (uses old API)
                            | pm_1G1vKgG0lscsYhYacGoC85jv - checkout **Stripe SCA** not saving card
 cus_GZ3TNw3EjFPk1f         | pm_1G1vMEG0lscsYhYa04bGSd6l - checkout **Stripe SCA** saving card

The old api stores tok (tokens) for one time payments and when saving a card uses card (cards) for the card and cus (customer) for the customer. The new api uses pm (payment methods) for one time payments as well as for the card when saving cards. It also uses cus_ (customer) to store the user id on the stripe side.

The one time payments on the old api are not important as they are not reusable. We can ignore the entries with tokens.

I believe all we need to do is to make the new api calls work with the card objects. In some way we need to convert a card id into a pm id. This could be done as a migration or on the fly when the card is used.

Maybe we can make the payment process convert card to pm and store thee new pm_ in the database.

Acceptance Criteria & Tests

We have two use cases: 1 - A customer with a saved card that has been used with the existing Stripe payment method should be able use this same card with the new Stripe SCA payment method. (this includes 2 cases: cards saved on checkout with "old" Stripe payment method and cards added in account through "add a card") 2 - A customer should be able to add a card in the account section and use it with the new Stripe SCA payment method.

luisramos0 commented 4 years ago

this is probably what we need to do: https://stripe.com/docs/api/payment_methods/create#create_payment_method-card

  1. get a fresh token from the card id stored in the DB (this is what happens now when paying with an existing card against the old Charges API
  2. use this payment methods endpoint to create a new payment method with the card token
  3. use the new payment method to make the payment against the new Payment Intents API
  4. replace card_id with pm_id in the database so we dont need to perform step 1 and 2 the next usage of the card
luisramos0 commented 4 years ago

ok, I have this sorted, it's nothing to do with what I described above :-)

The card_ids already stored in stripe will just work on the new API BUT the cards are currently being stored on the platform account and they need to be stored on the connected accounts (hub's accounts). To do that, before we charge the card with the Payment Intents API, we need to get the customer_id and card_id on the platform account and:

this is the code that does this:

        payment_method = Stripe::PaymentMethod.create({ customer: customer_id, payment_method: card_id }, { stripe_account: stripe_account_id })
        new_customer = Stripe::Customer.create({ email: creditcard.user.email }, { stripe_account: stripe_account_id })
        attached_payment_method = Stripe::PaymentMethod.attach(payment_method.id, { customer: new_customer.id }, { stripe_account: stripe_account_id })