Skip to content

Commit

Permalink
Handling addresses between a Spree store and Paypal.
Browse files Browse the repository at this point in the history
The goal of this is to give a store operator the tools needed to be able to
place seller protection elibible payments against Paypal.

The changes are:

* Creating a no_shipping preference to give the store developer a say in whether to send the shipping address. (spree-contrib#113)
* Adds the ability to send a shipping address to Paypal (see spree-contrib#113)
* Adds the option of displaying the shipping address on the Paypal pages.
* Upgrades the Paypal SDK gem
* Adds the ability to request a confirmed shipping address from Paypal.
* Extracts the confirmed address from the Paypal express checkout details when
  we are configured to require a confirmed shipping address.
  • Loading branch information
fredjean committed Oct 7, 2014
1 parent 2f540c3 commit 99a3eab
Show file tree
Hide file tree
Showing 8 changed files with 361 additions and 60 deletions.
45 changes: 42 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ You will also need a "Personal" account to test the transactions on your site. C

#### Spree Setup

In Spree, go to the admin backend, click "Configuration" and then "Payment Methods" and create a new payment method. Select "Spree::Gateway::PayPalExpress" as the provider, and click "Create". Enter the email address, password and signature from the "API Credentials" tab for the **Business** account on PayPal.
In Spree, go to the admin backend, click "Configuration" and then "Payment Methods" and create a new payment method. Select "Spree::Gateway::PayPalExpress" as the provider, and click "Create". Enter the email address, password and signature from the "API Credentials" tab for the **Business** account on PayPal.

### Production setup

Expand All @@ -56,15 +56,15 @@ This Spree extension supports *some* of those. If your favourite is not here, th

### Solution Type

Determines whether or not a user needs a PayPal account to check out.
Determines whether or not a user needs a PayPal account to check out.

```ruby
payment_method.preferred_solution_type = "Mark"
# or
payment_method.preferred_solution_type = "Sole"
```

"Mark" if you do want users to have a paypal account, "Sole" otherwise.
"Mark" if you do want users to have a paypal account, "Sole" otherwise.

### Landing Page

Expand All @@ -88,6 +88,45 @@ payment_method.preferred_logourl = 'http://yoursite.com/images/checkout.jpg'

**Must** be an absolute path to the image.

### Displaying Shipping Addresses

You have the option of displaying the shipping address of an order on
the PayPal pages:

```ruby
# Displays the shipping address of the order on the Paypal page
payment_method.preferred_no_shipping = '0'

# Do not display the shipping address on the Paypal page
# This is the default configuration since this matches the pre-existing
# behavior of the gem
payment_method.preferred_no_shipping = '1'

# Display the shipping address listed in the profile if it is not
# sent along with the order
payment_method.preferred_no_shipping = '2'
```

This has no effect on what shipping address is passed to the gateway.

### Overriding the Shipping Address

By default PayPay will use the shipping address that it has on file as
the shipping address for an order.

You can configure the gateway to send up the shipping address associated
with the order through the following configuration:

```ruby
# Do not override the shipping address on file (default)
payment_method.preferred_address_override = '0'

# Override the shipping address on file
payment_method.preferred_address_override = '1'
```

The shipping address will be sent to the server if configured to do so.

## Caveats

*Caveat venditor*
Expand Down
49 changes: 29 additions & 20 deletions app/controllers/spree/paypal_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,23 @@ def line_item(item)
end

def express_checkout_request_details order, items
{ :SetExpressCheckoutRequestDetails => {
:InvoiceID => order.number,
:BuyerEmail => order.email,
:ReturnURL => confirm_paypal_url(:payment_method_id => params[:payment_method_id], :utm_nooverride => 1),
:CancelURL => cancel_paypal_url,
:SolutionType => payment_method.preferred_solution.present? ? payment_method.preferred_solution : "Mark",
:LandingPage => payment_method.preferred_landing_page.present? ? payment_method.preferred_landing_page : "Billing",
:cppheaderimage => payment_method.preferred_logourl.present? ? payment_method.preferred_logourl : "",
:NoShipping => 1,
:PaymentDetails => [payment_details(items)]
}}
request_details = {
:InvoiceID => order.number,
:BuyerEmail => order.email,
:ReturnURL => confirm_paypal_url(:payment_method_id => params[:payment_method_id], :utm_nooverride => 1),
:CancelURL => cancel_paypal_url,
:SolutionType => payment_method.preferred_solution.present? ? payment_method.preferred_solution : "Mark",
:LandingPage => payment_method.preferred_landing_page.present? ? payment_method.preferred_landing_page : "Billing",
:cppheaderimage => payment_method.preferred_logourl.present? ? payment_method.preferred_logourl : "",
:NoShipping => payment_method.preferred_no_shipping.present? ? payment_method.preferred_no_shipping : '1',
:PaymentDetails => [payment_details(items)]
}

# Optional fields without set defaults.
request_details[:AddressOverride] = payment_method.preferred_address_override if payment_method.preferred_address_override.present?
request_details[:ReqConfirmShipping] = payment_method.preferred_req_confirmed_address if payment_method.preferred_req_confirmed_address.present?

{ :SetExpressCheckoutRequestDetails => request_details }
end

def payment_method
Expand Down Expand Up @@ -156,15 +162,17 @@ def payment_details items
def address_options
return {} unless address_required?

address = current_order.use_billing ? current_order.bill_address : current_order.ship_address

{
:Name => current_order.bill_address.try(:full_name),
:Street1 => current_order.bill_address.address1,
:Street2 => current_order.bill_address.address2,
:CityName => current_order.bill_address.city,
:Phone => current_order.bill_address.phone,
:StateOrProvince => current_order.bill_address.state_text,
:Country => current_order.bill_address.country.iso,
:PostalCode => current_order.bill_address.zipcode
:Name => address.try(:full_name),
:Street1 => address.address1,
:Street2 => address.address2,
:CityName => address.city,
:Phone => address.phone,
:StateOrProvince => address.state_text,
:Country => address.country.iso,
:PostalCode => address.zipcode
}
end

Expand All @@ -173,7 +181,8 @@ def completion_route(order)
end

def address_required?
payment_method.preferred_solution.eql?('Sole')
payment_method.preferred_solution.eql?('Sole') \
|| payment_method.preferred_address_override.eql?('1')
end
end
end
58 changes: 58 additions & 0 deletions app/models/spree/gateway/pay_pal_express.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,28 @@ class Gateway::PayPalExpress < Gateway
preference :solution, :string, default: 'Mark'
preference :landing_page, :string, default: 'Billing'
preference :logourl, :string, default: ''
# Indicates whether to display the shipping address on the paypal checkout
# page.
#
# 0 - Paypal displays the shipping address on the page.
# 1 - Paypal does not display the shipping address on its pages. This is
# the default due to the history of this gem.
# 2 - Paypal will obtain the shipping address from the profile.
preference :no_shipping, :string, default: '1'

# Allow Address Override
# 0 - Do not override the address stored at PayPal
# 1 - Override the address stored at Paypal
# By default, the shipping address is not overriden on Paypal's site
preference :address_override, :string

# Whether to require a confirmed address
# 0 - Do not require a confirmed address
# 1 - Require a confirmed address
#
# Paypal recommends that you do not override the address if you are
# requiring a confirmed address for this order.
preference :req_confirmed_address, :string

def supports?(source)
true
Expand Down Expand Up @@ -55,6 +77,14 @@ def purchase(amount, express_checkout, gateway_options={})
# This is mainly so we can use it later on to refund the payment if the user wishes.
transaction_id = pp_response.do_express_checkout_payment_response_details.payment_info.first.transaction_id
express_checkout.update_column(:transaction_id, transaction_id)

# We need to get a hold of the confirmed address
address = extract_address(pp_details_response)

if address && address.valid?
express_checkout.update_column(:address_id, address.id)
end

# This is rather hackish, required for payment/processing handle_response code.
Class.new do
def success?; true; end
Expand Down Expand Up @@ -99,6 +129,34 @@ def refund(payment, amount)
end
refund_transaction_response
end

private

def extract_address(response)
return nil unless self.preferred_req_confirmed_address == '1'

express_checkout_details = response.get_express_checkout_details_response_details
payment_details = express_checkout_details.PaymentDetails.first
payer_info = express_checkout_details.PayerInfo
ship_to_address = payment_details.ShipToAddress

return nil unless ship_to_address && payer_info

if state = Spree::State.find_by_abbr(ship_to_address.state_or_province)
Spree::Address.create(
firstname: payer_info.payer_name.first_name,
last_name: payer_info.payer_name.last_name,
address1: ship_to_address.street1,
address2: ship_to_address.street2,
city: ship_to_address.city_name,
state_id: state.id,
state_name: state.name,
country_id: state.country.id,
zipcode: ship_to_address.postal_code,
phone: ship_to_address.phone || "n/a"
)
end
end
end
end

Expand Down
4 changes: 3 additions & 1 deletion app/models/spree/paypal_express_checkout.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
module Spree
class PaypalExpressCheckout < ActiveRecord::Base
belongs_to :address, class_name: "Spree::Address"
alias_attribute :confirmed_address, :address
end
end
end
5 changes: 5 additions & 0 deletions db/migrate/20141006155949_add_address_to_express_checkout.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddAddressToExpressCheckout < ActiveRecord::Migration
def change
add_reference :spree_paypal_express_checkouts, :address
end
end
Loading

0 comments on commit 99a3eab

Please sign in to comment.