-
-
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
Sync Solidus Users and Stripe Customers #82
base: v4
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# frozen_string_literal: true | ||
|
||
module Spree | ||
module CreditCardDecorator | ||
def cc_type=(type) | ||
# See https://stripe.com/docs/api/cards/object#card_object-brand, | ||
# active_merchant/lib/active_merchant/billing/credit_card.rb, | ||
# and active_merchant/lib/active_merchant/billing/credit_card_methods.rb | ||
# (And see also the Solidus docs at core/app/models/spree/credit_card.rb, | ||
# which indicate that Solidus uses ActiveMerchant conventions by default.) | ||
self[:cc_type] = case type | ||
when 'American Express' | ||
'american_express' | ||
when 'Diners Club' | ||
'diners_club' | ||
when 'Discover' | ||
'discover' | ||
when 'JCB' | ||
'jcb' | ||
when 'MasterCard' | ||
'master' | ||
when 'UnionPay' | ||
'unionpay' | ||
when 'Visa' | ||
'visa' | ||
when 'Unknown' | ||
super('') | ||
else | ||
super(type) | ||
end | ||
end | ||
|
||
::Spree::CreditCard.prepend(self) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# frozen_string_literal: true | ||
|
||
module Spree | ||
module OrderDecorator | ||
include StripeApiMethods | ||
|
||
def stripe_customer_params | ||
stripe_customer_params_from_addresses(bill_address, ship_address, email) | ||
end | ||
|
||
::Spree::Order.prepend(self) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# frozen_string_literal: true | ||
|
||
module Spree | ||
module UserDecorator | ||
include StripeApiMethods | ||
|
||
def self.prepended(base) | ||
base.after_create :create_stripe_customer | ||
base.after_update :update_stripe_customer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could create/update the |
||
end | ||
|
||
def stripe_customer | ||
Stripe::Customer.retrieve(stripe_customer_id) if stripe_customer_id.present? | ||
end | ||
|
||
def create_stripe_customer | ||
stripe_customer = Stripe::Customer.create(self.stripe_params) | ||
update_column(:stripe_customer_id, stripe_customer.id) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason for using |
||
stripe_customer | ||
end | ||
|
||
def update_stripe_customer | ||
Stripe::Customer.update(stripe_customer_id, self.stripe_params) | ||
end | ||
|
||
def delete_stripe_customer | ||
if stripe_customer_id.present? | ||
deleted_user = Stripe::Customer.delete(stripe_customer_id) | ||
update_column(:stripe_customer_id, nil) | ||
deleted_user | ||
end | ||
end | ||
|
||
def stripe_params | ||
stripe_customer_params_from_addresses(bill_address, ship_address, email) | ||
end | ||
|
||
::Spree.user_class.prepend(self) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
module StripeApiMethods | ||
extend ActiveSupport::Concern | ||
|
||
def stripe_customer_params_from_addresses(bill_address, ship_address, email) | ||
{ | ||
address: stripe_address_hash(bill_address), | ||
email: email, | ||
name: bill_address.try(:name) || bill_address&.full_name, | ||
phone: bill_address&.phone, | ||
shipping: { | ||
address: stripe_address_hash(ship_address), | ||
name: ship_address.try(:name) || ship_address&.full_name, | ||
phone: ship_address&.phone | ||
} | ||
} | ||
end | ||
|
||
def stripe_address_hash(address) | ||
{ | ||
city: address&.city, | ||
country: address&.country&.iso, | ||
line1: address&.address1, | ||
line2: address&.address2, | ||
postal_code: address&.zipcode, | ||
state: address&.state_text | ||
} | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Stripe.api_key = Spree::PaymentMethod::StripeCreditCard.last&.preferences&.dig(:secret_key) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Stripe |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# frozen_string_literal: true | ||
|
||
class AddStripeCustomerIdToUsers < SolidusSupport::Migration[5.1] | ||
def up | ||
add_column Spree.user_class.table_name.to_sym, :stripe_customer_id, :string, unique: true | ||
|
||
Spree::User.includes(bill_address: :country, ship_address: :country).find_each do |u| | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part could be in a different migration. I believe with the custom user class situation, some users would want to deal with it themselves, and would be easier to skip a migration instead of comment this one. |
||
user_stripe_payment_sources = u&.wallet&.wallet_payment_sources&.select do |wps| | ||
wps.payment_source.payment_method.type == 'Spree::PaymentMethod::StripeCreditCard' | ||
end | ||
payment_customer_id = user_stripe_payment_sources&.map { |ps| ps&.payment_source&.gateway_customer_profile_id }.compact.last | ||
|
||
if payment_customer_id.present? | ||
u.update_column(:stripe_customer_id, payment_customer_id) | ||
u.update_stripe_customer | ||
else | ||
u.create_stripe_customer | ||
end | ||
end | ||
end | ||
|
||
def down | ||
remove_column Spree.user_class.table_name.to_sym, :stripe_customer_id | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,3 +5,4 @@ | |
require "solidus_support" | ||
require "solidus_stripe/engine" | ||
require "solidus_stripe/version" | ||
require "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.
What do you think about having a dedicated class to deal with the
StripeCustomer
management(creation, update, delete, etc)? I don't think the User class should have knowledge about the Stripe integration, even through a decorator.