spree / spree_gateway

Huge collection of payment gateways for @spree. Stripe, Braintree, Apple Pay, Authorize.net and many others!
https://spreecommerce.org
BSD 3-Clause "New" or "Revised" License
220 stars 438 forks source link

Stripe Strong Customer Authentication #330

Closed manikkang closed 4 years ago

manikkang commented 5 years ago

Stripe Api uses new methods for creating the charge. Has the gem being updated to accordingly?

dyerc commented 5 years ago

I've just been investigating this myself. It looks as though spree_gateway doesn't currently support this. It supports v3 of the Stripe api, but still uses the old methods for creating a token and active merchant to process the payment. This doesn't work for supporting 3D secure which appears to by default require using Stripe payment intents functions. I could be wrong, this is how I interpreted it.

There is a pull request for active merchant which seems to support the new Stripe payment intents api (https://github.com/activemerchant/active_merchant/pull/3290).

Once this is merged into active merchant, this gem will need updating to use the new functionality. I'm hoping this will enable support for SCA and 3D Secure, I guess by redirecting the user to the 3D secure confirmation page?

It would be really helpful to have an update from someone who knows more about the spree_gateway gem, because the deadline is just over a month away. I'm happy to help writing a pull request.

waaux commented 5 years ago

Few days ago Active Merchant added the new gateway Stripe Payment Intents to support SCA and 3D Secure. Is anyone working on updating this gem to be able to use this gateway??

https://github.com/activemerchant/active_merchant/blob/master/lib/active_merchant/billing/gateways/stripe_payment_intents.rb

damianlegawiec commented 5 years ago

Hey @dyerc @waaux we're always happy reviewing and merging PR with this feature, thanks!

imotion-software commented 5 years ago

Any news about Stripe Payment Intents API implementation? In Europe it's mandatory since September...

AnkurVyas-BTC commented 4 years ago

@damianlegawiec @dyerc @manikkang - Any update on this?

damianlegawiec commented 4 years ago

@AnkurVyas-BTC you can track progress here: https://github.com/spree/spree_gateway/pull/355

AnkurVyas-BTC commented 4 years ago

@damianlegawiec - Cool :+1:

weefunker commented 4 years ago

Any word on this being updated? I just got an email from Stripe warning that payments are going to be declined

satyakam-nividata commented 4 years ago

Any updates about when this PR #355 will be merged?

mvanio commented 4 years ago

Are we there yet? #355 seems complete or am I wrong?

eduardstinga commented 4 years ago

I'm interested in an update as well

satyakam-nividata commented 4 years ago

Hello @damianlegawiec In pull request mentioned above #355 we only give support for processing payments using Stripe's PaymentIntent in Spree UI. Are we going to add support for Spree Storefront API V2 too? I'm working on a project which requires SCA supported payments. I'm no expert here, but it'll be great if you can lead me in the right direction.

Currently, I tried to use the code of #355 PR, and I can easily process normal payments by using Stripe's createToken method. But it seems that the method doesn't support SCA. So now, I'm wondering how to make it work with Stripe's PaymentMethod creation method.

I'm using https://github.com/spree/spree-storefront-api-v2-js-sdk and stripe/react-stripe-js v1.1.2 and stripe/stripe-js v1.5.0

Here's the code snippet. `const result = await stripe.createPaymentMethod({ type: 'card', card: elements.getElement(CardElement), billing_details: { name: 'ABC XYZ' } });

if (!result.error) { const order = {payments_attributes: [{payment_method_id: '7'}]}; const { userAddress } = getState(); const { firstname, lastname } = userAddress; const { exp_year, exp_month, last4 } = result.paymentMethod.card; const paymentSource = {'7': { gateway_payment_profile_id: result.paymentMethod.id, number: last4, month: exp_month, year: exp_year}}; const response = await client.checkout.orderUpdate({ bearerToken: token }, {...params, include: 'payments'}); // Here I get error from backend server, that it cannot }`

And in the backend, I get below error message.

Spree::Core::GatewayError at /api/v2/storefront/checkout

You cannot supply a PaymentMethod ID as the source parameter. Please use the payment_method parameter instead. If you wish to set a default payment method for this Customer for future Invoices, set invoice_settings.default_payment_method.

Thanks.

satyakam-nividata commented 4 years ago

I believe #355 handles all Spree UI stuff. I need to make it work for API V2, so it'll be great if you can lead me in the right direction. @damianlegawiec

satyakam-nividata commented 4 years ago

I believe #355 handles all Spree UI stuff. I need to make it work for API V2, so it'll be great if you can lead me in the right direction. @damianlegawiec

I found the solution. Thanks for implementing Stripe SCA.

damianlegawiec commented 4 years ago

@satyakam-nividata could you share that solution? Yes, our plan is to have this also in the headless API mode as well

satyakam-nividata commented 4 years ago

Here's how I managed to make it work in API mode. In frontend code, I've added Stripe's CardElement, configured it in the checkout form.

const result = await stripe.createToken(elements.getElement(CardElement), { name: 'First Name + Last Name', });

I used result.token.id in the below code.

` const { token } = nextCookie({});

const payment = await client.checkout.paymentMethods({ bearerToken: token, });

if (payment.isSuccess()) { const { id } = payment .success() .data.find( item => item.attributes.type === 'Spree::Gateway::StripeElementsGateway', ) || {}; const order = { payments_attributes: [ { payment_method_id: id, }, ], };

const paymentSource = { [id]: { gateway_payment_profile_id: result.token.id } };

const checkout3 = await dispatch(
  orderUpdate({ order, payment_source: paymentSource }),
);
// I'll add below Axios call to Spree sdk 
const pc_data = await axios.patch(
  `https://localhost:3000/api/v2/storefront/checkout/payment_confirm`,
  { access_token: token },
);

if (pc_data.data.client_secret) {
  const ccp_resp = await stripe.confirmCardPayment(
    pc_data.data.client_secret,
    {},
  );

  // I'll add below Axios call to Spree sdk 
  const hr_data = await axios.patch(
    `https://localhost:3000/api/v2/storefront/checkout/handle_response`,
    { access_token: token, response: ccp_resp },
  );

  if (!hr_data.data.success) {
    dispatch(handlePaymentFailure());
    toast.error(<ToastError>Payment declined.</ToastError>);
    dispatch(updateCheckoutStatus('error'));
    return;
  }
}

// Order complete
const complete = await client.checkout.complete(
  { bearerToken: token },
  {
    include: 'variants',
    fields: orderCompleteFields,
  },
);

`

I added 2 methods in CheckoutControllerDecorator

`module MyProject module CheckoutControllerDecorator

def payment_confirm
  spree_authorize! :update, spree_current_order, order_token

  if spree_current_order.intents?
    spree_current_order.tap do |order|
      order.process_payments!
      order.reload.payments.valid.where.not(intent_client_key: nil).last.tap do |payment|
        client_secret = payment.intent_client_key
        pk_key = payment.payment_method.preferred_publishable_key
        render json: {client_secret: client_secret, pk_key: pk_key}, status: :ok and return
      end
    end
  end

  render json: {client_secret: '', pk_key: ''}, status: :ok
end

def handle_response
  spree_authorize! :update, spree_current_order, order_token

  if params['response']['error']
    payment = spree_current_order.payments.find_by!(response_code: params['response']['error']['payment_intent']['id'])
    payment.update(state: 'failed', intent_client_key: nil)
    render json: {success: false}, status: :ok
  else
    render json: {success: true}, status: :ok
  end
end

end end

Spree::Api::V2::Storefront::CheckoutController.prepend MyProject::CheckoutControllerDecorator `

In routes.rb

namespace :api do namespace :v2 do namespace :storefront do resource :checkout, controller: :checkout, only: %i[update] do patch :payment_confirm patch :handle_response end end end end

I think it's good to go. Any suggestions are much appreciated.

satyakam-nividata commented 4 years ago

Hello @damianlegawiec, Any suggestions for the above solution? And it creates customers in stripe per order. Is it normal? I think we should assign only one Stripe customer to Spree::User rather than having multiple stripe customers.