spree_paypal_website_standard 0.8.2 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -34,7 +34,7 @@ You may want to implement your own custom logic by adding 'state_machine' hooks.
34
34
 
35
35
  Add to your Spree application Gemfile.
36
36
 
37
- gem "pp_website_standard", :git => "git://github.com/tomash/spree-pp-website-standard.git"
37
+ gem "spree_paypal_website_standard", :git => "git://github.com/tomash/spree-pp-website-standard.git"
38
38
 
39
39
  Run the install rake task to copiy migrations (create payment_notifications table)
40
40
 
@@ -60,10 +60,29 @@ The following configuration options (keys) can be set:
60
60
  :currency_code # default EUR
61
61
  :sandbox_url # paypal url in sandbox mode, default https://www.sandbox.paypal.com/cgi-bin/webscr
62
62
  :paypal_url # paypal url in production, default https://www.paypal.com/cgi-bin/webscr
63
- :populate_address (true/false) # pre-populate fields of billing address based on spree order data
63
+ :populate_address # (true/false) pre-populate fields of billing address based on spree order data
64
+ :encryption # (true/false) use encrypted shopping cart
65
+ :cert_id # id of certificate used to encrypted stuff
66
+ :ipn_secret # secret string for authorizing IPN
64
67
 
65
- Only the first three ones need to be set up in order to get running.
68
+ Only the first three ones need to be set up in order to get running.
66
69
 
70
+ The last three are required for secure, encrypted operation (see below).
71
+
72
+ ## Encryption / Security
73
+
74
+ The payment link can be encrypted using an SSL key pair and a PayPal public key. In order to attempt this encryption, the following elements must be available. If these are not available a normal link will be generated.
75
+
76
+ Spree::Paypal::Config[:encrypted] must be set to true.
77
+ Spree::Paypal::Config[:cert_id] must be set to a valid certificate id.
78
+ Spree::Paypal::Config[:ipn_secret] must be set to a string considered secret.
79
+ Application must have a Rails.root/certs directory with following files:
80
+
81
+ app_cert.pem # application certificate
82
+ app_key.pem # application key
83
+ paypal_cert_#{Rails.env}.pem # paypal certificate
84
+
85
+ The best instructions on what is what, how these files should be generated etc. are [in AsciiCast 143](http://asciicasts.com/episodes/143-paypal-security) (basically the code of this extension is also based on this AsciiCast).
67
86
 
68
87
  ## IPN Notes (outdated!)
69
88
 
@@ -2,12 +2,18 @@ class PaymentNotificationsController < ApplicationController
2
2
  protect_from_forgery :except => [:create]
3
3
  skip_before_filter :restriction_access
4
4
 
5
- def create
5
+ def create
6
+ if(Spree::Paypal::Config[:encryption] && (params[:secret] != Spree::Paypal::Config[:ipn_secret]))
7
+ logger.info "PayPal_Website_Standard: attempt to send an IPN with invalid secret"
8
+ raise Exception
9
+ end
10
+
6
11
  @order = Order.find_by_number(params[:invoice])
7
12
  PaymentNotification.create!(:params => params,
8
13
  :order_id => @order.id,
9
14
  :status => params[:payment_status],
10
15
  :transaction_id => params[:txn_id])
16
+ logger.info "PayPal_Website_Standard: processing payment notification for invoice #{params["invoice"]}, amount is #{params["mc_gross"]} #{params["mc_currency"]}"
11
17
 
12
18
  # this logging stuff won't live here for long...
13
19
 
@@ -17,21 +23,29 @@ class PaymentNotificationsController < ApplicationController
17
23
 
18
24
  #create payment for this order
19
25
  payment = Payment.new
20
- payment.amount = order.total
26
+
27
+ # 1. Assume that if payment notification comes, it's exactly for the amount
28
+ # sent to paypal (safe assumption -- cart can't be edited while on paypal)
29
+ # 2. Can't use Order#total, as it's intercepted by spree-multi-currency
30
+ # which might lead to lots of false "credit owed" payment states
31
+ # (when they should be "complete")
32
+ payment.amount = order.read_attribute(:total)
33
+ logger.info "PayPal_Website_Standard: set payment.amount to #{payment.amount} based on order's total #{order.read_attribute(:total)}"
34
+
21
35
  payment.payment_method = Order.paypal_payment_method
22
36
  order.payments << payment
23
37
  payment.started_processing
24
38
 
25
39
  order.payment.complete
26
- logger.info("order #{order.number} (#{order.id}) -- completed payment")
40
+ logger.info("PayPal_Website_Standard: order #{order.number} (#{order.id}) -- completed payment")
27
41
  while order.state != "complete"
28
42
  order.next
29
- logger.info("advanced state of Order #{order.number} (#{order.id}). current state #{order.state}. thread #{Thread.current.to_s}. issuing callback")
43
+ logger.info("PayPal_Website_Standard: advanced state of Order #{order.number} (#{order.id}). current state #{order.state}. thread #{Thread.current.to_s}. issuing callback")
30
44
  state_callback(:after) # that line will run all _not run before_ callbacks
31
45
  end
32
46
  order.update_totals
33
47
  order.update!
34
- logger.info("Order #{order.number} (#{order.id}) updated successfully, IPN complete")
48
+ logger.info("PayPal_Website_Standard: Order #{order.number} (#{order.id}) updated successfully, IPN complete")
35
49
  end
36
50
 
37
51
  render :nothing => true
@@ -1,6 +1,11 @@
1
1
  Order.class_eval do
2
2
  has_many :payment_notifications
3
3
 
4
+ # SSL certificates for encrypting paypal link
5
+ PAYPAL_CERT_PEM = "#{Rails.root}/certs/paypal_cert_#{Rails.env}.pem"
6
+ APP_CERT_PEM = "#{Rails.root}/certs/app_cert.pem"
7
+ APP_KEY_PEM = "#{Rails.root}/certs/app_key.pem"
8
+
4
9
  def shipment_cost
5
10
  adjustment_total - credit_total
6
11
  end
@@ -12,4 +17,50 @@ Order.class_eval do
12
17
  def self.paypal_payment_method
13
18
  PaymentMethod.select{ |pm| pm.name.downcase =~ /paypal/}.first
14
19
  end
20
+
21
+ def self.use_encrypted_paypal_link?
22
+ Spree::Paypal::Config[:encryption] &&
23
+ Spree::Paypal::Config[:ipn_secret] &&
24
+ Spree::Paypal::Config[:cert_id] &&
25
+ File.exist?(PAYPAL_CERT_PEM) &&
26
+ File.exist?(APP_CERT_PEM) &&
27
+ File.exist?(APP_KEY_PEM)
28
+ end
29
+
30
+ def paypal_encrypted(payment_notifications_url, options = {})
31
+ values = {
32
+ :business => Spree::Paypal::Config[:account],
33
+ :invoice => self.number,
34
+ :cmd => '_cart',
35
+ :upload => 1,
36
+ :currency_code => options[:currency_code] || Spree::Paypal::Config[:currency_code],
37
+ :handling_cart => self.ship_total,
38
+ :return => Spree::Paypal::Config[:success_url],
39
+ :notify_url => payment_notifications_url,
40
+ :charset => "utf-8",
41
+ :cert_id => Spree::Paypal::Config[:cert_id],
42
+ :page_style => Spree::Paypal::Config[:page_style],
43
+ :tax_cart => self.tax_total
44
+ }
45
+
46
+ self.line_items.each_with_index do |item, index|
47
+ values.merge!({
48
+ "amount_#{index + 1}" => item.price,
49
+ "item_name_#{index + 1}" => item.variant.product.name,
50
+ "item_number_#{index + 1}" => item.variant.product.id,
51
+ "quantity_#{index + 1}" => item.quantity
52
+ })
53
+ end
54
+
55
+ encrypt_for_paypal(values)
56
+ end
57
+
58
+ def encrypt_for_paypal(values)
59
+ paypal_cert = File.read(PAYPAL_CERT_PEM)
60
+ app_cert = File.read(APP_CERT_PEM)
61
+ app_key = File.read(APP_KEY_PEM)
62
+ signed = OpenSSL::PKCS7::sign(OpenSSL::X509::Certificate.new(app_cert), OpenSSL::PKey::RSA.new(app_key, ''), values.map { |k, v| "#{k}=#{v}" }.join("\n"), [], OpenSSL::PKCS7::BINARY)
63
+ OpenSSL::PKCS7::encrypt([OpenSSL::X509::Certificate.new(paypal_cert)], signed.to_der, OpenSSL::Cipher::Cipher::new("DES3"), OpenSSL::PKCS7::BINARY).to_s.gsub("\n", "")
64
+ end
65
+
15
66
  end
@@ -6,44 +6,51 @@
6
6
  %>
7
7
  <%= form_tag submit_url do %>
8
8
 
9
- <input id="business" name="business" type="hidden" value="<%= Spree::Paypal::Config[:account] %>" />
10
- <input id="invoice" name="invoice" type="hidden" value="<%= @order.number %>" />
9
+ <%- if(Order.use_encrypted_paypal_link?) %>
10
+ <%= hidden_field_tag(:cmd, "_s-xclick") %>
11
+ <%= hidden_field_tag(:encrypted, @order.paypal_encrypted(payment_notifications_url(:secret => Spree::Paypal::Config[:ipn_secret]))) %>
12
+ <% else %>
11
13
 
12
- <% @order.line_items.each_with_index do |item, index| %>
13
- <%- if item.variant.respond_to?(:paypal_name) %>
14
- <input id="item_name_<%= index + 1 %>" name="item_name_<%= index + 1 %>" type="hidden" value="<%= item.variant.paypal_name %>" />
15
- <% else %>
16
- <input id="item_name_<%= index + 1 %>" name="item_name_<%= index + 1 %>" type="hidden" value="<%= item.variant.product.name %>" />
14
+ <input id="page_style" name="page_style" type="hidden" value="<%= Spree::Paypal::Config[:page_style] %>"/>
15
+ <input id="business" name="business" type="hidden" value="<%= Spree::Paypal::Config[:account] %>" />
16
+ <input id="invoice" name="invoice" type="hidden" value="<%= @order.number %>" />
17
+
18
+ <% @order.line_items.each_with_index do |item, index| %>
19
+ <%- if item.variant.respond_to?(:paypal_name) %>
20
+ <input id="item_name_<%= index + 1 %>" name="item_name_<%= index + 1 %>" type="hidden" value="<%= item.variant.paypal_name %>" />
21
+ <% else %>
22
+ <input id="item_name_<%= index + 1 %>" name="item_name_<%= index + 1 %>" type="hidden" value="<%= item.variant.product.name %>" />
23
+ <% end %>
24
+ <input id="amount_<%= index + 1 %>" name="amount_<%= index + 1 %>" type="hidden" value="<%= item.price %>" />
25
+ <input id="quantity_<%= index + 1 %>" name="quantity_<%= index + 1 %>" type="hidden" value="<%= item.quantity %>" />
26
+ <% end %>
27
+
28
+ <input id="currency_code" name="currency_code" type="hidden" value="<%= Spree::Paypal::Config[:currency_code] %>" />
29
+ <input id="handling_cart" name="handling_cart" type="hidden" value="<%= @order.ship_total %>" />
30
+ <input id="tax_cart" name="tax_cart" type="hidden" value="<%= @order.tax_total %>" />
31
+
32
+
33
+ <input id="cmd" name="cmd" type="hidden" value="_cart" />
34
+ <input type="hidden" name="upload" value="1" />
35
+ <input id="notify_url" name="notify_url" type="hidden" value="<%= payment_notifications_url %>" />
36
+ <input type="hidden" name="rm" value ="2" />
37
+ <input id="return" name="return" type="hidden" value="<%= Spree::Paypal::Config[:success_url] %>" />
38
+
39
+ <input id="charset" name="charset" type="hidden" value="utf-8" />
40
+
41
+ <% if(Spree::Paypal::Config[:populate_address] && @order.bill_address) %>
42
+ <input type="hidden" name="address1" id="address1" value="<%= @order.bill_address.address1 %>" />
43
+ <input type="hidden" name="address2" id="address2" value="<%= @order.bill_address.address2 %>" />
44
+ <input type="hidden" name="city" id="city" value="<%= @order.bill_address.city %>" />
45
+ <input type="hidden" name="country" id="country" value="<%= @order.bill_address.country.iso %>" />
46
+ <input type="hidden" name="email" id="email" value="<%= @order.email %>" />
47
+ <input type="hidden" name="first_name" id="first_name" value="<%= @order.bill_address.firstname %>" />
48
+ <input type="hidden" name="last_name" id="last_name" value="<%= @order.bill_address.lastname %>" />
49
+ <input type="hidden" name="zip" id="zip" value="<%= @order.bill_address.zipcode %>" />
50
+ <input type="hidden" name="night_phone_b" id="night_phone_b" value="<%= @order.bill_address.phone %>" />
17
51
  <% end %>
18
- <input id="amount_<%= index + 1 %>" name="amount_<%= index + 1 %>" type="hidden" value="<%= item.price %>" />
19
- <input id="quantity_<%= index + 1 %>" name="quantity_<%= index + 1 %>" type="hidden" value="<%= item.quantity %>" />
20
52
  <% end %>
21
53
 
22
- <input id="currency_code" name="currency_code" type="hidden" value="<%= Spree::Paypal::Config[:currency_code] %>" />
23
- <input id="handling_cart" name="handling_cart" type="hidden" value="<%= @order.ship_total %>" />
24
- <input id="tax_cart" name="tax_cart" type="hidden" value="<%= @order.tax_total %>" />
25
-
26
-
27
- <input id="cmd" name="cmd" type="hidden" value="_cart" />
28
- <input type="hidden" name="upload" value="1" />
29
- <input id="notify_url" name="notify_url" type="hidden" value="<%= payment_notifications_url %>" />
30
- <input type="hidden" name="rm" value ="2" />
31
- <input id="return" name="return" type="hidden" value="<%= Spree::Paypal::Config[:success_url] %>" />
32
-
33
- <input id="charset" name="charset" type="hidden" value="utf-8" />
34
-
35
- <% if(Spree::Paypal::Config[:populate_address] && @order.bill_address) %>
36
- <input type="hidden" name="address1" id="address1" value="<%= @order.bill_address.address1 %>" />
37
- <input type="hidden" name="address2" id="address2" value="<%= @order.bill_address.address2 %>" />
38
- <input type="hidden" name="city" id="city" value="<%= @order.bill_address.city %>" />
39
- <input type="hidden" name="country" id="country" value="<%= @order.bill_address.country.iso %>" />
40
- <input type="hidden" name="email" id="email" value="<%= @order.email %>" />
41
- <input type="hidden" name="first_name" id="first_name" value="<%= @order.bill_address.firstname %>" />
42
- <input type="hidden" name="last_name" id="last_name" value="<%= @order.bill_address.lastname %>" />
43
- <input type="hidden" name="zip" id="zip" value="<%= @order.bill_address.zipcode %>" />
44
- <input type="hidden" name="night_phone_b" id="night_phone_b" value="<%= @order.bill_address.phone %>" />
45
- <% end %>
46
-
47
54
  <%= image_submit_tag "pp_checkout.gif" %>
48
55
 
49
56
  <% end -%>
@@ -10,7 +10,13 @@ class PaypalConfiguration < Configuration
10
10
 
11
11
  # this stuff is really handy
12
12
  preference :currency_code, :string, :default => "EUR"
13
+ preference :page_style, :string, :default => 'PayPal'
13
14
 
15
+ # encryption / security
16
+ preference :encrypted, :boolean, :default => false
17
+ preference :cert_id, :string, :default => "12345678"
18
+ preference :ipn_secret, :string, :default => "secret"
19
+
14
20
  validates_presence_of :name
15
21
  validates_uniqueness_of :name
16
- end
22
+ end
@@ -1,7 +1,7 @@
1
1
  require 'spree_core'
2
- require 'pp_website_standard_hooks'
2
+ require 'spree_paypal_website_standard_hooks'
3
3
 
4
- module PpWebsiteStandard
4
+ module SpreePaypalWebsiteStandard
5
5
  class Engine < Rails::Engine
6
6
 
7
7
  config.autoload_paths += %W(#{config.root}/lib)
@@ -1,4 +1,4 @@
1
- class PpWebsiteStandardHooks < Spree::ThemeSupport::HookListener
1
+ class SpreePaypalWebsiteStandardHooks < Spree::ThemeSupport::HookListener
2
2
  # custom hooks go here
3
3
  # insert_before :checkout_summary_box, 'checkout/paypal_checkout'
4
4
  end
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_paypal_website_standard
3
3
  version: !ruby/object:Gem::Version
4
- hash: 59
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
7
  - 8
9
- - 2
10
- version: 0.8.2
8
+ - 3
9
+ version: 0.8.3
11
10
  platform: ruby
12
11
  authors:
13
12
  - Gregg Pollack, Sean Schofield, Tomasz Stachewicz
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2011-04-29 00:00:00 +02:00
17
+ date: 2011-10-10 00:00:00 +02:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -26,16 +25,15 @@ dependencies:
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
29
- hash: 103
30
28
  segments:
31
29
  - 0
32
- - 30
30
+ - 50
33
31
  - 0
34
- version: 0.30.0
32
+ version: 0.50.0
35
33
  type: :runtime
36
34
  version_requirements: *id001
37
- description:
38
- email:
35
+ description: Spree extension for integration with PayPal Website Standard payment
36
+ email: tomekrs@o2.pl
39
37
  executables: []
40
38
 
41
39
  extensions: []
@@ -46,9 +44,9 @@ files:
46
44
  - README.md
47
45
  - LICENSE
48
46
  - lib/spree/paypal/config.rb
49
- - lib/pp_website_standard.rb
47
+ - lib/spree_paypal_website_standard.rb
50
48
  - lib/paypal_configuration.rb
51
- - lib/pp_website_standard_hooks.rb
49
+ - lib/spree_paypal_website_standard_hooks.rb
52
50
  - lib/tasks/pp_website_standard.rake
53
51
  - lib/tasks/install.rake
54
52
  - app/controllers/checkout_controller_decorator.rb
@@ -74,7 +72,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
74
72
  requirements:
75
73
  - - ">="
76
74
  - !ruby/object:Gem::Version
77
- hash: 57
78
75
  segments:
79
76
  - 1
80
77
  - 8
@@ -85,7 +82,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
82
  requirements:
86
83
  - - ">="
87
84
  - !ruby/object:Gem::Version
88
- hash: 3
89
85
  segments:
90
86
  - 0
91
87
  version: "0"