spree_paypal_api_checkout 0.0.8 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a5a5201c741e77e835868a6fda79d71b658c1b58fabca8ef39d3990199294f2
4
- data.tar.gz: d9b4e0c18fef43f234f07ed9bfc011811fbd87b49751cb524dfba3f189c428df
3
+ metadata.gz: bd7531190eafee3bb5fe40ac3378269b2e9265e4c66773cf2a447f0c1cef757f
4
+ data.tar.gz: b62014828506b097f6287cfa41ffde2e4638f29e5be241f9746aa28ea23e4e24
5
5
  SHA512:
6
- metadata.gz: fb54727dee1cef2a75bdf4527b6ba3c33d2dad98cb14a85ec36099be4c7f3dd9c0ed80918fd4a794330d47055c069113b20fc01fb8b62c0df73cfe2988dc4e33
7
- data.tar.gz: 9cf9906841e3359b96fae7f8581dcea5c24026035c7a02e46a7351efc9c1b17d8bb9a3c1f029330b07f7f4f89cb5dae9be5c04d14f034cd2335282e31fabfe45
6
+ metadata.gz: beaf4e7e206f97393da179553fe15e6548734fffcb62dce78b3b6bdb6dc79d344b83bc8f332900525e586d0575fc0b367cfb21e99781c995d5ec14f07496546d
7
+ data.tar.gz: da8a449dd52eeb1d1c864b65db9f1bc84e5be991316d7578f90657f1a3eaba7bb384ca9fd21a48165fc997b18b8a09c8fad748e6be4b2b864069d078e3240c2d
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  1. Add this extension to your Gemfile with this line:
6
6
 
7
- gem 'spree_paypal_api_checkout', '~> 0.0.8'
7
+ gem 'spree_paypal_api_checkout', '~> 0.1.1'
8
8
 
9
9
  2. Install the gem using Bundler:
10
10
 
@@ -1,6 +1,7 @@
1
1
  module Spree
2
2
  class PaypalCheckoutController < StoreController
3
3
  skip_before_action :verify_authenticity_token
4
+
4
5
  def express
5
6
  order = current_order || raise(ActiveRecord::RecordNotFound)
6
7
  items = order.line_items.map(&method(:line_item))
@@ -8,46 +9,43 @@ module Spree
8
9
  additional_adjustments = order.all_adjustments.additional
9
10
  tax_adjustments = additional_adjustments.tax
10
11
  shipping_adjustments = additional_adjustments.shipping
12
+ promotion_adjustments = additional_adjustments.promotion
11
13
 
12
14
  additional_adjustments.eligible.each do |adjustment|
13
15
  # Because PayPal doesn't accept $0 items at all. See #10
14
16
  # https://cms.paypal.com/uk/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_ECCustomizing
15
17
  # "It can be a positive or negative value but not zero."
16
18
  next if adjustment.amount.zero?
17
- next if tax_adjustments.include?(adjustment) || shipping_adjustments.include?(adjustment)
19
+ next if tax_adjustments.include?(adjustment) || shipping_adjustments.include?(adjustment) || promotion_adjustments.include?(adjustment)
18
20
 
19
21
  items << {
20
22
  name: adjustment.label,
21
23
  quantity: 1,
22
- amount: {
24
+ unit_amount: {
23
25
  currency_code: order.currency,
24
26
  value: adjustment.amount
25
27
  }
26
28
  }
27
29
  end
28
30
 
29
- pp_response = provider.create_order(order, express_checkout_request_details(order, items))
31
+ details = express_checkout_request_details(order: order, items: items, tax_adjustments: tax_adjustments, promotion_adjustments: promotion_adjustments)
32
+ pp_response = provider.parse_response(provider.create_order(order, details))
30
33
 
31
34
  render json: pp_response
32
35
  end
33
36
 
34
37
  def confirm
35
38
  order = current_order || raise(ActiveRecord::RecordNotFound)
36
- response = provider.capture_payment(params[:number])
37
- order.payments.create!({
38
- source: Spree::PaypalApiCheckout.create({
39
- token: response['id'],
40
- payer_id: response['payer']['payer_id']
41
- }),
42
- amount: order.total,
43
- payment_method: payment_method
44
- })
39
+
40
+ response = provider.set_payment_records(order, params[:number], payment_method)
41
+
45
42
  render json: response
46
43
  end
47
44
 
48
45
  def proceed
49
46
  order = current_order || raise(ActiveRecord::RecordNotFound)
50
47
  order.payments.last.source.update(transaction_id: params[:transaction_id])
48
+ order.payments.last.update(response_code: params[:transaction_id])
51
49
  order.next
52
50
  path = checkout_state_path(order.state)
53
51
  if order.complete?
@@ -70,31 +68,68 @@ module Spree
70
68
  def line_item(item)
71
69
  {
72
70
  name: item.product.name,
73
- number: item.variant.sku,
71
+ sku: item.variant.sku,
74
72
  quantity: item.quantity,
75
- amount: {
73
+ description: item.product.meta_description,
74
+ unit_amount: {
76
75
  currency_code: item.order.currency,
77
76
  value: item.price
78
77
  },
79
- ItemCategory: "Physical"
78
+ item_category: "Physical"
80
79
  }
81
80
  end
82
81
 
83
- def express_checkout_request_details order, items
82
+ def express_checkout_request_details order:, items:, tax_adjustments:, promotion_adjustments:
84
83
  {
85
84
  intent: 'CAPTURE',
86
85
  purchase_units: [
87
86
  {
88
87
  amount: {
89
88
  currency_code: current_order.currency,
90
- value: order.total
89
+ value: order.total,
90
+ breakdown: {
91
+ item_total: {
92
+ currency_code: current_order.currency,
93
+ value: items.sum{|r| (r[:unit_amount][:value] * r[:quantity]) }
94
+ },
95
+ shipping: {
96
+ currency_code: current_order.currency,
97
+ value: current_order.shipments.sum(:cost)
98
+ },
99
+ tax_total: {
100
+ currency_code: current_order.currency,
101
+ value: tax_adjustments.sum(:amount)
102
+ },
103
+ discount: {
104
+ currency_code: current_order.currency,
105
+ value: promotion_adjustments.sum(:amount).abs
106
+ }
107
+ }
91
108
  },
92
- item: items
109
+ items: items,
110
+ shipping: address_options
93
111
  },
94
112
  ]
95
113
  }
96
114
  end
97
115
 
116
+ def address_options
117
+ address = current_order.ship_address
118
+ {
119
+ name: { full_name: address.try(:full_name) },
120
+ address: {
121
+ address_line_1: address.address1,
122
+ address_line_2: address.address2,
123
+ # phone: address.phone,
124
+ admin_area_1: address.state_text,
125
+ admin_area_2: address.city,
126
+ country_code: address.country.iso,
127
+ postal_code: address.zipcode
128
+ },
129
+ type: 'SHIPPING'
130
+ }
131
+ end
132
+
98
133
  def payment_method
99
134
  Spree::PaymentMethod.find(params[:payment_method_id])
100
135
  end
@@ -20,75 +20,25 @@ module Spree
20
20
  'paypal_checkout'
21
21
  end
22
22
 
23
- def generate_client_token
24
- uri = URI.parse("https://#{preferred_server}/v1/identity/generate-token")
25
- request = Net::HTTP::Post.new(uri)
26
- request['Authorization'] = "Bearer #{generate_access_token}"
27
- request.content_type = "application/json"
28
-
29
- req_options = { use_ssl: uri.scheme == "https" }
30
-
31
- response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
32
- response = http.request(request)
33
- end
34
- return response
35
- end
36
-
37
23
  def generate_access_token
38
- uri = URI.parse("https://#{preferred_server}/v1/oauth2/token")
39
- request = Net::HTTP::Post.new(uri)
40
- request.basic_auth("#{preferred_api_key}", "#{preferred_secret_key}")
41
- request.content_type = "application/json"
42
- request.body = "grant_type=client_credentials"
43
-
44
- req_options = { use_ssl: uri.scheme == "https" }
24
+ response = post_response_without_token(api_url('v1/oauth2/token'))
25
+ return response['access_token']
26
+ end
45
27
 
46
- response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
47
- response = http.request(request)
48
- end
49
- return JSON.parse(response.read_body)['access_token']
28
+ def generate_client_token
29
+ post_response(api_url('v1/identity/generate-token'))
50
30
  end
51
31
 
52
32
  def create_order order, body
53
- uri = URI.parse("https://#{preferred_server}/v2/checkout/orders")
54
- request = Net::HTTP::Post.new(uri)
55
- request['Authorization'] = "Bearer #{generate_access_token}"
56
- request.content_type = "application/json"
57
-
58
- request.body = body.to_json
59
- req_options = { use_ssl: uri.scheme == "https" }
60
-
61
- response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
62
- response = http.request(request)
63
- end
64
- return JSON.parse(response.read_body)
33
+ post_response(api_url('v2/checkout/orders'), body)
65
34
  end
66
35
 
67
36
  def capture_payment order_id
68
- uri = URI.parse("https://#{preferred_server}/v2/checkout/orders/#{order_id}/capture")
69
- request = Net::HTTP::Post.new(uri)
70
- request['Authorization'] = "Bearer #{generate_access_token}"
71
- request.content_type = "application/json"
72
- req_options = { use_ssl: uri.scheme == "https" }
73
-
74
- response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
75
- response = http.request(request)
76
- end
77
- return JSON.parse(response.read_body)
37
+ post_response(api_url("v2/checkout/orders/#{order_id}/capture"))
78
38
  end
79
39
 
80
40
  def refund_payment id, body
81
- uri = URI.parse("https://#{preferred_server}/v2/payments/captures/#{id}/refund")
82
- request = Net::HTTP::Post.new(uri)
83
- request['Authorization'] = "Bearer #{generate_access_token}"
84
- request.body = body.to_json
85
- request.content_type = "application/json"
86
- req_options = { use_ssl: uri.scheme == "https" }
87
-
88
- response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
89
- response = http.request(request)
90
- end
91
- return JSON.parse(response.read_body)
41
+ post_response(api_url("v2/payments/captures/#{id}/refund"), body)
92
42
  end
93
43
 
94
44
  def purchase(amount, express_checkout, gateway_options={})
@@ -114,8 +64,10 @@ module Spree
114
64
  refund_type: refund_type,
115
65
  refund_source: 'any'
116
66
  }
117
- refund_transaction_response = refund_payment(payment.source.transaction_id, refund_transaction)
118
- if refund_transaction_response['status']=='COMPLETED'
67
+ refund_entry = refund_payment(payment.source.transaction_id, refund_transaction)
68
+ refund_transaction_response = parse_response(refund_entry)
69
+
70
+ if success_response?(refund_transaction_response)
119
71
  payment.source.update({
120
72
  :refunded_at => Time.now,
121
73
  :refund_transaction_id => refund_transaction_response['id'],
@@ -123,7 +75,7 @@ module Spree
123
75
  :refund_type => refund_type
124
76
  })
125
77
 
126
- payment.class.create!(
78
+ refund_payment = payment.class.create!(
127
79
  :order => payment.order,
128
80
  :source => payment,
129
81
  :payment_method => payment.payment_method,
@@ -131,9 +83,93 @@ module Spree
131
83
  :response_code => refund_transaction_response['id'],
132
84
  :state => 'completed'
133
85
  )
86
+ refund_payment.log_entries.create!(details: refund_entry.to_yaml)
134
87
  end
135
88
  refund_transaction_response
136
89
  end
90
+
91
+ def post_response uri, body={}
92
+ request = Net::HTTP::Post.new(uri)
93
+ request['Authorization'] = "Bearer #{generate_access_token}"
94
+ request.body = body.to_json if body.present?
95
+
96
+ return hit_api(request: request, body: body, uri: uri)
97
+ end
98
+
99
+ def post_response_without_token uri, body={}
100
+ request = Net::HTTP::Post.new(uri)
101
+ request.basic_auth("#{preferred_api_key}", "#{preferred_secret_key}")
102
+ request.body = 'grant_type=client_credentials'
103
+
104
+ return parse_response(hit_api(request: request, body: body, uri: uri))
105
+ end
106
+
107
+ def hit_api request:, body:, uri:
108
+ request.content_type = 'application/json'
109
+ req_options = { use_ssl: uri.scheme == 'https' }
110
+
111
+ response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
112
+ response = http.request(request)
113
+ end
114
+ response
115
+ end
116
+
117
+ def parse_response response
118
+ JSON.parse(response.read_body) rescue response
119
+ end
120
+
121
+ def api_url url
122
+ URI.parse("https://#{preferred_server}/#{url}")
123
+ end
124
+
125
+ def set_payment_records order, number, payment_method
126
+ raw_response = response = nil
127
+ success = false
128
+ begin
129
+ payment_entry = capture_payment(number)
130
+ response = parse_response(payment_entry)
131
+ success = success_response?(response)
132
+
133
+ payment = order.payments.create!({
134
+ source: Spree::PaypalApiCheckout.create({
135
+ token: response['id'],
136
+ payer_id: response['payer']['payer_id']
137
+ }),
138
+ amount: order.total,
139
+ payment_method: payment_method
140
+ })
141
+ rescue Exception => e
142
+ raw_response = e.response.body
143
+ response = response_error(raw_response)
144
+ rescue JSON::ParserError
145
+ response = json_error(raw_response)
146
+ end
147
+
148
+ payment.log_entries.create!(details: payment_entry.to_yaml)
149
+ response
150
+ end
151
+
152
+ def response_error(raw_response)
153
+ begin
154
+ parse(raw_response)
155
+ rescue JSON::ParserError
156
+ json_error(raw_response)
157
+ end
158
+ end
159
+
160
+ def json_error(raw_response)
161
+ msg = 'Invalid response. Please contact team if you continue to receive this message.'
162
+ msg += " (The raw response returned by the API was #{raw_response.inspect})"
163
+ {
164
+ "error" => {
165
+ "message" => msg
166
+ }
167
+ }
168
+ end
169
+
170
+ def success_response? response
171
+ response.key?('status') && response.key?('id') && (response['status'] == 'COMPLETED')
172
+ end
137
173
  end
138
174
  end
139
175
 
@@ -1,5 +1,5 @@
1
1
  <div id="paypal-button-container" class="paypal-button-container"></div>
2
- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
2
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
3
3
  <script type="text/javascript" src="https://www.paypal.com/sdk/js?client-id=<%= payment_method.preferred_api_key %>&currency=<%= @order.currency %>"></script>
4
4
 
5
5
  <script type="text/javascript">
@@ -15,6 +15,7 @@ de:
15
15
  refund_unsuccessful: "PayPal Erstattung nicht erfolgreich"
16
16
  actions:
17
17
  refund: "Erstatten"
18
+ status: 'Toestand'
18
19
  flash:
19
20
  cancel: "Sie wollen PayPal doch nicht benutzen? Kein Problem."
20
21
  connection_failed: "Verbindung zu Paypal nicht erfolgreich."
@@ -15,6 +15,7 @@ en:
15
15
  refund_unsuccessful: "PayPal refund unsuccessful"
16
16
  actions:
17
17
  refund: "Refund"
18
+ status: Status
18
19
  flash:
19
20
  cancel: "Don't want to use PayPal? No problems."
20
21
  connection_failed: "Could not connect to PayPal."
@@ -15,6 +15,7 @@ es:
15
15
  refund_unsuccessful: "No se pudo rembolsar el pago de PayPal"
16
16
  actions:
17
17
  refund: "Reembolso"
18
+ status: Estado
18
19
  flash:
19
20
  cancel: "No quieres usar PayPal? No hay problema."
20
21
  connection_failed: "No se pudo conectar con PayPal."
@@ -15,6 +15,7 @@ it:
15
15
  refund_unsuccessful: "Rimborso PayPal non completato"
16
16
  actions:
17
17
  refund: "Rimborso"
18
+ status: Stato
18
19
  flash:
19
20
  cancel: "Non vuoi usare PayPal? Nessun problema."
20
21
  connection_failed: "Impossibile connettersi a PayPal."
@@ -15,6 +15,7 @@ pl:
15
15
  refund_unsuccessful: "Błąd podczas wykonywania zwrotu PayPal"
16
16
  actions:
17
17
  refund: "Zwróć Płatność"
18
+ status: Status
18
19
  flash:
19
20
  cancel: "Nie chcesz użyć PayPal? Nie ma problemu."
20
21
  connection_failed: "Nie można było połączyć się z usługą PayPal."
@@ -15,6 +15,7 @@ pt:
15
15
  refund_unsuccessful: "Não foi possível reembolsar o pagamento de PayPal"
16
16
  actions:
17
17
  refund: "Reembolso"
18
+ status: Status
18
19
  flash:
19
20
  cancel: "Não pretende usar PayPal? Não há problema."
20
21
  connection_failed: "Não foi possível estabelecer ligação com o PayPal."
@@ -1,3 +1,3 @@
1
1
  module SpreePaypalApiCheckout
2
- VERSION = '0.0.8'
2
+ VERSION = '0.1.1'
3
3
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "spree_paypal_api_checkout".freeze
3
- s.version = "0.0.8"
3
+ s.version = "0.1.1"
4
4
 
5
5
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
6
6
  s.require_paths = ["lib".freeze]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_paypal_api_checkout
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Spree Commerce
@@ -401,7 +401,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
401
401
  version: '0'
402
402
  requirements:
403
403
  - none
404
- rubygems_version: 3.2.15
404
+ rubygems_version: 3.0.3
405
405
  signing_key:
406
406
  specification_version: 4
407
407
  summary: Adds PayPal API Checkout as a Payment Method to Spree Commerce