Skip to content

Payment Intents with card pre-authorization#43

Closed
spaghetticode wants to merge 4 commits into
solidusio:masterfrom
spaghetticode:spaghetticode/card-pre-auth
Closed

Payment Intents with card pre-authorization#43
spaghetticode wants to merge 4 commits into
solidusio:masterfrom
spaghetticode:spaghetticode/card-pre-auth

Conversation

@spaghetticode

Copy link
Copy Markdown
Member

At the moment payments done with Payment Intents end up in a double charge for the customer.

A first charge is created instantly when the customer authorizes their card, and a second one is created later after order confirmation. This issue is not of easy solution, as the first charge is always required for Payment Intents, and the second one is created automatically by Solidus flow.
Also, the first charge happens outside of Solidus' current processes, so there's no record of it in on the DB.

A possible temporary workaround for this issue is to introduce the concept of card pre-authorization: the first charge, now limited to only 1$ is not captured anymore, and have an appropriate description so, even when customers receive notifications for uncaptured transactions from their bank, they will understand this is not going to be actually captured.

At the moment payments done with Payment Intents end up in a double
charge for the customer. A first charge is created istantly when
the customer authorizes their card, and a second one is created later
after order confirmation. This issue is not of easy solution, as the
first charge is always required for Payment Intents, and the second
one is created automatically by Solidus flow.

A possible temporary workaround for this issue is to introduce the
concept of card pre-authorization: the first charge, now limited to
only 1$ is not captured anymore, and have an appropriate description
so, even when customers receive notifications for uncaptured
transactions from their bank, they will understand this is not going
to be actually captured.
@spaghetticode spaghetticode added the bug Something isn't working label Mar 27, 2020
@spaghetticode spaghetticode self-assigned this Mar 27, 2020

@kennyadsl kennyadsl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a couple of questions, thanks Andrea!

# frozen_string_literal: true

module ActiveMerchant::StripePaymentIntentsGatewayDecorator
def authorize(money, payment_method, options = {})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we pass this option to AM via the authorize method in our StripePaymentMethod?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that makes more sense, thanks!

if params[:stripe_payment_method_id].present?
intent = stripe.create_intent(
(current_order.total * 100).to_i,
100,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's wrong with preauthorizing the full amount?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that would block twice the full amount on the customer card.

Also, as a customer, I would not be particularly scared by noticing a $1 pre-authorization charge notice (I've seen something similar done in the past by PayPal and Amazon as well if I recall correctly), but seeing the double amount I would instantly think there's been some double charge error.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, but if you have $1 on your card, it's not guaranteed that you have the full amount to pay the order. Anyway, I'm ok with this strategy, and we can revisit it later. We just need to be clear in the README that this is the behavior of the gem. Also, what about pre-authorizing $0.01 instead?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, you can't pre-authorize less than 0.50$ (or 0.50€... so why not 0.50? if you're in EU and you're buying in USD, the conversion would be 0.43€ which is not acceptable again).
I'm not sure what happens when you don't have enough funds for the order, but I guess that at that point the second payment would fail (for sure the capture would fail).

Anyway, the second payment is Solidus' default process, for example what happens with Solidus Stripe and Elements. The failed pre-authorization for the whole amount would behave just like in that case.

@aldesantis aldesantis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spaghetticode this is looking good as a workaround, although it's not an ideal UX. For a long-term solution, would it make sense to implement some kind of API in the core to support this?

@spaghetticode

Copy link
Copy Markdown
Member Author

@aldesantis the long term solution would be, in my opinion, to only save the Payment Intents charge in Solidus (with the full amount, uncaptured) and don't create the second one (which is currently created automatically). So, not sure what changes we would need inside Solidus, if any at all... this is something I still have to look into.

@spaghetticode
spaghetticode force-pushed the spaghetticode/card-pre-auth branch 4 times, most recently from 07dc18d to 3e55d38 Compare March 27, 2020 14:47
end

def authorize(money, creditcard, transaction_options)
transaction_options.merge!(off_session: true)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to add this option only when using intents?

When the customer pays with a 3D-Secure card we need to explicitly set the
`off_session` param so the charge for their order will not require to go
through the 3D-Secure verification step. Failing this, it will not be
possible to capture the payment on Solidus backend or Stripe dashboard.

This is legitimated by the fact that when we create a Payment Intent, we
explicitly allow this by using `setup_future_usage: 'off_session'`.
The card ending with 3184 is not compatible with real-life scenarions
as it will always require customer manual authorization, regardless of
the actual settings for the payment and payment method used.

So, the new card ending with 3155 simulates more realisticly what
happens in real life, being still 3D-Secure, but allowing payment
methods to create off-session charges.
@spaghetticode
spaghetticode force-pushed the spaghetticode/card-pre-auth branch from 3e55d38 to 2108717 Compare March 27, 2020 15:08
@spaghetticode

Copy link
Copy Markdown
Member Author

Superseded by #45

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants