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 +4 -4
- data/app/controllers/spree/adyen/hpps_controller.rb +1 -1
- data/app/controllers/spree/adyen_redirect_controller.rb +41 -29
- data/app/models/spree/adyen/notification_processor.rb +38 -0
- data/lib/spree/adyen/version.rb +1 -1
- data/spec/controllers/spree/adyen_notifications_controller_spec.rb +5 -4
- data/spec/controllers/spree/adyen_redirect_controller_spec.rb +44 -58
- data/spec/factories/spree_payment.rb +1 -0
- data/spec/models/spree/adyen/notification_processor_spec.rb +38 -6
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d5a390a36ec18d1af12c1f0d506bbb43d2bb26a
|
4
|
+
data.tar.gz: 7d2ddb172391f7a153bf8208bea04a63f8f3618c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a40f1640428be6f878c1ed88cd3c1b560e8d0675ecf04e985f8eaa28524d14e6ac017e5c2ed72f9ad0999e07aa6742349d4c9761c8a42cc23c41127876948d5
|
7
|
+
data.tar.gz: 4824b60671ed21f56d2a0e4d489aafb6154d4c896ddf4e4155fe6903e927190df990c7345b854c2188c3b9210158aacb921ed6f6afaf5b1ef0c8fb2446eaee63
|
@@ -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
|
-
|
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
|
-
|
22
|
-
@order.
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
data/lib/spree/adyen/version.rb
CHANGED
@@ -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 :
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
69
|
-
|
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
|
-
|
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
|
-
|
96
|
+
:auth,
|
97
|
+
processed: true,
|
95
98
|
psp_reference: psp_reference,
|
96
99
|
merchant_reference: order.number)
|
97
100
|
end
|
98
101
|
|
99
|
-
|
100
|
-
|
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
|
-
|
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
|
-
|
111
|
-
let(:notification_type) { :sofort_auth }
|
112
|
-
include_examples "auth received"
|
109
|
+
order.complete
|
113
110
|
end
|
114
111
|
|
115
|
-
|
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
|
-
|
121
|
-
|
122
|
-
|
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:
|
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: "
|
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
|
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-
|
11
|
+
date: 2015-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: adyen
|