solidus-adyen 0.1.3 → 0.2.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
  SHA1:
3
- metadata.gz: 08b088acce113a3bfb9cbbb3e9c2ef2537440a0f
4
- data.tar.gz: efe31205a5b93b5901c4a7c0d31d9a862dfd46dd
3
+ metadata.gz: 8d5a390a36ec18d1af12c1f0d506bbb43d2bb26a
4
+ data.tar.gz: 7d2ddb172391f7a153bf8208bea04a63f8f3618c
5
5
  SHA512:
6
- metadata.gz: 7d6a081a47bf73716edc97270f2ea7319b58960a1b6eba7764b6403488bacdc8159904e46027c8914b2e7a4ed3e6d4cb9528fb1f15d73667c514229433084369
7
- data.tar.gz: 1ee9ade8ed1b00679ee61c7e27d120be94945dcd1af96cbdb995c3d8d8ade8e279d5cfe445fe97effaf3398b9de58c9fbac22733324d420a0d0aeac979c7d554
6
+ metadata.gz: 4a40f1640428be6f878c1ed88cd3c1b560e8d0675ecf04e985f8eaa28524d14e6ac017e5c2ed72f9ad0999e07aa6742349d4c9761c8a42cc23c41127876948d5
7
+ data.tar.gz: 4824b60671ed21f56d2a0e4d489aafb6154d4c896ddf4e4155fe6903e927190df990c7345b854c2188c3b9210158aacb921ed6f6afaf5b1ef0c8fb2446eaee63
@@ -1,6 +1,6 @@
1
1
  module Spree
2
2
  module Adyen
3
- class HppsController < ::ActionController::Base
3
+ class HppsController < StoreController
4
4
  load_resource :order, class: "Spree::Order", id_param: :order_id
5
5
  load_resource(
6
6
  :payment_method,
@@ -7,7 +7,17 @@ module Spree
7
7
 
8
8
  # This is the entry point after an Adyen HPP payment is completed
9
9
  def confirm
10
- source = Adyen::HppSource.new(source_params(params))
10
+ if @order.complete?
11
+ confirm_order_already_completed
12
+ else
13
+ confirm_order_incomplete
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def confirm_order_incomplete
20
+ source = Adyen::HppSource.new(source_params)
11
21
 
12
22
  unless source.authorised?
13
23
  flash.notice = Spree.t(:payment_processing_failed)
@@ -18,40 +28,39 @@ module Spree
18
28
  # payment is created in a 'checkout' state so that the payment method
19
29
  # can attempt to auth it. The payment of course is already auth'd and
20
30
  # adyen hpp's authorize implementation just returns a dummy response.
21
- payment =
22
- @order.payments.create!(
23
- amount: @order.total,
24
- payment_method: @payment_method,
25
- source: source,
26
- response_code: params[:pspReference],
27
- state: "checkout",
28
- # Order is explicitly defined here because as of writing the
29
- # Order -> Payments association does not have the inverse of defined
30
- # when we call `order.complete` below payment.order will still
31
- # refer to a previous state of the record.
32
- #
33
- # If the payment is auto captured only then the payment will completed
34
- # in `process_outstanding!`, and because Payment calls
35
- # .order.update_totals after save the order is saved with its
36
- # previous values, causing payment_state and shipment_state to revert
37
- # to nil.
38
- order: @order
39
- )
31
+ @order.payments.create!(
32
+ amount: @order.total,
33
+ payment_method: @payment_method,
34
+ source: source,
35
+ response_code: psp_reference,
36
+ state: "checkout"
37
+ )
40
38
 
41
39
  if @order.complete
42
- # We may have already recieved the authorization notification, so process
43
- # it now
44
- Spree::Adyen::NotificationProcessor.process_outstanding!(payment)
45
-
46
- flash.notice = Spree.t(:order_processed_successfully)
47
- redirect_to order_path(@order)
40
+ redirect_to_order
48
41
  else
49
42
  #TODO void/cancel payment
50
43
  redirect_to checkout_state_path(@order.state)
51
44
  end
52
45
  end
53
46
 
54
- private
47
+ # If an authorization notification is received before the redirection the
48
+ # payment is created there.In this case we just need to assign the addition
49
+ # parameters received about the source.
50
+ #
51
+ # We do this because there is a chance that we never get redirected back
52
+ # so we need to make sure we complete the payment and order.
53
+ def confirm_order_already_completed
54
+ payment = @order.payments.find_by!(response_code: psp_reference)
55
+ payment.source.update(source_params)
56
+
57
+ redirect_to_order
58
+ end
59
+
60
+ def redirect_to_order
61
+ flash.notice = Spree.t(:order_processed_successfully)
62
+ redirect_to order_path(@order)
63
+ end
55
64
 
56
65
  def check_signature
57
66
  unless ::Adyen::Form.redirect_signature_check(params, @payment_method.shared_secret)
@@ -71,11 +80,10 @@ module Spree
71
80
 
72
81
  @order =
73
82
  Spree::Order.
74
- incomplete.
75
83
  find_by!(guest_token: cookies.signed[:guest_token])
76
84
  end
77
85
 
78
- def source_params params
86
+ def source_params
79
87
  params.permit(
80
88
  :authResult,
81
89
  :pspReference,
@@ -86,5 +94,9 @@ module Spree
86
94
  :shopperLocale,
87
95
  :merchantReturnData)
88
96
  end
97
+
98
+ def psp_reference
99
+ params[:pspReference]
100
+ end
89
101
  end
90
102
  end
@@ -1,11 +1,21 @@
1
1
  module Spree
2
2
  module Adyen
3
+ # Class responsible for taking in a notification from Adyen and applying
4
+ # some form of modification to the associated payment.
5
+ #
6
+ # I would in the future like to refactor this by breaking this into
7
+ # separate classes that are only aware of how to process specific kinds of
8
+ # notifications (auth, capture, refund, etc.).
3
9
  class NotificationProcessor
4
10
  attr_accessor :notification, :payment
5
11
 
6
12
  def initialize(notification, payment = nil)
7
13
  self.notification = notification
8
14
  self.payment = payment ? payment : notification.payment
15
+
16
+ if self.payment.nil?
17
+ self.payment = create_missing_payment
18
+ end
9
19
  end
10
20
 
11
21
  # for the given payment, process all notifications that are currently
@@ -97,6 +107,34 @@ module Spree
97
107
  payment.update!(amount: payment.captured_amount)
98
108
  payment.complete!
99
109
  end
110
+
111
+ # At this point the auth was received before the redirect, we create
112
+ # the payment here with the information we have available so that if
113
+ # the user is not redirected to back for some reason we still have a
114
+ # record of the payment.
115
+ def create_missing_payment
116
+ order = notification.order
117
+
118
+ source = Spree::Adyen::HppSource.new(
119
+ auth_result: "unknown",
120
+ order: order,
121
+ payment_method: notification.payment_method,
122
+ psp_reference: notification.psp_reference
123
+ )
124
+
125
+ payment = order.payments.create!(
126
+ amount: notification.money.dollars,
127
+ # We have no idea what payment method they used, this will be
128
+ # updated when/if they get redirected
129
+ payment_method: Spree::Gateway::AdyenHPP.last,
130
+ response_code: notification.psp_reference,
131
+ source: source,
132
+ order: order
133
+ )
134
+
135
+ order.complete
136
+ payment
137
+ end
100
138
  end
101
139
  end
102
140
  end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
2
  module Adyen
3
- VERSION = "0.1.3"
3
+ VERSION = "0.2.1"
4
4
  end
5
5
  end
@@ -5,7 +5,6 @@ describe Spree::AdyenNotificationsController do
5
5
 
6
6
  routes { Spree::Core::Engine.routes }
7
7
 
8
- let(:order) { create :order }
9
8
  let(:params) do
10
9
  { "pspReference" => reference,
11
10
  "eventDate" => "2013-10-21T14:45:45.93Z",
@@ -22,12 +21,14 @@ describe Spree::AdyenNotificationsController do
22
21
  "live" => "false" }
23
22
  end
24
23
 
24
+ let!(:order) { create :completed_order_with_totals }
25
+
25
26
  let!(:payment) do
26
- create :payment, response_code: reference,
27
- payment_method: payment_method
27
+ create :hpp_payment, response_code: reference,
28
+ payment_method: payment_method, order: order
28
29
  end
29
30
 
30
- let(:payment_method) { create :hpp_gateway }
31
+ let!(:payment_method) { create :hpp_gateway }
31
32
 
32
33
  let(:reference) { "8513823667306210" }
33
34
 
@@ -12,8 +12,8 @@ RSpec.describe Spree::AdyenRedirectController, type: :controller do
12
12
  )
13
13
  end
14
14
 
15
- let(:store) { Spree::Store.default }
16
- let(:gateway) { create :hpp_gateway }
15
+ let!(:store) { Spree::Store.default }
16
+ let!(:gateway) { create :hpp_gateway }
17
17
 
18
18
  before do
19
19
  allow(controller).to receive(:check_signature)
@@ -24,6 +24,7 @@ RSpec.describe Spree::AdyenRedirectController, type: :controller do
24
24
 
25
25
  let(:psp_reference) { "8813824003752247" }
26
26
  let(:payment_method) { "amex" }
27
+ let(:merchantReturnData) { "#{order.guest_token}|#{gateway.id}" }
27
28
  let(:params) do
28
29
  { merchantReference: order.number,
29
30
  skinCode: "xxxxxxxx",
@@ -35,28 +36,20 @@ RSpec.describe Spree::AdyenRedirectController, type: :controller do
35
36
  merchantReturnData: merchantReturnData
36
37
  }
37
38
  end
38
- let(:merchantReturnData) { [order.guest_token, gateway.id].join("|") }
39
-
40
- shared_examples "payments are pending" do
41
- it "has pending payments" do
42
- expect(order.payments).to all be_pending
43
- end
44
- end
45
39
 
46
40
  shared_examples "payment is successful" do
47
41
  it "changes the order state to completed" do
48
- expect { subject }.
49
- to change { order.reload.state }.
50
- from("payment").
51
- to("complete").
52
-
53
- and change { order.payment_state }.
54
- from(nil).
55
- to("balance_due").
56
-
57
- and change { order.shipment_state }.
58
- from(nil).
59
- to("pending")
42
+ subject
43
+ order.reload
44
+ expect(order).to have_attributes(
45
+ state: "complete",
46
+ payment_state: "balance_due",
47
+ shipment_state: "pending"
48
+ )
49
+ end
50
+
51
+ it "has pending payments" do
52
+ expect(order.payments).to all be_pending
60
53
  end
61
54
 
62
55
  it "redirects to the order complete page" do
@@ -65,10 +58,8 @@ RSpec.describe Spree::AdyenRedirectController, type: :controller do
65
58
  end
66
59
 
67
60
  it "creates a payment" do
68
- expect{ subject }.
69
- to change{ order.payments.count }.
70
- from(0).
71
- to(1)
61
+ subject
62
+ expect(order.reload.payments.count).to eq 1
72
63
  end
73
64
 
74
65
  context "and the order cannot complete" do
@@ -80,9 +71,20 @@ RSpec.describe Spree::AdyenRedirectController, type: :controller do
80
71
  end
81
72
  end
82
73
 
74
+ shared_examples "payment is not successful" do
75
+ it "does not change order state" do
76
+ expect{ subject }.to_not change{ order.state }
77
+ end
78
+
79
+ it "redirects to the order payment page" do
80
+ is_expected.to have_http_status(:redirect).
81
+ and redirect_to checkout_state_path("payment")
82
+ end
83
+ end
84
+
83
85
  context "when the payment is AUTHORISED" do
84
86
  include_examples "payment is successful"
85
- include_examples "payments are pending"
87
+
86
88
  let(:auth_result) { "AUTHORISED" }
87
89
 
88
90
  context "and the authorisation notification has already been received" do
@@ -91,56 +93,40 @@ RSpec.describe Spree::AdyenRedirectController, type: :controller do
91
93
  let(:notification) do
92
94
  create(
93
95
  :notification,
94
- notification_type,
96
+ :auth,
97
+ processed: true,
95
98
  psp_reference: psp_reference,
96
99
  merchant_reference: order.number)
97
100
  end
98
101
 
99
- shared_examples "auth received" do
100
- include_examples "payment is successful"
102
+ # there will already be a payment and source created at this point
103
+ before do
104
+ source =
105
+ create(:hpp_source, psp_reference: psp_reference, order: order)
101
106
 
102
- it "processes the notification" do
103
- expect { subject }.
104
- to change { notification.reload.processed }.
105
- from(false).
106
- to(true)
107
- end
108
- end
107
+ create(:hpp_payment, source: source, order: order)
109
108
 
110
- context "and payment method is sofort" do
111
- let(:notification_type) { :sofort_auth }
112
- include_examples "auth received"
109
+ order.complete
113
110
  end
114
111
 
115
- context "and payment method is ideal" do
116
- let(:notification_type) { :ideal_auth }
117
- include_examples "auth received"
118
- end
112
+ it { expect { subject }.to_not change { order.payments.count }.from 1 }
119
113
 
120
- context "and payment method is credit" do
121
- let(:notification_type) { :auth }
122
- include_examples "auth received"
114
+ it "updates the source" do
115
+ expect(order.payments.last.source).to have_attributes(
116
+ auth_result: "AUTHORISED",
117
+ skin_code: "XXXXXXXX"
118
+ )
123
119
  end
120
+
121
+ include_examples "payment is successful"
124
122
  end
125
123
  end
126
124
 
127
125
  context "when the payment is PENDING" do
128
126
  include_examples "payment is successful"
129
- include_examples "payments are pending"
130
127
  let(:auth_result) { "PENDING" }
131
128
  end
132
129
 
133
- shared_examples "payment is not successful" do
134
- it "does not change order state" do
135
- expect{ subject }.to_not change{ order.state }
136
- end
137
-
138
- it "redirects to the order payment page" do
139
- is_expected.to have_http_status(:redirect).
140
- and redirect_to checkout_state_path("payment")
141
- end
142
- end
143
-
144
130
  context "when the payment is CANCELLED" do
145
131
  include_examples "payment is not successful"
146
132
  let(:auth_result) { "CANCELLED" }
@@ -6,6 +6,7 @@ FactoryGirl.define do
6
6
 
7
7
  before :create do |record, _|
8
8
  # these associations/keys are awful and are making this difficult
9
+ record.response_code = record.source.psp_reference
9
10
  record.source.order = record.order
10
11
  record.source.merchant_reference = record.order.number
11
12
  end
@@ -12,10 +12,18 @@ RSpec.describe Spree::Adyen::NotificationProcessor do
12
12
  amount: 23.99,
13
13
  state: payment_state,
14
14
  payment_method: hpp_gateway,
15
- order: create(:order, currency: "EUR")
15
+ order: order
16
16
  )
17
17
  end
18
18
 
19
+ let!(:order) do
20
+ # spree factories suck, it's not easy to get something to payment state
21
+ create(:order_with_line_items).tap do |order|
22
+ order.contents.advance
23
+ expect(order.state).to eq "payment"
24
+ end
25
+ end
26
+
19
27
  let!(:hpp_gateway) do
20
28
  create(:hpp_gateway)
21
29
  end
@@ -26,8 +34,9 @@ RSpec.describe Spree::Adyen::NotificationProcessor do
26
34
  event_type, # these are registered traits, refer to the factory
27
35
  success: success,
28
36
  value: 2399,
29
- currency: "EUR",
30
- payment: payment
37
+ currency: "USD",
38
+ payment: payment,
39
+ order: order
31
40
  )
32
41
  end
33
42
 
@@ -66,7 +75,7 @@ RSpec.describe Spree::Adyen::NotificationProcessor do
66
75
  end
67
76
 
68
77
  it "completes the payment" do
69
- expect{ subject }.
78
+ expect { subject }.
70
79
  to change{ payment.reload.state }.
71
80
  from("pending").
72
81
  to("completed")
@@ -99,8 +108,6 @@ RSpec.describe Spree::Adyen::NotificationProcessor do
99
108
  context "when event is AUTHORISATION" do
100
109
  let(:event_type) { :auth }
101
110
 
102
- it "changes the available actions"
103
-
104
111
  context "and payment method was c_cash", pending: true do
105
112
  pending "completes payment"
106
113
  end
@@ -109,6 +116,31 @@ RSpec.describe Spree::Adyen::NotificationProcessor do
109
116
  pending "completes payment"
110
117
  end
111
118
 
119
+ # this is for the situation where we can the notification before the
120
+ # redirect
121
+ context "and the payment doesn't exist yet" do
122
+ let!(:payment) { nil }
123
+
124
+ it "processes the notification" do
125
+ expect { subject }.
126
+ to change { notification.processed }.
127
+ to true
128
+ end
129
+
130
+ it "completes the order" do
131
+ subject
132
+ expect(order.reload).to have_attributes(
133
+ state: "complete"
134
+ )
135
+ end
136
+
137
+ it "creates a payment" do
138
+ expect { subject }.
139
+ to change { order.payments.count }.
140
+ to 1
141
+ end
142
+ end
143
+
112
144
  context "and payment method was ideal" do
113
145
  let(:event_type) { :ideal_auth }
114
146
  include_examples "completes payment"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solidus-adyen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dylan Kendal
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-10 00:00:00.000000000 Z
11
+ date: 2015-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: adyen