Skip to content

Commit 84657b7

Browse files
committed
Merge pull request #35 from StemboltHQ/bug-auth-completion
Create payment in auth notification
2 parents 9711cdb + 26a9ded commit 84657b7

File tree

7 files changed

+168
-98
lines changed

7 files changed

+168
-98
lines changed

app/controllers/spree/adyen_redirect_controller.rb

+41-29
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,17 @@ class AdyenRedirectController < StoreController
77

88
# This is the entry point after an Adyen HPP payment is completed
99
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)
1121

1222
unless source.authorised?
1323
flash.notice = Spree.t(:payment_processing_failed)
@@ -18,40 +28,39 @@ def confirm
1828
# payment is created in a 'checkout' state so that the payment method
1929
# can attempt to auth it. The payment of course is already auth'd and
2030
# 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+
)
4038

4139
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
4841
else
4942
#TODO void/cancel payment
5043
redirect_to checkout_state_path(@order.state)
5144
end
5245
end
5346

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
5564

5665
def check_signature
5766
unless ::Adyen::Form.redirect_signature_check(params, @payment_method.shared_secret)
@@ -71,11 +80,10 @@ def restore_session
7180

7281
@order =
7382
Spree::Order.
74-
incomplete.
7583
find_by!(guest_token: cookies.signed[:guest_token])
7684
end
7785

78-
def source_params params
86+
def source_params
7987
params.permit(
8088
:authResult,
8189
:pspReference,
@@ -86,5 +94,9 @@ def source_params params
8694
:shopperLocale,
8795
:merchantReturnData)
8896
end
97+
98+
def psp_reference
99+
params[:pspReference]
100+
end
89101
end
90102
end

app/models/spree/adyen/notification_processor.rb

+38
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
module Spree
22
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.).
39
class NotificationProcessor
410
attr_accessor :notification, :payment
511

612
def initialize(notification, payment = nil)
713
self.notification = notification
814
self.payment = payment ? payment : notification.payment
15+
16+
if self.payment.nil?
17+
self.payment = create_missing_payment
18+
end
919
end
1020

1121
# for the given payment, process all notifications that are currently
@@ -97,6 +107,34 @@ def complete_payment!
97107
payment.update!(amount: payment.captured_amount)
98108
payment.complete!
99109
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
100138
end
101139
end
102140
end

lib/spree/adyen/version.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module Spree
22
module Adyen
3-
VERSION = "0.1.3"
3+
VERSION = "0.2.0"
44
end
55
end

spec/controllers/spree/adyen_notifications_controller_spec.rb

+5-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
routes { Spree::Core::Engine.routes }
77

8-
let(:order) { create :order }
98
let(:params) do
109
{ "pspReference" => reference,
1110
"eventDate" => "2013-10-21T14:45:45.93Z",
@@ -22,12 +21,14 @@
2221
"live" => "false" }
2322
end
2423

24+
let!(:order) { create :completed_order_with_totals }
25+
2526
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
2829
end
2930

30-
let(:payment_method) { create :hpp_gateway }
31+
let!(:payment_method) { create :hpp_gateway }
3132

3233
let(:reference) { "8513823667306210" }
3334

spec/controllers/spree/adyen_redirect_controller_spec.rb

+44-58
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
)
1313
end
1414

15-
let(:store) { Spree::Store.default }
16-
let(:gateway) { create :hpp_gateway }
15+
let!(:store) { Spree::Store.default }
16+
let!(:gateway) { create :hpp_gateway }
1717

1818
before do
1919
allow(controller).to receive(:check_signature)
@@ -24,6 +24,7 @@
2424

2525
let(:psp_reference) { "8813824003752247" }
2626
let(:payment_method) { "amex" }
27+
let(:merchantReturnData) { "#{order.guest_token}|#{gateway.id}" }
2728
let(:params) do
2829
{ merchantReference: order.number,
2930
skinCode: "xxxxxxxx",
@@ -35,28 +36,20 @@
3536
merchantReturnData: merchantReturnData
3637
}
3738
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
4539

4640
shared_examples "payment is successful" do
4741
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
6053
end
6154

6255
it "redirects to the order complete page" do
@@ -65,10 +58,8 @@
6558
end
6659

6760
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
7263
end
7364

7465
context "and the order cannot complete" do
@@ -80,9 +71,20 @@
8071
end
8172
end
8273

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+
8385
context "when the payment is AUTHORISED" do
8486
include_examples "payment is successful"
85-
include_examples "payments are pending"
87+
8688
let(:auth_result) { "AUTHORISED" }
8789

8890
context "and the authorisation notification has already been received" do
@@ -91,56 +93,40 @@
9193
let(:notification) do
9294
create(
9395
:notification,
94-
notification_type,
96+
:auth,
97+
processed: true,
9598
psp_reference: psp_reference,
9699
merchant_reference: order.number)
97100
end
98101

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)
101106

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)
109108

110-
context "and payment method is sofort" do
111-
let(:notification_type) { :sofort_auth }
112-
include_examples "auth received"
109+
order.complete
113110
end
114111

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 }
119113

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+
)
123119
end
120+
121+
include_examples "payment is successful"
124122
end
125123
end
126124

127125
context "when the payment is PENDING" do
128126
include_examples "payment is successful"
129-
include_examples "payments are pending"
130127
let(:auth_result) { "PENDING" }
131128
end
132129

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-
144130
context "when the payment is CANCELLED" do
145131
include_examples "payment is not successful"
146132
let(:auth_result) { "CANCELLED" }

spec/factories/spree_payment.rb

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
before :create do |record, _|
88
# these associations/keys are awful and are making this difficult
9+
record.response_code = record.source.psp_reference
910
record.source.order = record.order
1011
record.source.merchant_reference = record.order.number
1112
end

0 commit comments

Comments
 (0)