-
-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Maintain the same Stripe Customer across multiple cards #77
Conversation
Specs are passing for both V2 and V3 with Elements! 🎉 I have never used V3 with Intents before and am unfamiliar with how it works, so I am not sure what to do there. @spaghetticode what do you think? |
f1b4331
to
39fbb69
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@brchristian thank you so much for this PR! 💳 🎉
The approach looks good to me, I think it just needs some polishing before merging. In general, I noticed that there are some commits that can be squashed, as they modify stuff that was introduced earlier with this PR and IMO they have no real reason to be separated. Then there's some code duplication in the specs that would be great if could be avoided somehow.
Also, it would be great if you could review the commit messages. Generally speaking we tend to prefer commits with a body description and capitalized title. This is a great reference for writing good commit messages, it really helped me in the past: https://chris.beams.io/posts/git-commit/
@@ -130,8 +136,8 @@ def create_profile(payment) | |||
else | |||
payment.source.update!( | |||
cc_type: payment.source.cc_type, | |||
gateway_customer_profile_id: response.params['id'], | |||
gateway_payment_profile_id: response.params['default_source'] || response.params['default_card'] | |||
gateway_customer_profile_id: options[:customer] ? response.params['customer'] : response.params['id'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the ternary can become: response.params['customer'] || response.params['id']
. Currently response.params['customer']
is nil
when not reusing a customer, but we can make it a little more future proof with response.params['customer'].presence
, so it will work properly also if we start receiving empty-string values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general I would agree with you, but I think in context with the line that follows, it's a bit more complicated. As you can see, both ternaries use the same setup with options[:customer] ?
.
So I think there's an argument for keeping the logic parallel between the two, even if the first one could be made shorter.
337451a
to
8eb6d6f
Compare
@spaghetticode Thanks for the code review! I squashed into two commits (specs + change) and made all of your suggested changes with two exceptions where I've followed up and which you can see above. LMK on those last two and then we should be just about there! |
6ca923d
to
5d43fed
Compare
Update: I returned to the specs and was able to dedupe 172 lines by following your suggestion. :) That just leaves the last question about |
Hi, I just want to raise a question here : It's possible to delete a customer in the stripe dashboard, and I couldn't find a way to recreate it with the same ID. When it happens it will break this code. I'm not sure if this should be considered or not. |
@igorbp thanks for raising this question 💯 I don't know if it's very common for admins to delete accounts on the stripe dashboards, but I think it's something that we should consider fixing although not necessarily in the first implementation of this feature. |
@spaghetticode I agree with you. Also, I'm aware of #81 and #82. But I think we could move this forward before start the change to the official stripe gem. |
Hi @igorbp I second @spaghetticode in saying this is a very good point. Though we might want to visit that in a future PR. @spaghetticode I agree with @igorbp that it might make sense to merge this before we look at either #81 or #82 which introduce additional changes. Ideally I would like to add some tests as well, because there is certain behavior that differs between Active Merchant and the Stripe Gem – for instance, whether to return In the meantime, however, anything else that I can do to help get this over the finish line? |
Gemfile
Outdated
@@ -27,6 +27,7 @@ group :development, :test do | |||
gem "pry-rails" | |||
gem "ffaker" | |||
gem "rails-controller-testing" | |||
gem "stripe" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to introduce this development dependency? Is it just for testing purposes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kennyadsl Exactly, I used the Stripe gem in the specs as a source for "ground-truth" customer and payment info.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@brchristian thanks for addressing the issues ❤️
I think the change is good, I left some more comments on the code.
One more thing, I would expand the commit message for 5d43f as it's currently not explaining the reason of the change. A good description may help people debugging and understanding what's going on in case of unexpected behavior.
@@ -119,6 +119,14 @@ def create_profile(payment) | |||
creditcard = source | |||
end | |||
|
|||
user_stripe_payment_sources = payment.source.user&.wallet&.wallet_payment_sources&.select do |wps| | |||
wps.payment_source.payment_method.type == 'Spree::PaymentMethod::StripeCreditCard' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can avoid hardcoding the class name here by replacing 'Spree::PaymentMethod::StripeCreditCard'
with self.class.name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea! I’ve made the change and force-pushed.
@@ -0,0 +1,176 @@ | |||
# frozen_string_literal: true | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first 100 lines here are still almost identical to the ones in stripe_checkout.spec. I think that all this duplication may become an issue in the future, as changes in one file will need to be ported to the other, for example if something changes on Solidus FE flow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I’ve moved this to a checkout_helper
, into a method called initialize_checkout
. This deduplicates almost all of the code that was shared between stripe_checkout_spec
and stripe_customer_spec
.
1f10984
to
59f152b
Compare
@spaghetticode I've gone ahead and followed all of your suggestions! I've...
...and CI is green! |
@@ -0,0 +1,57 @@ | |||
# frozen_string_literal: true | |||
|
|||
module SolidusCheckoutHelper |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to have the module name match the filename. Also, since we're in the Solidus Stripe gem, I would rather name the module StripeCheckoutHelper
or something similar, what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spaghetticode Sounds good. I've renamed to StripeCheckoutHelper
and stripe_checkout_helper.rb
and have force-pushed the latest!
59f152b
to
0fe8990
Compare
@spaghetticode What else is needed to push this over the finish line? |
@brchristian sorry for the late answer. I think the changes look good, but the CI is failing. The failure is not related to your changes, but I think it's best to wait for the green. I'll keep an eye on the fix (either in Solidus or Solidus Support) and ping you back if any rebase/change is needed, thanks! |
0fe8990
to
78b5e66
Compare
78b5e66
to
6ea3fa1
Compare
@spaghetticode Now that #99 has been merged, the specs are looking good except for one issue, that only seems to come up in Solidus v2.10 and seems unrelated to these changes – I also notice it in the failing specs for #92, for example. Is this just a fluke, or is there more work to be done on |
@brchristian can you please rebase this branch with master? The postgres build is now failing because of Solidus gem incompatibilities that are solved in master, so hopefully after that it should be passing again. Thanks 🙏 |
These specs address the need in solidusio#26 for maintaining a stable Stripe customer object across multiple credit cards. Currently V2 and V3 Elements are supported, with V3 Intents functionality remaining as a TODO for a later PR.
Avoid creating a new Stripe customer during checkout in both V2 and V3 Elements by checking to see whether this Solidus user has a previous Stripe payment source. Support for V3 Intents will require further work, and will be included in a subsequent PR.
This issue has been automatically marked as stale because it has not had recent activity. It might be closed if no further activity occurs. Thank you for your contributions. |
I think we can close this one. It is covered in the new stripe version (v5). Thanks anyway! |
The initial commit creates a (currently failing) spec to test whether multiple cards used by the same Solidus user produce the same Stripe customer (i.e., the same
gateway_customer_profile_id
).This is the beginning of progress to address #26. The following commits resolve the issue.