Spree : oauth : Import Shipping/Billing Address along with PaymentSource from Doorkeeper app

If you already have billing/shipping address and payment info of the store in the OAuth provider [eg. some app with Doorkeeper]; and you don’t want to let your users go through  the hassle to fill the form all over again, then, you can reuse the data. For this, you need to return the info along with user info from the OAuth provider in the response.

# in /app/models/spree/user_decorator.rb
class << self
  def from_omniauth(auth_data)
    spree_user = get_or_set_user(auth_data)

    # Extract address info from the payload and update
    #  can update only shipping or both address with same data
    set_shipping_address(spree_user, auth_data) if spree_user.ship_address.nil?
    set_billing_address (spree_user, auth_data) if spree_user.bill_address.nil?
    
    # You might also import PaymentSource info from parent app
    #   Ex: User has already submitted payment info [CreditCard info] and
    #   and you have Stripe PaymentSource Id, then you can import the data
    #   along with other info. I recommend you to use HTTPS for this. 
    set_payment_source(spree_user, auth_data) if spree_user.payment_sources.blank?
 
    # Set user's role; say in parent app the user is Admin, then you
    #  might want to set that user as an Admin in child app as well.
    #  See the function definition below
    set_roles(spree_user, auth_data)
    
    # Need to return the user object
    spree_user
  end

  private
  # all the methods assisting 'from_omniauth' will go below here

private

def get_or_set_user(auth_data)
  params          = { provider: auth_data.provider, uid: auth_data.uid }
  user_with_email = self.where(email: auth_data.info.email).first

  user_with_email.update params if user_with_email.present?

  self.where(params).first_or_create! do |user|
    user.email                 = auth_data.info.email
    user.password              = auth_data.info.encrypted_password
    user.password_confirmation = auth_data.info.encrypted_password
  end
end

#
# Assuming users coming from thePact are either Admin / User
#  no matter if they are Parent / Child
# @param auth_data [OAuth::AuthHash]
# @return [String]
# #
def role_name(auth_data)
  auth_data.info.roles.include?('admin') ? 'admin' : 'user'
end

# Sets roles to spree user identified by OAuth only if not already present
# @param spree_user [Spree::User]
# @param auth_data [OAuth::AuthHash]
# @return [Boolean]
def set_roles(spree_user, auth_data)
  role_name = role_name(auth_data)

  unless spree_user.has_spree_role?(role_name)
    spree_user.spree_roles << Spree::Role.find_by_name(role_name)
  end
end

#
# @return [Spree::Address]
# #
def set_shipping_address(spree_user, auth_data)
  raw_info         = auth_data.extra.raw_info
  country          = Spree::Country.find_by(iso: 'US')
  state            = country.states.find_by_abbr(raw_info.try(:shipping_state) || 'LA')
  shipping_address = Spree::Address.create(
      {
          firstname:         raw_info.first_name,
          lastname:          raw_info.last_name,
          address1:          raw_info.payment_source.try(:shipping_address) || '-',
          address2:          raw_info.payment_source.try(:shipping_apt_or_suite) || '-',
          city:              raw_info.payment_source.try(:shipping_city) || '-',
          zipcode:           raw_info.payment_source.try(:shipping_zip) || '',
          phone:             raw_info.phone,
          state_name:        state.name,
          alternative_phone: nil,
          company:           nil,
          state_id:          state.id,
          country_id:        country.id
      })

  spree_user.update(ship_address_id: shipping_address.try(:id))
end

#
# @return [Spree::Address]
# #
def set_billing_address(spree_user, auth_data)
  raw_info        = auth_data.extra.raw_info
  country         = Spree::Country.find_by(iso: 'US')
  state           = country.states.find_by_abbr(raw_info.payment_source.try(:billing_state) || 'LA')
  billing_address = Spree::Address.create(
      {
          firstname:         raw_info.first_name,
          lastname:          raw_info.last_name,
          address1:          raw_info.payment_source.try(:billing_address) || '-',
          address2:          raw_info.payment_source.try(:billing_apt_or_suite) || '-',
          city:              raw_info.payment_source.try(:billing_city) || '-',
          zipcode:           raw_info.payment_source.try(:billing_zip) || '',
          phone:             raw_info.phone,
          state_name:        state.name,
          alternative_phone: nil,
          company:           nil,
          state_id:          state.id,
          country_id:        country.id
      })

  spree_user.update(bill_address_id: billing_address.try(:id))
end

def set_payment_source(spree_user, auth_data)
  raw_info          = auth_data.extra.raw_info
  payment_source    = raw_info.payment_source || (return false)
  payment_method_id = Spree::PaymentMethod.
      find_by(type: 'Spree::Gateway::StripeGateway').try(:id)

  spree_user.payment_sources.create(
      {
          month:                       payment_source.exp_month,
          year:                        payment_source.exp_year,
          cc_type:                     payment_source.brand,
          last_digits:                 payment_source.last4,
          address_id:                  spree_user.billing_address.try(:id),
          gateway_customer_profile_id: raw_info.stripe_id,
          gateway_payment_profile_id:  payment_source['object_id'], # `object_id` is reserved in ruby
          name:                        payment_source.name,
          default:                     payment_source['default'], # may be the `default` means something else in this context
          payment_method_id:           payment_method_id
      })
end

4 thoughts on “Spree : oauth : Import Shipping/Billing Address along with PaymentSource from Doorkeeper app

  1. chileap says:

    Thank for this guide but could you tell me which part should I put this code? Because I also wanna create shipping/billing address along with user too.

    Like

    • Hi chileap, to address your concern I have updated the blogpost. Please go through the post over again and if still not clear, please feel free to comment. I have also added some more code to help you understand better.

      Thanks for visiting!

      ————————-
      “could you tell me which part should I put this code?”
      –> You have to add the code in `in /app/models/spree/user_decorator.rb`. This is the way we modify the default behavior of Spree Components

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s