spree_paypal_api_checkout 0.0.8 → 0.1.1

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.
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