spree_mollie_gateway 2.1.5 → 3.0.0.pre.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/.travis.yml +11 -3
  4. data/Appraisals +11 -0
  5. data/Gemfile +3 -3
  6. data/Rakefile +1 -1
  7. data/app/controllers/spree/api/v1/mollie_controller.rb +3 -4
  8. data/app/controllers/spree/api/v1/shipments_controller_decorator.rb +12 -0
  9. data/app/controllers/spree/checkout_controller_decorator.rb +2 -1
  10. data/app/controllers/spree/mollie_controller.rb +7 -4
  11. data/app/models/concerns/spree/mollie/money_formatter.rb +11 -0
  12. data/app/models/mollie/client_decorator.rb +1 -1
  13. data/app/models/spree/address_decorator.rb +16 -0
  14. data/app/models/spree/gateway/mollie_gateway.rb +98 -161
  15. data/app/models/spree/line_item_decorator.rb +25 -0
  16. data/app/models/spree/mollie/order.rb +59 -0
  17. data/app/models/spree/mollie/order_serializer.rb +172 -0
  18. data/app/models/spree/mollie/payment_state_updater.rb +68 -0
  19. data/app/models/spree/mollie_logger.rb +3 -2
  20. data/app/models/spree/mollie_payment_source.rb +38 -34
  21. data/app/models/spree/order_decorator.rb +23 -3
  22. data/app/models/spree/payment/gateway_options_decorator.rb +19 -0
  23. data/app/models/spree/payment/processing_decorator.rb +6 -6
  24. data/app/models/spree/payment_decorator.rb +9 -1
  25. data/app/models/spree/payment_method_decorator.rb +2 -2
  26. data/app/models/spree/shipment_decorator.rb +99 -0
  27. data/app/models/spree/user_decorator.rb +3 -1
  28. data/app/views/spree/api/v1/orders/complete.v1.rabl +1 -1
  29. data/app/views/spree/api/v1/orders/payment.v1.rabl +1 -1
  30. data/bin/console +3 -3
  31. data/config/routes.rb +1 -1
  32. data/db/migrate/20180301084841_add_mollie_customer_id_to_spree_user.rb +1 -0
  33. data/gemfiles/spree_3_1.gemfile +8 -0
  34. data/gemfiles/spree_3_1.gemfile.lock +389 -0
  35. data/gemfiles/spree_3_2.gemfile +8 -0
  36. data/gemfiles/spree_3_2.gemfile.lock +394 -0
  37. data/gemfiles/spree_3_5.gemfile +8 -0
  38. data/gemfiles/spree_3_5.gemfile.lock +394 -0
  39. data/gemfiles/spree_3_6.gemfile +8 -0
  40. data/gemfiles/spree_3_6.gemfile.lock +400 -0
  41. data/lib/generators/spree_mollie_gateway/install/install_generator.rb +2 -2
  42. data/lib/spree_mollie_gateway/engine.rb +6 -3
  43. data/lib/spree_mollie_gateway/factories.rb +1 -1
  44. data/lib/spree_mollie_gateway/version.rb +1 -1
  45. data/lib/spree_mollie_gateway.rb +0 -1
  46. data/spree_mollie_gateway.gemspec +17 -15
  47. metadata +86 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4270e54464d5cbac28a3aee6df366df3ce16ca8aa59283dccdee6eca59a1ffc3
4
- data.tar.gz: 31195d4f4b50d2636f9f4500523743f676a8eae2b43cc840371701ac8a433a70
3
+ metadata.gz: cf83a48a0236601f55589927af67ebc9b340aa7ff623996e6a7a568d1299b695
4
+ data.tar.gz: 97b9e8c49d143227aacb90e71cf7223f0b4843a5c3a635b37f363e7827f87134
5
5
  SHA512:
6
- metadata.gz: d8072d87439b0e1ddc28e7e43406f25441fb2e877222dc2235c37bf33c691e445087deb504eeb98113b0996cecb42013404c1b4a6a8294b35d04f7fc2c45cb50
7
- data.tar.gz: 395848075f6d9d54e1d318dd045556affbeaf59ef5ddd21d07d2a4ce7cb4930c9446de17b614d4f6fe225b304709253c21b3acb43657b1d4cc2345b53c72a631
6
+ metadata.gz: 3b8580923c7885ef0c7dc9487cad268167d52414812b7085f9983623e7fadbdc38efb227a810234c1c74ca3b8be7d3ae93375cefcfa66b93ca16e0a035ca22e2
7
+ data.tar.gz: 30b271c5e2b822f2ba9ca8f9cff3ef9fe0bae328956e49f372b4268d3ecf7cd0f979c74cb11779d2c08ad90be89c6b1673c7d05fcdc358e8ca998431961262f8
data/.rubocop.yml ADDED
@@ -0,0 +1 @@
1
+ require: rubocop-rspec
data/.travis.yml CHANGED
@@ -1,8 +1,15 @@
1
1
  sudo: false
2
+ dist: trusty
3
+
2
4
  language: ruby
5
+
3
6
  rvm:
4
- - 2.3.0
5
- before_install: gem install bundler -v 1.16.1
7
+ - 2.5.0
8
+
9
+ script:
10
+ - bundle exec rake test_app
11
+ - bundle exec rake spec
12
+
6
13
  deploy:
7
14
  provider: rubygems
8
15
  gem: spree_mollie_gateway
@@ -11,6 +18,7 @@ deploy:
11
18
  repo: mollie/spree-mollie-gateway
12
19
  api_key:
13
20
  secure: N3+LevY4HZd57cmwfIy6a+E/wX/jCD9Pnqq41/kr6I6iLIMz5xWoU0iDEZEXGhTWmId5O0zxTl2o3w4PiVkH4Onmufv3oLOONexrJeIF5cx/TdWpY36RycA2euVvOGVYMNWGIhslCern4zc8pOTwGpmm8DD8qPTQAV1JPXzbPEiXkxJX0KYm1Vb6v4GTW0q2ghvqY3fzgNaJKVQoEXVrOaXMd/dJDQRjBzbzfF8V2Z779kfOfV5PV/jqheq4bXQYMDBKQUHuTvRMNx0tF4mGgHANp1AXMS4GVbYNyAQNmyd3QnkJHSdKthTTe6nwQoH7oloY4Dbd89fG7Yx7I/kt2Aip3pHMts+4/oVN6QPqe0xe11TeC6yRhi/357I+DCzIk1aRGJ9lkpGQnM+oB54N0yw+htvsd7WE2ac57ixVG0ni39IQciQJ4p3aJzho8z3ui3CNj0p3Y4B4moEIJ3JV3Gg+TEXbzViu9G8j7YIlqw0OpGaaczavfuRSTYkE0QKA4BSqGOG+qsY8ArdvW3KgVohSxHzFR6ubhgT81JvzF6hpS7h7PalOUKA5cr7ha/dB0qL1PYNU6xHQFWiM/ILW5eF6ttQDgzA294U8FoRykG6T9swdVZA5Vd6DZafcu6ofqgyRFGQXzlhq/Hdkocb2W60l5BR4BSq4uoIvmMHXd+8=
21
+
14
22
  env:
15
23
  matrix:
16
- secure: ftw4Vv/ri+oxmuKcKX3MElYurqR/HHSuChyx8RM7ixfP9+l87jr772OGtvg0v0neW2CCz9ot+4SXL+dewFELTr6+8rstT0C1Lcyn7ANTpBp80bnMpHZrpJFkDUYne17r8kA+OUkW71OC1RrTTfa9mfSTd9g1wKSbfbKsQvZ1Q/mmwAAX7b1YyR0k83tJO23vr1Yx4GU7RNhuv+uTTNz0kuO4ezbv5WR1wjKQQsJwcc72WDsDyqZq5o9SVxA0fFe4666v1yheBZsX5gcWSugwtRQqD8fp1Era1T+11mJDk6S/4wZu/vRrOQCEkaFYdYtY3owVg8X3JVnAgtsiTDLt+s+tkZPTfTq56f3EqIEpbZQ6x906zQfGBosVBpWO1Bn+cKMGFrURWwr+19dOHU+jXYUfvV5EE12aJq6EV82IvveK2YhZ5+axP7JYkQDjwYJcuQd4ONQvagHf1YGeqLiGB5SuXEYBRv1521276inp2DOqUNjfquQwqWSy3Q2iQ/0yrAKcjYgYBk4yS8HkpaLsYd1EzgxW5IllJ+E040ViSddIQLHpDI9cmg9/uPFaVyHxWDMNOBMOAhq7znPAEtDfG6KnRmXswqkVfR7XM7ObIX8kdpGwc/cHGZwPD1595BHCcc0y5FkyRMY9KzTvj13yNdO61icIJa0PoQzfypaKuQQ=
24
+ secure: ftw4Vv/ri+oxmuKcKX3MElYurqR/HHSuChyx8RM7ixfP9+l87jr772OGtvg0v0neW2CCz9ot+4SXL+dewFELTr6+8rstT0C1Lcyn7ANTpBp80bnMpHZrpJFkDUYne17r8kA+OUkW71OC1RrTTfa9mfSTd9g1wKSbfbKsQvZ1Q/mmwAAX7b1YyR0k83tJO23vr1Yx4GU7RNhuv+uTTNz0kuO4ezbv5WR1wjKQQsJwcc72WDsDyqZq5o9SVxA0fFe4666v1yheBZsX5gcWSugwtRQqD8fp1Era1T+11mJDk6S/4wZu/vRrOQCEkaFYdYtY3owVg8X3JVnAgtsiTDLt+s+tkZPTfTq56f3EqIEpbZQ6x906zQfGBosVBpWO1Bn+cKMGFrURWwr+19dOHU+jXYUfvV5EE12aJq6EV82IvveK2YhZ5+axP7JYkQDjwYJcuQd4ONQvagHf1YGeqLiGB5SuXEYBRv1521276inp2DOqUNjfquQwqWSy3Q2iQ/0yrAKcjYgYBk4yS8HkpaLsYd1EzgxW5IllJ+E040ViSddIQLHpDI9cmg9/uPFaVyHxWDMNOBMOAhq7znPAEtDfG6KnRmXswqkVfR7XM7ObIX8kdpGwc/cHGZwPD1595BHCcc0y5FkyRMY9KzTvj13yNdO61icIJa0PoQzfypaKuQQ=
data/Appraisals ADDED
@@ -0,0 +1,11 @@
1
+ appraise 'spree-3-2' do
2
+ gem 'spree', '~> 3.2.0'
3
+ end
4
+
5
+ appraise 'spree-3-5' do
6
+ gem 'spree', '~> 3.5.0'
7
+ end
8
+
9
+ appraise 'spree-3-6' do
10
+ gem 'spree', '~> 3.6.1'
11
+ end
data/Gemfile CHANGED
@@ -1,8 +1,8 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
- gem 'mollie-api-ruby', '~> 4.1.1'
5
+ gem 'mollie-api-ruby', '~> 4.1.3'
6
6
 
7
7
  # Specify your gem's dependencies in spree_mollie_gateway.gemspec
8
8
  gemspec
data/Rakefile CHANGED
@@ -18,4 +18,4 @@ desc 'Generates a dummy app for testing'
18
18
  task :test_app do
19
19
  ENV['LIB_NAME'] = 'spree_mollie_gateway'
20
20
  Rake::Task['extension:test_app'].invoke
21
- end
21
+ end
@@ -4,16 +4,15 @@ module Spree
4
4
  class MollieController < BaseController
5
5
  def methods
6
6
  mollie = Spree::PaymentMethod.find_by_type 'Spree::Gateway::MollieGateway'
7
- payment_methods = mollie.available_methods(methods_params).map {
8
- |method| method.attributes
9
- }
7
+ payment_methods = mollie.available_methods(methods_params).map(&:attributes)
10
8
 
11
9
  render json: payment_methods
12
10
  end
13
11
 
14
12
  private
13
+
15
14
  def methods_params
16
- params.permit(:amount => [:currency, :value])
15
+ params.permit(amount: %i[currency value])
17
16
  end
18
17
  end
19
18
  end
@@ -0,0 +1,12 @@
1
+ Spree::Api::V1::ShipmentsController.class_eval do
2
+ def ship
3
+ unless @shipment.tracking.present?
4
+ # We should enforce entering tracking details, otherwise shipments cannot
5
+ # be created through Mollie.
6
+ unprocessable_entity('Please fill in your tracking details')
7
+ return
8
+ end
9
+ @shipment.ship! unless @shipment.shipped?
10
+ respond_with(@shipment, default_template: :show)
11
+ end
12
+ end
@@ -22,6 +22,7 @@ module Spree
22
22
 
23
23
  CheckoutController.class_eval do
24
24
  prepend CheckoutWithMollie
25
+
25
26
  private
26
27
 
27
28
  def payment_method_id_param
@@ -37,4 +38,4 @@ module Spree
37
38
  (params[:state] === 'payment') && params[:order][:payments_attributes]
38
39
  end
39
40
  end
40
- end
41
+ end
@@ -1,6 +1,6 @@
1
1
  module Spree
2
2
  class MollieController < BaseController
3
- skip_before_action :verify_authenticity_token, :only => [:update_payment_status]
3
+ skip_before_action :verify_authenticity_token, only: [:update_payment_status]
4
4
 
5
5
  # When the user is redirected from Mollie back to the shop, we can check the
6
6
  # mollie transaction status and set the Spree order state accordingly.
@@ -13,13 +13,16 @@ module Spree
13
13
 
14
14
  MollieLogger.debug("Redirect URL visited for order #{params[:order_number]}")
15
15
 
16
- redirect_to order.reload.paid? ? order_path(order) : checkout_state_path(:payment)
16
+ order = order.reload
17
+
18
+ # Order is paid for or authorized (e.g. Klarna Pay Later)
19
+ redirect_to order.paid? || payment.pending? ? order_path(order) : checkout_state_path(:payment)
17
20
  end
18
21
 
19
22
  # Mollie might send us information about a transaction through the webhook.
20
23
  # We should update the payment state accordingly.
21
24
  def update_payment_status
22
- MollieLogger.debug("Webhook called for payment #{params[:id]}")
25
+ MollieLogger.debug("Webhook called for Mollie order #{params[:id]}")
23
26
 
24
27
  payment = Spree::MolliePaymentSource.find_by_payment_id(params[:id]).payments.first
25
28
  mollie = Spree::PaymentMethod.find_by_type 'Spree::Gateway::MollieGateway'
@@ -35,4 +38,4 @@ module Spree
35
38
  payment_identifier.split '-'
36
39
  end
37
40
  end
38
- end
41
+ end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ module Mollie
3
+ module MoneyFormatter
4
+ include ActiveSupport::Concern
5
+
6
+ def format_money(money)
7
+ money.format(symbol: nil, thousands_separator: nil, decimal_mark: '.')
8
+ end
9
+ end
10
+ end
11
+ end
@@ -10,4 +10,4 @@ Mollie::Client.class_eval do
10
10
  add_version_string 'Ruby/' << RUBY_VERSION
11
11
  add_version_string OpenSSL::OPENSSL_VERSION.split(' ').slice(0, 2).join '/'
12
12
  end
13
- end
13
+ end
@@ -0,0 +1,16 @@
1
+ Spree::Address.class_eval do
2
+ def active_merchant_hash
3
+ {
4
+ name: full_name,
5
+ firstname: firstname,
6
+ lastname: lastname,
7
+ address1: address1,
8
+ address2: address2,
9
+ city: city,
10
+ state: state_text,
11
+ zip: zipcode,
12
+ country: country.try(:iso),
13
+ phone: phone
14
+ }
15
+ end
16
+ end
@@ -1,13 +1,18 @@
1
1
  module Spree
2
2
  class Gateway::MollieGateway < PaymentMethod
3
+ include ::Spree::Mollie::MoneyFormatter
4
+
3
5
  preference :api_key, :string
4
6
  preference :hostname, :string
7
+ # When set to true, Mollie will automatically charge all discounts and shipping
8
+ # fees after the first shipment.
9
+ preference :collect_shipping_costs_and_discounts_on_first_shipment, :boolean, default: true
5
10
 
6
11
  has_many :spree_mollie_payment_sources, class_name: 'Spree::MolliePaymentSource'
7
12
 
8
13
  # Only enable one-click payments if spree_auth_devise is installed
9
14
  def self.allow_one_click_payments?
10
- Gem.loaded_specs.has_key?('spree_auth_devise')
15
+ Gem.loaded_specs.key?('spree_auth_devise')
11
16
  end
12
17
 
13
18
  def payment_source_class
@@ -15,7 +20,7 @@ module Spree
15
20
  end
16
21
 
17
22
  def actions
18
- %w{credit}
23
+ %w[credit]
19
24
  end
20
25
 
21
26
  def provider_class
@@ -27,7 +32,7 @@ module Spree
27
32
  true
28
33
  end
29
34
 
30
- def available_for_order?(order)
35
+ def available_for_order?(_order)
31
36
  true
32
37
  end
33
38
 
@@ -43,217 +48,149 @@ module Spree
43
48
  payment_method.available_methods(options)
44
49
  else
45
50
  raise 'Unprocessable input'
46
- end.map do |method|
47
- method.attributes
48
- end
51
+ end.map(&:attributes)
49
52
  end
50
53
 
51
- # Create a new Mollie payment.
52
- def create_transaction(money, source, gateway_options)
54
+ # Create Mollie order
55
+ def process(money, source, gateway_options)
53
56
  MollieLogger.debug("About to create payment for order #{gateway_options[:order_id]}")
54
57
 
55
58
  begin
56
- mollie_payment = ::Mollie::Payment.create(
57
- prepare_payment_params(money, source, gateway_options)
58
- )
59
- MollieLogger.debug("Payment #{mollie_payment.id} created for order #{gateway_options[:order_id]}")
59
+ # First of all, invalidate all previous Mollie orders to prevent multiple paid orders
60
+ invalidate_previous_orders(gateway_options[:order].id)
60
61
 
61
- source.status = mollie_payment.status
62
- source.payment_id = mollie_payment.id
63
- source.payment_url = mollie_payment.checkout_url
62
+ # Create a new Mollie order and update the payment source
63
+ order_params = prepare_order_params(money, source, gateway_options)
64
+ mollie_order = ::Mollie::Order.create(order_params)
65
+ MollieLogger.debug("Mollie order #{mollie_order.id} created for Spree order #{gateway_options[:order_id]}")
66
+
67
+ source.status = mollie_order.status
68
+ source.payment_id = mollie_order.id
69
+ source.payment_url = mollie_order.checkout_url
64
70
  source.save!
65
- ActiveMerchant::Billing::Response.new(true, 'Payment created')
66
- rescue Mollie::Exception => e
71
+ ActiveMerchant::Billing::Response.new(true, 'Order created')
72
+ rescue ::Mollie::Exception => e
67
73
  MollieLogger.debug("Could not create payment for order #{gateway_options[:order_id]}: #{e.message}")
68
- ActiveMerchant::Billing::Response.new(false, "Payment could not be created: #{e.message}")
74
+ ActiveMerchant::Billing::Response.new(false, "Order could not be created: #{e.message}")
69
75
  end
70
76
  end
71
77
 
72
78
  # Create a Mollie customer which can be passed with a payment.
73
79
  # Required for one-click Mollie payments.
74
80
  def create_customer(user)
75
- customer = Mollie::Customer.create(
76
- email: user.email,
77
- api_key: get_preference(:api_key),
81
+ customer = ::Mollie::Customer.create(
82
+ email: user.email,
83
+ api_key: get_preference(:api_key)
78
84
  )
79
85
  MollieLogger.debug("Created a Mollie Customer for Spree user with ID #{customer.id}")
80
86
  customer
81
87
  end
82
88
 
83
- def prepare_payment_params(money, source, gateway_options)
84
- spree_routes = ::Spree::Core::Engine.routes.url_helpers
85
- order_number = gateway_options[:order_id]
86
- customer_id = gateway_options[:customer_id]
87
- currency = gateway_options[:currency]
88
-
89
- order_params = {
90
- amount: {
91
- value: format_money(money),
92
- currency: currency
93
- },
94
- description: "Spree Order: #{order_number}",
95
- redirectUrl: spree_routes.mollie_validate_payment_mollie_url(
96
- order_number: order_number,
97
- host: get_preference(:hostname)
98
- ),
99
- webhookUrl: spree_routes.mollie_update_payment_status_mollie_url(
100
- order_number: order_number,
101
- host: get_preference(:hostname)
102
- ),
103
- metadata: {
104
- order_id: order_number
105
- },
106
- api_key: get_preference(:api_key),
107
- }
108
-
109
- if source.try(:payment_method_name).present?
110
- order_params.merge! ({
111
- method: source.payment_method_name,
112
- })
113
- end
114
-
115
- if source.try(:issuer).present?
116
- order_params.merge! ({
117
- issuer: source.issuer
118
- })
119
- end
120
-
121
- if customer_id.present?
122
- if source.try(:payment_method_name).present?
123
- if source.payment_method_name.match(Regexp.union([::Mollie::Method::BITCOIN, ::Mollie::Method::BANKTRANSFER, ::Mollie::Method::GIFTCARD]))
124
- order_params.merge! ({
125
- billingEmail: gateway_options[:email]
126
- })
127
- end
128
- end
129
-
130
- if Spree::Gateway::MollieGateway.allow_one_click_payments?
131
- mollie_customer_id = Spree.user_class.find(customer_id).try(:mollie_customer_id)
132
-
133
- # Allow one-click payments by passing Mollie customer ID.
134
- if mollie_customer_id.present?
135
- order_params.merge! ({
136
- customerId: mollie_customer_id
137
- })
138
- end
139
- end
140
- end
141
-
142
- order_params
89
+ # Create a new Mollie refund
90
+ def credit(_credit_cents, _payment_id, _options)
91
+ ActiveMerchant::Billing::Response.new(false, 'Refunding Mollie orders is not yet supported in Spree. Please refund your order via Mollie Dashboard')
143
92
  end
144
93
 
145
- # Create a new Mollie refund
146
- def credit(credit_cents, payment_id, options)
147
- order = options[:originator].try(:payment).try(:order)
148
- order_number = order.try(:number)
149
- order_currency = order.try(:currency)
150
- MollieLogger.debug("Starting refund for order #{order_number}")
94
+ def authorize(*_args)
95
+ ActiveMerchant::Billing::Response.new(true, 'Mollie will automatically capture the amount after creating a shipment.')
96
+ end
151
97
 
152
- begin
153
- Mollie::Payment::Refund.create(
154
- payment_id: payment_id,
155
- amount: {
156
- value: format_money(order.display_total.money),
157
- currency: order_currency
158
- },
159
- description: "Refund Spree Order ID: #{order_number}",
160
- api_key: get_preference(:api_key)
161
- )
162
- MollieLogger.debug("Successfully refunded #{order.display_total} for order #{order_number}")
163
- ActiveMerchant::Billing::Response.new(true, 'Refund successful')
164
- rescue Mollie::Exception => e
165
- MollieLogger.debug("Refund failed for order #{order_number}: #{e.message}")
166
- ActiveMerchant::Billing::Response.new(false, 'Refund unsuccessful')
167
- end
98
+ def capture(*_args)
99
+ ActiveMerchant::Billing::Response.new(true, 'Mollie will automatically capture the amount after creating a shipment.')
168
100
  end
169
101
 
170
- def cancel(transaction_id)
171
- MollieLogger.debug("Starting cancelation for #{transaction_id}")
102
+ def cancel(mollie_order_id)
103
+ MollieLogger.debug("Starting cancellation for #{mollie_order_id}")
172
104
 
173
105
  begin
174
- mollie_payment = ::Mollie::Payment.get(
175
- transaction_id,
176
- api_key: get_preference(:api_key)
106
+ mollie_order = ::Mollie::Order.get(
107
+ mollie_order_id,
108
+ api_key: get_preference(:api_key)
177
109
  )
178
- if mollie_payment.cancelable?
179
- mollie_payment.delete(transaction_id)
180
- ActiveMerchant::Billing::Response.new(true, 'Mollie payment has been cancelled')
110
+ if mollie_order.cancelable?
111
+ cancel_order!(mollie_order_id)
112
+ ActiveMerchant::Billing::Response.new(true, 'Mollie order has been cancelled.')
181
113
  else
182
- MollieLogger.debug("Molie payment #{transaction_id} is not cancelable. Skipping any further updates.")
183
- ActiveMerchant::Billing::Response.new(true, 'Mollie payment has not been cancelled because it is not cancelable')
114
+ MollieLogger.debug("Spree order #{mollie_order_id} has been canceled, could not cancel Mollie order.")
115
+ ActiveMerchant::Billing::Response.new(true, 'Spree order has been canceled, could not cancel Mollie order.')
184
116
  end
185
- rescue Mollie::Exception => e
186
- MollieLogger.debug("Payment #{transaction_id} could not be canceled: #{e.message}")
187
- ActiveMerchant::Billing::Response.new(false, 'Payment cancellation unsuccessful')
117
+ rescue ::Mollie::Exception => e
118
+ MollieLogger.debug("Order #{mollie_order_id} could not be canceled: #{e.message}")
119
+ ActiveMerchant::Billing::Response.new(false, 'Order cancellation unsuccessful.')
188
120
  end
189
121
  end
190
122
 
191
123
  def available_methods(params = nil)
192
124
  method_params = {
193
- api_key: get_preference(:api_key),
194
- include: 'issuers',
125
+ api_key: get_preference(:api_key),
126
+ include: 'issuers',
127
+ resource: 'orders'
195
128
  }
196
129
 
197
- if params.present?
198
- method_params.merge! params
199
- end
130
+ method_params.merge! params if params.present?
200
131
 
201
132
  ::Mollie::Method.all(method_params)
202
133
  end
203
134
 
204
- def format_money(money)
205
- money.format(symbol: nil, thousands_separator: nil, decimal_mark: '.')
206
- end
207
-
208
135
  def available_methods_for_order(order)
209
136
  params = {
210
- amount: {
211
- currency: order.currency,
212
- value: format_money(order.display_total.money)
213
- }
137
+ amount: {
138
+ currency: order.currency,
139
+ value: format_money(order.display_total.money)
140
+ },
141
+ resource: 'orders',
142
+ billingCountry: order.billing_address.country.try(:iso)
214
143
  }
215
144
  available_methods(params)
216
145
  end
217
146
 
218
- def update_payment_status(payment)
219
- mollie_transaction_id = payment.source.payment_id
220
- mollie_payment = ::Mollie::Payment.get(
221
- mollie_transaction_id,
222
- api_key: get_preference(:api_key)
147
+ def update_payment_status(spree_payment)
148
+ mollie_order_id = spree_payment.source.payment_id
149
+ mollie_order = ::Mollie::Order.get(
150
+ mollie_order_id,
151
+ embed: 'payments',
152
+ api_key: get_preference(:api_key)
223
153
  )
224
154
 
225
- MollieLogger.debug("Checking Mollie payment status. Mollie payment has status #{mollie_payment.status}")
226
- update_by_mollie_status!(mollie_payment, payment)
155
+ MollieLogger.debug("Checking Mollie order status for order #{mollie_order_id}. Its status is: #{mollie_order.status}")
156
+ update_by_mollie_status!(mollie_order, spree_payment)
227
157
  end
228
158
 
229
- def update_by_mollie_status!(mollie_payment, payment)
230
- case mollie_payment.status
231
- when 'paid'
232
- # If Mollie payment is already paid and refunded amount is more than 0, don't update payment
233
- if mollie_payment.paid? && mollie_payment.amount_refunded.value > 0
234
- MollieLogger.debug('Payment is refunded. Not updating the payment status within Spree.')
235
- return
236
- end
159
+ def update_by_mollie_status!(mollie_order, spree_payment)
160
+ Spree::Mollie::PaymentStateUpdater.update(mollie_order, spree_payment)
161
+ end
237
162
 
238
- if payment.completed?
239
- MollieLogger.debug('Payment is already completed. Not updating the payment status within Spree.')
240
- return
241
- end
163
+ private
242
164
 
243
- # If order is already paid for, don't mark it as complete again.
244
- payment.complete!
245
- payment.order.finalize!
246
- payment.order.update_attributes(:state => 'complete', :completed_at => Time.now)
247
- MollieLogger.debug('Payment is paid and will transition to completed. Order will be finalized.')
248
- when 'canceled', 'expired', 'failed'
249
- payment.failure! unless payment.failed?
250
- payment.order.update_attributes(:state => 'payment', :completed_at => nil)
251
- else
252
- MollieLogger.debug('Unhandled Mollie payment state received. Therefore we did not update the payment state.')
253
- payment.order.update_attributes(state: 'payment', completed_at: nil)
254
- end
165
+ def prepare_order_params(money, source, gateway_options)
166
+ gateway_preferences = {
167
+ hostname: get_preference(:hostname),
168
+ api_key: get_preference(:api_key)
169
+ }
170
+ Spree::Mollie::OrderSerializer.serialize(money, source, gateway_options, gateway_preferences)
171
+ end
172
+
173
+ def cancel_order!(mollie_order_id)
174
+ ::Mollie::Order.cancel(
175
+ mollie_order_id,
176
+ api_key: get_preference(:api_key)
177
+ )
178
+ MollieLogger.debug("Canceled Mollie order #{mollie_order_id}")
179
+ end
255
180
 
256
- payment.source.update(status: payment.state)
181
+ def invalidate_previous_orders(spree_order_id)
182
+ Spree::Payment.where(order_id: spree_order_id, state: 'processing').each do |payment|
183
+ begin
184
+ mollie_order_id = payment.source.payment_id
185
+ order = ::Mollie::Order.get(
186
+ mollie_order_id,
187
+ api_key: get_preference(:api_key)
188
+ )
189
+ cancel_order!(mollie_order_id) if order.cancelable?
190
+ rescue ::Mollie::Exception => e
191
+ MollieLogger.debug("Failed to invalidate previous order: #{e.message}")
192
+ end
193
+ end
257
194
  end
258
195
  end
259
196
  end
@@ -0,0 +1,25 @@
1
+ Spree::LineItem.class_eval do
2
+ money_methods :discount_amount, :vat_amount
3
+
4
+ def discount_amount
5
+ adjustments.non_tax.sum(:amount).abs
6
+ end
7
+
8
+ def vat_amount
9
+ adjustments.tax.sum(:amount).abs
10
+ end
11
+
12
+ def vat_rate
13
+ if adjustments.tax.any?
14
+ # Spree allows line items to have multiple TaxRate adjustments.
15
+ # Mollie does not support this. Raise an error if there > 1 TaxRate adjustment.
16
+ if adjustments.tax.count > 1
17
+ raise 'Mollie does not support multiple TaxRate adjustments per line item'
18
+ end
19
+
20
+ adjustments.tax.first.source.amount
21
+ else
22
+ 0.00
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,59 @@
1
+ module Spree
2
+ module Mollie
3
+ class Order
4
+ def initialize(order)
5
+ @order = order
6
+ @mollie_order = mollie_order
7
+ end
8
+
9
+ attr_accessor :mollie_order, :order
10
+
11
+ def mollie_order
12
+ ::Mollie::Order.get(mollie_order_id,
13
+ api_key: api_key)
14
+ end
15
+
16
+ def get_line_by_id(line_item_id)
17
+ @mollie_order.lines.detect { |line| line.sku.start_with?("#{line_item_id}-") }
18
+ end
19
+
20
+ def id
21
+ @mollie_order.id
22
+ end
23
+
24
+ def shipping_fees
25
+ @mollie_order.lines.select { |line| line.type === 'shipping_fee' && line.status === 'authorized' }
26
+ end
27
+
28
+ def discounts
29
+ @mollie_order.lines.select { |line| line.type === 'discount' && line.status === 'authorized' }
30
+ end
31
+
32
+ def collect_fees_on_first_shipment?
33
+ gateway.get_preference(:collect_shipping_costs_and_discounts_on_first_shipment)
34
+ end
35
+
36
+ private
37
+
38
+ def mollie_order_id
39
+ payment.source.payment_id
40
+ end
41
+
42
+ def has_mollie_order?
43
+ payment.source.is_a? Spree::MolliePaymentSource
44
+ end
45
+
46
+ def payment
47
+ @order.payments.last
48
+ end
49
+
50
+ def gateway
51
+ Spree::PaymentMethod.find_by_type 'Spree::Gateway::MollieGateway'
52
+ end
53
+
54
+ def api_key
55
+ gateway.get_preference(:api_key)
56
+ end
57
+ end
58
+ end
59
+ end