solidus-adyen 0.1.2
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 +7 -0
- data/.gitignore +26 -0
- data/.rubocop.yml +9 -0
- data/.rubocop_todo.yml +325 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +238 -0
- data/Rakefile +16 -0
- data/app/assets/javascripts/spree/backend/solidus-adyen.js +1 -0
- data/app/assets/javascripts/spree/checkout/payment/adyen.js +10 -0
- data/app/assets/javascripts/spree/frontend/solidus-adyen.js +1 -0
- data/app/assets/stylesheets/spree/backend/solidus-adyen/buttons.scss +15 -0
- data/app/assets/stylesheets/spree/backend/solidus-adyen/communication.scss +121 -0
- data/app/assets/stylesheets/spree/backend/solidus-adyen/variables.scss +4 -0
- data/app/assets/stylesheets/spree/backend/solidus-adyen.css +5 -0
- data/app/assets/stylesheets/spree/frontend/solidus-adyen.css +3 -0
- data/app/controllers/concerns/spree/adyen/admin/refunds_controller.rb +59 -0
- data/app/controllers/spree/adyen/hpps_controller.rb +22 -0
- data/app/controllers/spree/adyen_notifications_controller.rb +34 -0
- data/app/controllers/spree/adyen_redirect_controller.rb +90 -0
- data/app/models/adyen_notification.rb +159 -0
- data/app/models/concerns/spree/adyen/order.rb +11 -0
- data/app/models/concerns/spree/adyen/payment.rb +95 -0
- data/app/models/spree/adyen/hpp_source.rb +101 -0
- data/app/models/spree/adyen/notification_processor.rb +102 -0
- data/app/models/spree/adyen/presenters/communication.rb +31 -0
- data/app/models/spree/adyen/presenters/communications/adyen_notification.rb +26 -0
- data/app/models/spree/adyen/presenters/communications/base.rb +50 -0
- data/app/models/spree/adyen/presenters/communications/hpp_source.rb +31 -0
- data/app/models/spree/adyen/presenters/communications/log_entry.rb +28 -0
- data/app/models/spree/adyen/presenters/communications.rb +9 -0
- data/app/models/spree/gateway/adyen_hpp.rb +95 -0
- data/app/overrides/spree/admin/shared/_order_summary.rb +6 -0
- data/app/views/spree/admin/payments/source_forms/_adyen.html.erb +1 -0
- data/app/views/spree/admin/payments/source_views/_adyen.html.erb +14 -0
- data/app/views/spree/adyen/_manual_refund.html.erb +9 -0
- data/app/views/spree/adyen/communication/_communication.html.erb +42 -0
- data/app/views/spree/adyen/hpps/directory.html.erb +10 -0
- data/app/views/spree/checkout/payment/_adyen.html.erb +33 -0
- data/bin/checkout.rb +111 -0
- data/bin/regen.sh +1 -0
- data/config/initializers/solidus_adyen.rb +1 -0
- data/config/locales/en.yml +23 -0
- data/config/routes.rb +13 -0
- data/db/migrate/20131017040945_create_adyen_notifications.rb +29 -0
- data/db/migrate/20150911201942_add_index_adyen_notifications_psp_reference.rb +5 -0
- data/db/migrate/20150914162539_create_spree_adyen_hpp_sources.rb +21 -0
- data/db/migrate/20151007090519_add_days_to_ship_to_config.rb +5 -0
- data/db/migrate/20151020230830_remove_indices_on_adyen_notifications.rb +6 -0
- data/db/migrate/20151106093023_allow_merchant_reference_to_be_null_for_adyen_notification.rb +5 -0
- data/lib/solidus-adyen.rb +1 -0
- data/lib/spree/adyen/engine.rb +32 -0
- data/lib/spree/adyen/form.rb +135 -0
- data/lib/spree/adyen/hpp_check.rb +11 -0
- data/lib/spree/adyen/url.rb +30 -0
- data/lib/spree/adyen/version.rb +5 -0
- data/lib/spree/adyen.rb +6 -0
- data/solidus-adyen.gemspec +51 -0
- data/spec/controllers/concerns/spree/adyen/admin/refunds_controller_spec.rb +76 -0
- data/spec/controllers/spree/adyen/hpps_controller_spec.rb +43 -0
- data/spec/controllers/spree/adyen_notifications_controller_spec.rb +118 -0
- data/spec/controllers/spree/adyen_redirect_controller_spec.rb +154 -0
- data/spec/factories/active_merchant_billing_response.rb +23 -0
- data/spec/factories/adyen_notification.rb +91 -0
- data/spec/factories/spree_adyen_hpp_source.rb +15 -0
- data/spec/factories/spree_gateway_adyen_hpp.rb +23 -0
- data/spec/factories/spree_payment.rb +13 -0
- data/spec/lib/spree/adyen/form_spec.rb +214 -0
- data/spec/models/adyen_notification_spec.rb +86 -0
- data/spec/models/concerns/spree/adyen/order_spec.rb +22 -0
- data/spec/models/concerns/spree/adyen/payment_spec.rb +93 -0
- data/spec/models/spree/adyen/hpp_source_spec.rb +101 -0
- data/spec/models/spree/adyen/notification_processor_spec.rb +205 -0
- data/spec/models/spree/adyen/presenters/communication_spec.rb +62 -0
- data/spec/models/spree/gateway/adyen_hpp_spec.rb +76 -0
- data/spec/spec_helper.rb +65 -0
- data/spec/support/shared_contexts/mock_adyen_api.rb +47 -0
- metadata +463 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe AdyenNotification do
|
|
4
|
+
it { is_expected.to have_many(:next).inverse_of(:prev) }
|
|
5
|
+
it { is_expected.to belong_to(:prev).inverse_of(:next) }
|
|
6
|
+
it { is_expected.to belong_to :order }
|
|
7
|
+
|
|
8
|
+
describe ".payment" do
|
|
9
|
+
subject { notification.payment }
|
|
10
|
+
|
|
11
|
+
let(:ref) { "999999999" }
|
|
12
|
+
let!(:payment) { create :payment, response_code: ref }
|
|
13
|
+
let!(:notification) { described_class.new attr => ref }
|
|
14
|
+
|
|
15
|
+
shared_examples "finds the payment" do
|
|
16
|
+
it { is_expected.to eq payment }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context "normal notification" do
|
|
20
|
+
let(:attr) { :original_reference }
|
|
21
|
+
include_examples "finds the payment"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
context "modification notification" do
|
|
25
|
+
let(:attr) { :psp_reference }
|
|
26
|
+
include_examples "finds the payment"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "#build" do
|
|
31
|
+
subject { described_class.build params }
|
|
32
|
+
|
|
33
|
+
let(:params) do
|
|
34
|
+
{ "currency" => "USD",
|
|
35
|
+
"eventCode" => "AUTHORISATION",
|
|
36
|
+
"eventDate" => "2013-10-21T00:00:00.00Z",
|
|
37
|
+
"live" => "false",
|
|
38
|
+
"merchantAccountCode" => "Test",
|
|
39
|
+
"merchantReference" => "R999999999",
|
|
40
|
+
"operations" => "CANCEL,CAPTURE,REFUND",
|
|
41
|
+
"originalReference" => "",
|
|
42
|
+
"paymentMethod" => "visa",
|
|
43
|
+
"pspReference" => "999999999",
|
|
44
|
+
"reason" => "41061:1111:6/2016",
|
|
45
|
+
"success" => "true",
|
|
46
|
+
"value" => "6999",
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "makes a new notification with the expected fields" do
|
|
51
|
+
is_expected.
|
|
52
|
+
to be_a_new_record.
|
|
53
|
+
and have_attributes(
|
|
54
|
+
currency: "USD",
|
|
55
|
+
event_code: "AUTHORISATION",
|
|
56
|
+
event_date: Time.utc(2013, 10, 21),
|
|
57
|
+
live: false,
|
|
58
|
+
merchant_account_code: "Test",
|
|
59
|
+
merchant_reference: "R999999999",
|
|
60
|
+
operations: "CANCEL,CAPTURE,REFUND",
|
|
61
|
+
original_reference: nil,
|
|
62
|
+
payment_method: "visa",
|
|
63
|
+
psp_reference: "999999999",
|
|
64
|
+
reason: "41061:1111:6/2016",
|
|
65
|
+
success: true,
|
|
66
|
+
value: 6999,
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe ".actions" do
|
|
72
|
+
subject { notification.actions }
|
|
73
|
+
let(:notification) { create :notification, :auth, operations: operations }
|
|
74
|
+
|
|
75
|
+
context "when the notification has operations" do
|
|
76
|
+
let(:operations) { "CAPTURE,REFUND,CANCEL_OR_REFUND" }
|
|
77
|
+
|
|
78
|
+
it { is_expected.to eq ["capture", "refund", "cancel_or_refund"] }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
context "when the notification's operations are nil" do
|
|
82
|
+
let(:operations) { nil }
|
|
83
|
+
it { is_expected.to eq [] }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe Spree::Order do
|
|
4
|
+
include_context "mock adyen api", success: true
|
|
5
|
+
|
|
6
|
+
describe "requires_manual_refund?" do
|
|
7
|
+
subject { order.requires_manual_refund? }
|
|
8
|
+
|
|
9
|
+
let!(:order) { create :order_ready_to_ship }
|
|
10
|
+
|
|
11
|
+
it { is_expected.to be false }
|
|
12
|
+
|
|
13
|
+
context "when it is cancelled and has a payment that much be manually refunded" do
|
|
14
|
+
let!(:payment) { create :hpp_payment, order: order, source: source }
|
|
15
|
+
let(:source) { create :hpp_source, :sofort, order: order }
|
|
16
|
+
|
|
17
|
+
before { order.cancel! }
|
|
18
|
+
|
|
19
|
+
it { is_expected.to be true }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Spree::Adyen::Payment do
|
|
4
|
+
let(:payment) { create :hpp_payment }
|
|
5
|
+
|
|
6
|
+
shared_examples "gateway action" do
|
|
7
|
+
context "when the action succeeds" do
|
|
8
|
+
include_context "mock adyen api", success: true
|
|
9
|
+
|
|
10
|
+
it "logs the response" do
|
|
11
|
+
expect{ subject }.to change{ payment.reload.log_entries.count }.by(1)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "changes payment state to processing" do
|
|
15
|
+
expect{ subject }.to change{ payment.state }.to("processing")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context "when the action fails" do
|
|
20
|
+
include_context(
|
|
21
|
+
"mock adyen api",
|
|
22
|
+
success: false,
|
|
23
|
+
fault_message: "Expected message")
|
|
24
|
+
|
|
25
|
+
it "logs the response" do
|
|
26
|
+
expect{ subject }.
|
|
27
|
+
to raise_error(Spree::Core::GatewayError).
|
|
28
|
+
and change{ payment.reload.log_entries.count }.by(1)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "does not change the status of the payment" do
|
|
32
|
+
expect{ subject }.
|
|
33
|
+
to raise_error(Spree::Core::GatewayError, "Expected message").
|
|
34
|
+
and keep { payment.reload.state }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "cancel!" do
|
|
40
|
+
subject { payment.cancel! }
|
|
41
|
+
include_examples "gateway action"
|
|
42
|
+
|
|
43
|
+
context "when the payment doesn't have an hpp source" do
|
|
44
|
+
let(:payment) { create :payment }
|
|
45
|
+
|
|
46
|
+
it "keeps the orginal behaviour" do
|
|
47
|
+
expect{ subject }.
|
|
48
|
+
to change { payment.reload.state }.
|
|
49
|
+
from("checkout").
|
|
50
|
+
to("void")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context "when payment is only manually refundable" do
|
|
55
|
+
let(:payment) { create :hpp_payment, source: source }
|
|
56
|
+
let(:source) { create :hpp_source, :sofort }
|
|
57
|
+
|
|
58
|
+
it "creates a log entry" do
|
|
59
|
+
expect { subject }.
|
|
60
|
+
to change { payment.reload.log_entries.count }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "doesn't change the state" do
|
|
64
|
+
expect { subject }.
|
|
65
|
+
to_not change { payment.reload.state }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe "capture!" do
|
|
71
|
+
subject { payment.capture! }
|
|
72
|
+
include_examples "gateway action"
|
|
73
|
+
|
|
74
|
+
context "when the payment doesn't have an hpp source" do
|
|
75
|
+
let(:payment) { create :payment }
|
|
76
|
+
|
|
77
|
+
it "keeps the orginal behaviour" do
|
|
78
|
+
expect{ subject }.
|
|
79
|
+
to change { payment.reload.state }.
|
|
80
|
+
from("checkout").
|
|
81
|
+
to("completed").
|
|
82
|
+
|
|
83
|
+
and change { payment.capture_events.count }.
|
|
84
|
+
by(1)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe "credit!" do
|
|
90
|
+
subject { payment.credit! "1000", currency: "EUR" }
|
|
91
|
+
include_examples "gateway action"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe Spree::Adyen::HppSource do
|
|
4
|
+
include_context "mock adyen api", success: true
|
|
5
|
+
|
|
6
|
+
it { is_expected.to belong_to(:order) }
|
|
7
|
+
it { is_expected.to have_one(:payment) }
|
|
8
|
+
it { is_expected.to have_many(:notifications) }
|
|
9
|
+
|
|
10
|
+
let(:hpp_source) do
|
|
11
|
+
create :hpp_source,
|
|
12
|
+
psp_reference: "999999999",
|
|
13
|
+
merchant_reference: "R11111111",
|
|
14
|
+
payment: create(:hpp_payment)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
describe ".actions" do
|
|
18
|
+
subject { hpp_source.actions }
|
|
19
|
+
let!(:notification) do
|
|
20
|
+
create(
|
|
21
|
+
:notification,
|
|
22
|
+
:auth,
|
|
23
|
+
operations: "CAPTURE,REFUND",
|
|
24
|
+
psp_reference: "999999999",
|
|
25
|
+
merchant_reference: "R11111111",
|
|
26
|
+
processed: true
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it { expect(hpp_source.notifications.count).to eq 1 }
|
|
31
|
+
it { expect(hpp_source.actions).
|
|
32
|
+
to eq %w{capture credit} }
|
|
33
|
+
|
|
34
|
+
shared_examples "has no actions" do
|
|
35
|
+
it { is_expected.to eq [] }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context "when it has no auth notification" do
|
|
39
|
+
let!(:notification) { nil }
|
|
40
|
+
include_examples "has no actions"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context "when the payment is void" do
|
|
44
|
+
before { hpp_source.payment.void }
|
|
45
|
+
include_examples "has no actions"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context "when the payment is still proccesing" do
|
|
49
|
+
before { hpp_source.payment.started_processing! }
|
|
50
|
+
include_examples "has no actions"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe ".can_capture?" do
|
|
55
|
+
subject { hpp_source.can_capture? payment }
|
|
56
|
+
let!(:payment) { create :payment, amount: 10.0 }
|
|
57
|
+
|
|
58
|
+
it { is_expected.to be true }
|
|
59
|
+
|
|
60
|
+
context "when there is no outstanding balance" do
|
|
61
|
+
before do
|
|
62
|
+
payment.
|
|
63
|
+
capture_events.
|
|
64
|
+
create! amount: ::Money.new(1000, "EUR").to_f
|
|
65
|
+
end
|
|
66
|
+
it { is_expected.to be false }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe ".authorised?" do
|
|
71
|
+
subject { described_class.new(auth_result: event).authorised? }
|
|
72
|
+
|
|
73
|
+
context "when pending" do
|
|
74
|
+
let(:event) { "PENDING" }
|
|
75
|
+
it { is_expected.to be true }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
context "when authorised" do
|
|
79
|
+
let(:event) { "AUTHORISED" }
|
|
80
|
+
it { is_expected.to be true }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
context "when something else" do
|
|
84
|
+
let(:event) { "REFUSED" }
|
|
85
|
+
it { is_expected.to be false }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
describe ".can_cancel?" do
|
|
90
|
+
subject { hpp_source.can_cancel? hpp_source.payment }
|
|
91
|
+
|
|
92
|
+
context "when the payment has refunds" do
|
|
93
|
+
before { create :refund, amount: 1, payment: hpp_source.payment }
|
|
94
|
+
it { is_expected.to be false }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context "when the payment doesn't have refunds" do
|
|
98
|
+
it { is_expected.to be true }
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
RSpec.describe Spree::Adyen::NotificationProcessor do
|
|
4
|
+
include_context "mock adyen api", success: true
|
|
5
|
+
|
|
6
|
+
describe "#process" do
|
|
7
|
+
subject { described_class.new(notification).process! }
|
|
8
|
+
|
|
9
|
+
let!(:payment) do
|
|
10
|
+
create(
|
|
11
|
+
:payment,
|
|
12
|
+
amount: 23.99,
|
|
13
|
+
state: payment_state,
|
|
14
|
+
payment_method: hpp_gateway,
|
|
15
|
+
order: create(:order, currency: "EUR")
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let!(:hpp_gateway) do
|
|
20
|
+
create(:hpp_gateway)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
let!(:notification) do
|
|
24
|
+
create(
|
|
25
|
+
:notification,
|
|
26
|
+
event_type, # these are registered traits, refer to the factory
|
|
27
|
+
success: success,
|
|
28
|
+
value: 2399,
|
|
29
|
+
currency: "EUR",
|
|
30
|
+
payment: payment
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
let(:payment_state) { "pending" }
|
|
35
|
+
let(:success) { true }
|
|
36
|
+
|
|
37
|
+
shared_examples "returns the notification" do
|
|
38
|
+
it "always returns the notification" do
|
|
39
|
+
is_expected.to be_a AdyenNotification
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
shared_examples "processed event" do
|
|
44
|
+
include_examples "returns the notification"
|
|
45
|
+
|
|
46
|
+
it "marks the notification as processed" do
|
|
47
|
+
expect{ subject }.
|
|
48
|
+
to change{ notification.processed }.
|
|
49
|
+
from(false).
|
|
50
|
+
to(true)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
shared_examples "completes payment" do
|
|
55
|
+
include_examples "processed event"
|
|
56
|
+
|
|
57
|
+
it "updates the captured amount" do
|
|
58
|
+
expect{ subject }.
|
|
59
|
+
to change{ payment.captured_amount }.
|
|
60
|
+
from(0).
|
|
61
|
+
to(23.99).
|
|
62
|
+
|
|
63
|
+
and change{ payment.capture_events.count }.
|
|
64
|
+
from(0).
|
|
65
|
+
to(1)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "completes the payment" do
|
|
69
|
+
expect{ subject }.
|
|
70
|
+
to change{ payment.reload.state }.
|
|
71
|
+
from("pending").
|
|
72
|
+
to("completed")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
shared_examples "fails payment" do
|
|
77
|
+
include_examples "processed event"
|
|
78
|
+
|
|
79
|
+
it "marks the payment as a failure" do
|
|
80
|
+
expect{ subject }.
|
|
81
|
+
to change{ payment.reload.state }.
|
|
82
|
+
from("pending").
|
|
83
|
+
to("failed")
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
shared_examples "does nothing" do
|
|
88
|
+
include_examples "processed event"
|
|
89
|
+
|
|
90
|
+
it "does not change the payment state" do
|
|
91
|
+
expect{ subject }.to_not change{ payment.state }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "does not change the captured amount" do
|
|
95
|
+
expect{ subject }.to_not change{ payment.captured_amount }
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
context "when event is AUTHORISATION" do
|
|
100
|
+
let(:event_type) { :auth }
|
|
101
|
+
|
|
102
|
+
it "changes the available actions"
|
|
103
|
+
|
|
104
|
+
context "and payment method was c_cash", pending: true do
|
|
105
|
+
pending "completes payment"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
context "and payment method was bank transfer", pending: true do
|
|
109
|
+
pending "completes payment"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
context "and payment method was ideal" do
|
|
113
|
+
let(:event_type) { :ideal_auth }
|
|
114
|
+
include_examples "completes payment"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context "and it was not successful" do
|
|
118
|
+
let(:success) { false }
|
|
119
|
+
include_examples "fails payment"
|
|
120
|
+
|
|
121
|
+
context "and the payment was already complete" do
|
|
122
|
+
let(:payment_state) { "completed" }
|
|
123
|
+
include_examples "does nothing"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context "when event is CAPTURE" do
|
|
129
|
+
let(:event_type) { :capture }
|
|
130
|
+
include_examples "completes payment"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
context "when event is CANCEL_OR_REFUND" do
|
|
134
|
+
let(:event_type) { :cancel_or_refund }
|
|
135
|
+
|
|
136
|
+
before do
|
|
137
|
+
payment.complete
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "voids the payment" do
|
|
141
|
+
expect { subject }.
|
|
142
|
+
to change { payment.reload.state }.
|
|
143
|
+
from("completed").
|
|
144
|
+
to("void")
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
context "when event is REFUND" do
|
|
149
|
+
include_examples "processed event"
|
|
150
|
+
|
|
151
|
+
let(:event_type) { :refund }
|
|
152
|
+
let(:payment_state) { "processing" }
|
|
153
|
+
|
|
154
|
+
it "creates a refund" do
|
|
155
|
+
expect { subject }.
|
|
156
|
+
to change { payment.reload.refunds.count }.
|
|
157
|
+
from(0).
|
|
158
|
+
to(1)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "changes the payment state to completed" do
|
|
162
|
+
expect { subject }.
|
|
163
|
+
to change { payment.reload.state }.
|
|
164
|
+
from("processing").
|
|
165
|
+
to("completed")
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
context "when the event is an event we don't process" do
|
|
170
|
+
let(:event_type) { :pending }
|
|
171
|
+
include_examples "returns the notification"
|
|
172
|
+
|
|
173
|
+
it "sets processed to false" do
|
|
174
|
+
expect(subject.processed).to be false
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
describe "#process_outstanding!" do
|
|
180
|
+
subject { described_class.process_outstanding! payment }
|
|
181
|
+
|
|
182
|
+
let!(:payment) { create :hpp_payment, amount: 19.99, state: "pending" }
|
|
183
|
+
|
|
184
|
+
let!(:notifications) do
|
|
185
|
+
opts = {payment: payment, value: 1999}
|
|
186
|
+
[
|
|
187
|
+
create(:notification, :auth, **opts),
|
|
188
|
+
create(:notification, :capture, **opts)
|
|
189
|
+
]
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it "processes all notifications" do
|
|
193
|
+
subject
|
|
194
|
+
notifications.map(&:reload)
|
|
195
|
+
expect(notifications).to all be_processed
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
it "modifies the payment" do
|
|
199
|
+
expect { subject }.
|
|
200
|
+
to change { payment.state }.from("pending").to("completed").
|
|
201
|
+
|
|
202
|
+
and change { payment.captured_amount }.from(0).to(19.99)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
module Adyen
|
|
5
|
+
module Presenters
|
|
6
|
+
RSpec.describe Communication do
|
|
7
|
+
let!(:source) { payment.source }
|
|
8
|
+
let!(:payment) { create :hpp_payment }
|
|
9
|
+
let!(:notification) { create :notification, :auth, payment: payment }
|
|
10
|
+
|
|
11
|
+
let!(:log_entry) do
|
|
12
|
+
payment.send(
|
|
13
|
+
:record_response,
|
|
14
|
+
OpenStruct.new(success?: true, message: "sup")
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe "#from_source" do
|
|
19
|
+
subject { described_class.from_source source }
|
|
20
|
+
|
|
21
|
+
it "builds a collection of presenters that all implement the interface" do
|
|
22
|
+
expect(subject).
|
|
23
|
+
to be_an(Array).
|
|
24
|
+
and all(
|
|
25
|
+
be_a(Communications::Base).
|
|
26
|
+
and respond_to(:success?).
|
|
27
|
+
and respond_to(:processed?).
|
|
28
|
+
and respond_to(:inbound?).
|
|
29
|
+
and respond_to(:fields))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe "#build" do
|
|
34
|
+
subject { described_class.build(record) }
|
|
35
|
+
|
|
36
|
+
[[:log_entry, Communications::LogEntry],
|
|
37
|
+
[:source, Communications::HppSource],
|
|
38
|
+
[:notification, Communications::AdyenNotification]
|
|
39
|
+
].each do |assigned_name, presenter|
|
|
40
|
+
context "when presented object is a #{assigned_name.to_s.humanize}" do
|
|
41
|
+
let(:record) { send assigned_name }
|
|
42
|
+
|
|
43
|
+
it "creates a presenter using #{presenter}" do
|
|
44
|
+
is_expected.to be_a presenter
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context "when a presenter cannot be found" do
|
|
50
|
+
let(:record) { Object.new }
|
|
51
|
+
|
|
52
|
+
it "fails" do
|
|
53
|
+
expect { subject }.to raise_error(
|
|
54
|
+
RuntimeError,
|
|
55
|
+
/Couldn't map to a communication type/)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "support/shared_contexts/mock_adyen_api"
|
|
3
|
+
|
|
4
|
+
module Spree
|
|
5
|
+
describe Gateway::AdyenHPP do
|
|
6
|
+
let(:hpp_source) { create :hpp_source, psp_reference: "9999" }
|
|
7
|
+
let(:gateway) { described_class.new }
|
|
8
|
+
|
|
9
|
+
include_context "mock adyen api", success: true
|
|
10
|
+
|
|
11
|
+
shared_examples "delayed gateway action" do
|
|
12
|
+
context "when the action succeeds" do
|
|
13
|
+
include_context "mock adyen api", success: true
|
|
14
|
+
|
|
15
|
+
it { is_expected.to be_a ::ActiveMerchant::Billing::Response }
|
|
16
|
+
|
|
17
|
+
it "returns the orginal psp ref as an authorization" do
|
|
18
|
+
expect(subject.authorization).to eq "9999"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context "when the action fails" do
|
|
23
|
+
include_context(
|
|
24
|
+
"mock adyen api",
|
|
25
|
+
success: false,
|
|
26
|
+
fault_message: "Should fail")
|
|
27
|
+
|
|
28
|
+
it "has a response that contains the failure message" do
|
|
29
|
+
expect(subject.success?).to be false
|
|
30
|
+
expect(subject.message).to eq "Should fail"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe ".capture" do
|
|
36
|
+
subject { gateway.capture(2000, "9999", currency: "EUR") }
|
|
37
|
+
include_examples "delayed gateway action"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe ".credit" do
|
|
41
|
+
subject { gateway.credit(2000, "9999", currency: "EUR") }
|
|
42
|
+
include_examples "delayed gateway action"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe ".cancel" do
|
|
46
|
+
subject { gateway.cancel("9999") }
|
|
47
|
+
include_examples "delayed gateway action"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
describe ".authorize" do
|
|
51
|
+
subject { gateway.authorize 2000, hpp_source, currency: "EUR" }
|
|
52
|
+
it { is_expected.to be_a ActiveMerchant::Billing::Response }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context "calculate ship_before_date" do
|
|
56
|
+
let(:test_time) { Time.local(2015, 9, 1, 12, 0, 0) }
|
|
57
|
+
|
|
58
|
+
context "days_to_ship has been set" do
|
|
59
|
+
it "returns tomorrow" do
|
|
60
|
+
Timecop.freeze(test_time) do
|
|
61
|
+
expect(subject.ship_before_date).to eq Time.local(2015, 9, 2, 12, 0, 0)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context "days_to_ship has not been set" do
|
|
67
|
+
it "returns date days_to_ship in the future" do
|
|
68
|
+
subject.preferred_days_to_ship = 3
|
|
69
|
+
Timecop.freeze(test_time) do
|
|
70
|
+
expect(subject.ship_before_date).to eq Time.local(2015, 9, 4, 12, 0, 0)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|