solidus_afterpay 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +41 -0
- data/.gem_release.yml +5 -0
- data/.github/stale.yml +17 -0
- data/.github_changelog_generator +2 -0
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/.rubocop.yml +14 -0
- data/CHANGELOG.md +1 -0
- data/Gemfile +33 -0
- data/LICENSE +202 -0
- data/README.md +175 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/solidus_afterpay/afterpay_checkout.js +131 -0
- data/app/assets/javascripts/spree/backend/solidus_afterpay.js +2 -0
- data/app/assets/javascripts/spree/frontend/solidus_afterpay.js +4 -0
- data/app/assets/stylesheets/spree/backend/solidus_afterpay.css +4 -0
- data/app/assets/stylesheets/spree/frontend/solidus_afterpay.css +4 -0
- data/app/controllers/solidus_afterpay/base_controller.rb +30 -0
- data/app/controllers/solidus_afterpay/callbacks_controller.rb +71 -0
- data/app/controllers/solidus_afterpay/checkouts_controller.rb +47 -0
- data/app/decorators/controllers/solidus_afterpay/spree/checkout_controller_decorator.rb +13 -0
- data/app/decorators/models/solidus_afterpay/spree/order_decorator.rb +15 -0
- data/app/helpers/solidus_afterpay/afterpay_helper.rb +15 -0
- data/app/models/solidus_afterpay/gateway.rb +157 -0
- data/app/models/solidus_afterpay/order_component_builder.rb +97 -0
- data/app/models/solidus_afterpay/payment_method.rb +56 -0
- data/app/models/solidus_afterpay/payment_source.rb +23 -0
- data/app/models/solidus_afterpay/user_agent_generator.rb +35 -0
- data/app/views/solidus_afterpay/_afterpay_javascript.html.erb +5 -0
- data/app/views/spree/admin/payments/source_forms/_afterpay.html.erb +1 -0
- data/app/views/spree/admin/payments/source_views/_afterpay.html.erb +0 -0
- data/app/views/spree/api/payments/source_views/_afterpay.json.jbuilder +3 -0
- data/app/views/spree/checkout/payment/_afterpay.html.erb +9 -0
- data/app/views/spree/shared/_afterpay_messaging.html.erb +13 -0
- data/bin/console +17 -0
- data/bin/rails +7 -0
- data/bin/rails-engine +13 -0
- data/bin/rails-sandbox +16 -0
- data/bin/rake +7 -0
- data/bin/sandbox +86 -0
- data/bin/setup +8 -0
- data/codecov.yml +2 -0
- data/config/locales/en.yml +12 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20210813142725_create_solidus_afterpay_payment_sources.rb +12 -0
- data/lib/generators/solidus_afterpay/install/install_generator.rb +43 -0
- data/lib/generators/solidus_afterpay/install/templates/initializer.rb +5 -0
- data/lib/solidus_afterpay/configuration.rb +25 -0
- data/lib/solidus_afterpay/engine.rb +25 -0
- data/lib/solidus_afterpay/testing_support/factories.rb +20 -0
- data/lib/solidus_afterpay/version.rb +5 -0
- data/lib/solidus_afterpay.rb +5 -0
- data/solidus_afterpay.gemspec +38 -0
- data/spec/fixtures/vcr_casettes/create_checkout/invalid.yml +65 -0
- data/spec/fixtures/vcr_casettes/create_checkout/valid.yml +64 -0
- data/spec/fixtures/vcr_casettes/credit/invalid.yml +61 -0
- data/spec/fixtures/vcr_casettes/credit/valid.yml +63 -0
- data/spec/fixtures/vcr_casettes/deferred/authorize/declined_payment.yml +120 -0
- data/spec/fixtures/vcr_casettes/deferred/authorize/invalid.yml +61 -0
- data/spec/fixtures/vcr_casettes/deferred/authorize/valid.yml +120 -0
- data/spec/fixtures/vcr_casettes/deferred/capture/invalid.yml +61 -0
- data/spec/fixtures/vcr_casettes/deferred/capture/valid.yml +140 -0
- data/spec/fixtures/vcr_casettes/deferred/void/invalid.yml +61 -0
- data/spec/fixtures/vcr_casettes/deferred/void/valid.yml +137 -0
- data/spec/fixtures/vcr_casettes/find_payment/invalid.yml +61 -0
- data/spec/fixtures/vcr_casettes/find_payment/valid.yml +140 -0
- data/spec/fixtures/vcr_casettes/immediate/capture/declined_payment.yml +120 -0
- data/spec/fixtures/vcr_casettes/immediate/capture/invalid.yml +61 -0
- data/spec/fixtures/vcr_casettes/immediate/capture/valid.yml +134 -0
- data/spec/fixtures/vcr_casettes/retrieve_configuration/valid.yml +67 -0
- data/spec/helpers/solidus_afterpay/afterpay_helper_spec.rb +23 -0
- data/spec/models/solidus_afterpay/gateway_spec.rb +418 -0
- data/spec/models/solidus_afterpay/order_component_builder_spec.rb +137 -0
- data/spec/models/solidus_afterpay/payment_method_spec.rb +143 -0
- data/spec/models/solidus_afterpay/payment_source_spec.rb +61 -0
- data/spec/models/solidus_afterpay/user_agent_generator_spec.rb +22 -0
- data/spec/models/spree/order_spec.rb +158 -0
- data/spec/requests/solidus_afterpay/callbacks_controller_spec.rb +127 -0
- data/spec/requests/solidus_afterpay/checkouts_controller_spec.rb +190 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/auth.rb +15 -0
- data/spec/support/preferences.rb +33 -0
- data/spec/support/vcr.rb +18 -0
- metadata +249 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://global-api-sandbox.afterpay.com/v2/payments/capture
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '{"token":"INVALID_TOKEN","merchantReference":null,"amount":null}'
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- SolidusAfterpay/0.0.1 (Solidus/3.0.1; Ruby/2.6.6; Merchant/100101481) https://
|
12
|
+
Authorization:
|
13
|
+
- Basic <ENCODED_AUTH_HEADER>
|
14
|
+
Content-Type:
|
15
|
+
- application/json
|
16
|
+
Accept-Encoding:
|
17
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
18
|
+
Accept:
|
19
|
+
- "*/*"
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 402
|
23
|
+
message: Payment Required
|
24
|
+
headers:
|
25
|
+
Date:
|
26
|
+
- Wed, 25 Aug 2021 14:55:10 GMT
|
27
|
+
Content-Type:
|
28
|
+
- application/json
|
29
|
+
Content-Length:
|
30
|
+
- '163'
|
31
|
+
Connection:
|
32
|
+
- keep-alive
|
33
|
+
Http-Correlation-Id:
|
34
|
+
- 2m54dxammo5yivs22xpqy5ymp4
|
35
|
+
X-Envoy-Upstream-Service-Time:
|
36
|
+
- '65'
|
37
|
+
Cf-Cache-Status:
|
38
|
+
- DYNAMIC
|
39
|
+
Expect-Ct:
|
40
|
+
- max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
41
|
+
Set-Cookie:
|
42
|
+
- __cf_bm=59dde99429147353c566297400188a205dd1c569-1629903310-1800-AW+vk29NN4QPXNzSzYsYy0du6wT4ILfy1NY9yfxHlmnG/9bR/OuBxCflDZ2b/f2kBF4GjBQ3d9y4Tov4NdIy990scvazqSnXwt/2X2iaTMBt;
|
43
|
+
path=/; expires=Wed, 25-Aug-21 15:25:10 GMT; domain=.afterpay.com; HttpOnly;
|
44
|
+
Secure; SameSite=None
|
45
|
+
Strict-Transport-Security:
|
46
|
+
- max-age=31536000; includeSubDomains; preload
|
47
|
+
Server:
|
48
|
+
- cloudflare
|
49
|
+
Cf-Ray:
|
50
|
+
- 6845a8e67fc5cd42-FCO
|
51
|
+
body:
|
52
|
+
encoding: UTF-8
|
53
|
+
string: |-
|
54
|
+
{
|
55
|
+
"errorCode" : "invalid_token",
|
56
|
+
"errorId" : "da76c8c79d41e92b",
|
57
|
+
"message" : "Cannot complete payment, expired or invalid token.",
|
58
|
+
"httpStatusCode" : 402
|
59
|
+
}
|
60
|
+
recorded_at: Wed, 25 Aug 2021 14:55:10 GMT
|
61
|
+
recorded_with: VCR 6.0.0
|
@@ -0,0 +1,134 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://global-api-sandbox.afterpay.com/v2/payments/capture
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '{"token":"002.nt7e0ioqj00fh0ua1nbqcj6vcn9obtfsglqvrj9ijpo3edfc","merchantReference":null,"amount":null}'
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- SolidusAfterpay/0.0.1 (Solidus/3.0.1; Ruby/2.6.6; Merchant/100101481) https://
|
12
|
+
Authorization:
|
13
|
+
- Basic <ENCODED_AUTH_HEADER>
|
14
|
+
Content-Type:
|
15
|
+
- application/json
|
16
|
+
Accept-Encoding:
|
17
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
18
|
+
Accept:
|
19
|
+
- "*/*"
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 201
|
23
|
+
message: Created
|
24
|
+
headers:
|
25
|
+
Date:
|
26
|
+
- Wed, 25 Aug 2021 14:57:00 GMT
|
27
|
+
Content-Type:
|
28
|
+
- application/json
|
29
|
+
Content-Length:
|
30
|
+
- '1853'
|
31
|
+
Connection:
|
32
|
+
- keep-alive
|
33
|
+
Http-Correlation-Id:
|
34
|
+
- aybxyh56w3cheadll4m2tdy7q4
|
35
|
+
X-Envoy-Upstream-Service-Time:
|
36
|
+
- '52'
|
37
|
+
Cf-Cache-Status:
|
38
|
+
- DYNAMIC
|
39
|
+
Expect-Ct:
|
40
|
+
- max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
41
|
+
Set-Cookie:
|
42
|
+
- __cf_bm=559bbd7450eb18d65d238a3f883de3d462989653-1629903420-1800-AZAsHum/8AJvXaC7SsSFuucq7UTebHQq8yI3EfbLOvY3g5R5nTPNo4NpNe4AJsWcz0yS5nit62GxjFvF2gp2C0JXbMys7O8SzRIgMSaMo0Lz;
|
43
|
+
path=/; expires=Wed, 25-Aug-21 15:27:00 GMT; domain=.afterpay.com; HttpOnly;
|
44
|
+
Secure; SameSite=None
|
45
|
+
Strict-Transport-Security:
|
46
|
+
- max-age=31536000; includeSubDomains; preload
|
47
|
+
Server:
|
48
|
+
- cloudflare
|
49
|
+
Cf-Ray:
|
50
|
+
- 6845ab96ea29cd2e-FCO
|
51
|
+
body:
|
52
|
+
encoding: UTF-8
|
53
|
+
string: |-
|
54
|
+
{
|
55
|
+
"id" : "100101768366",
|
56
|
+
"token" : "002.nt7e0ioqj00fh0ua1nbqcj6vcn9obtfsglqvrj9ijpo3edfc",
|
57
|
+
"status" : "APPROVED",
|
58
|
+
"created" : "2021-08-24T15:30:14.497Z",
|
59
|
+
"originalAmount" : {
|
60
|
+
"amount" : "22.04",
|
61
|
+
"currency" : "USD"
|
62
|
+
},
|
63
|
+
"openToCaptureAmount" : {
|
64
|
+
"amount" : "0.00",
|
65
|
+
"currency" : "USD"
|
66
|
+
},
|
67
|
+
"paymentState" : "CAPTURED",
|
68
|
+
"merchantReference" : "R903458715",
|
69
|
+
"refunds" : [ {
|
70
|
+
"amount" : {
|
71
|
+
"amount" : "0.10",
|
72
|
+
"currency" : "USD"
|
73
|
+
},
|
74
|
+
"refundId" : "1933891",
|
75
|
+
"refundedAt" : "2021-08-25T10:09:30.819Z"
|
76
|
+
}, {
|
77
|
+
"amount" : {
|
78
|
+
"amount" : "10.00",
|
79
|
+
"currency" : "USD"
|
80
|
+
},
|
81
|
+
"refundId" : "1934419",
|
82
|
+
"refundedAt" : "2021-08-25T14:56:33.753Z"
|
83
|
+
} ],
|
84
|
+
"orderDetails" : {
|
85
|
+
"consumer" : {
|
86
|
+
"phoneNumber" : "",
|
87
|
+
"givenNames" : "Christian",
|
88
|
+
"surname" : "Rimondi",
|
89
|
+
"email" : "admin@example.com"
|
90
|
+
},
|
91
|
+
"billing" : {
|
92
|
+
"name" : "Christian Rimondi",
|
93
|
+
"line1" : "1313 Broadway",
|
94
|
+
"line2" : "",
|
95
|
+
"area1" : "New York",
|
96
|
+
"region" : "NY",
|
97
|
+
"postcode" : "10001",
|
98
|
+
"phoneNumber" : "12524264421"
|
99
|
+
},
|
100
|
+
"shipping" : {
|
101
|
+
"name" : "Christian Rimondi",
|
102
|
+
"line1" : "1313 Broadway",
|
103
|
+
"line2" : "",
|
104
|
+
"area1" : "New York",
|
105
|
+
"region" : "NY",
|
106
|
+
"postcode" : "10001",
|
107
|
+
"phoneNumber" : "12524264421"
|
108
|
+
},
|
109
|
+
"courier" : { },
|
110
|
+
"items" : [ {
|
111
|
+
"name" : "Ruby Tote",
|
112
|
+
"sku" : "RUB-TOT01",
|
113
|
+
"quantity" : 1,
|
114
|
+
"price" : {
|
115
|
+
"amount" : "15.99",
|
116
|
+
"currency" : "USD"
|
117
|
+
}
|
118
|
+
} ],
|
119
|
+
"discounts" : [ ]
|
120
|
+
},
|
121
|
+
"events" : [ {
|
122
|
+
"id" : "1xBCPLdc9Opx4SQY8PeMXDtemsV",
|
123
|
+
"created" : "2021-08-24T15:31:09.859Z",
|
124
|
+
"expires" : null,
|
125
|
+
"type" : "CAPTURED",
|
126
|
+
"amount" : {
|
127
|
+
"amount" : "22.04",
|
128
|
+
"currency" : "USD"
|
129
|
+
},
|
130
|
+
"paymentEventMerchantReference" : null
|
131
|
+
} ]
|
132
|
+
}
|
133
|
+
recorded_at: Wed, 25 Aug 2021 14:57:00 GMT
|
134
|
+
recorded_with: VCR 6.0.0
|
@@ -0,0 +1,67 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://global-api-sandbox.afterpay.com/v2/configuration
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- SolidusAfterpay/0.0.1 (Solidus/3.0.1; Ruby/2.6.6; Merchant/100101481) https://
|
12
|
+
Authorization:
|
13
|
+
- Basic <ENCODED_AUTH_HEADER>
|
14
|
+
Accept-Encoding:
|
15
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
16
|
+
Accept:
|
17
|
+
- "*/*"
|
18
|
+
response:
|
19
|
+
status:
|
20
|
+
code: 200
|
21
|
+
message: OK
|
22
|
+
headers:
|
23
|
+
Date:
|
24
|
+
- Wed, 25 Aug 2021 14:56:02 GMT
|
25
|
+
Content-Type:
|
26
|
+
- application/json
|
27
|
+
Content-Length:
|
28
|
+
- '151'
|
29
|
+
Connection:
|
30
|
+
- keep-alive
|
31
|
+
Cache-Control:
|
32
|
+
- private, no-transform, max-age=1800
|
33
|
+
Http-Correlation-Id:
|
34
|
+
- u73bnoiwo265mfjp7rdrdzfsxu
|
35
|
+
Vary:
|
36
|
+
- Accept-Encoding
|
37
|
+
X-Envoy-Upstream-Service-Time:
|
38
|
+
- '10'
|
39
|
+
Cf-Cache-Status:
|
40
|
+
- DYNAMIC
|
41
|
+
Expect-Ct:
|
42
|
+
- max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
|
43
|
+
Set-Cookie:
|
44
|
+
- __cf_bm=ebd26f3256f7c877137bf42087fa5550fed5965e-1629903362-1800-AcvTlx8l0pTK7CNPb9AuSNR0Gno8NZxAq/ln/lsb1QfRDTKngcaw3pe7umDu3S2LL583bb0jFHJGa/Lbzh3XP31WX5N+vTDZeMa33NSOwYtV;
|
45
|
+
path=/; expires=Wed, 25-Aug-21 15:26:02 GMT; domain=.afterpay.com; HttpOnly;
|
46
|
+
Secure; SameSite=None
|
47
|
+
Strict-Transport-Security:
|
48
|
+
- max-age=31536000; includeSubDomains; preload
|
49
|
+
Server:
|
50
|
+
- cloudflare
|
51
|
+
Cf-Ray:
|
52
|
+
- 6845aa2e5871cd32-FCO
|
53
|
+
body:
|
54
|
+
encoding: UTF-8
|
55
|
+
string: |-
|
56
|
+
{
|
57
|
+
"minimumAmount" : {
|
58
|
+
"amount" : "1.00",
|
59
|
+
"currency" : "USD"
|
60
|
+
},
|
61
|
+
"maximumAmount" : {
|
62
|
+
"amount" : "1000.00",
|
63
|
+
"currency" : "USD"
|
64
|
+
}
|
65
|
+
}
|
66
|
+
recorded_at: Wed, 25 Aug 2021 14:56:02 GMT
|
67
|
+
recorded_with: VCR 6.0.0
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe SolidusAfterpay::AfterpayHelper, type: :helper do
|
4
|
+
describe '#include_afterpay_js' do
|
5
|
+
subject { helper.include_afterpay_js(test_mode: test_mode) }
|
6
|
+
|
7
|
+
context 'when test_mode is false' do
|
8
|
+
let(:test_mode) { false }
|
9
|
+
|
10
|
+
it 'includes the production javascript' do
|
11
|
+
is_expected.to eq('<script src="https://portal.afterpay.com/afterpay.js"></script>')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'when test_mode is true' do
|
16
|
+
let(:test_mode) { true }
|
17
|
+
|
18
|
+
it 'includes the sandbox javascript' do
|
19
|
+
is_expected.to eq('<script src="https://portal.sandbox.afterpay.com/afterpay.js"></script>')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,418 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe SolidusAfterpay::Gateway do
|
4
|
+
let(:gateway) { described_class.new(options) }
|
5
|
+
let(:options) do
|
6
|
+
{
|
7
|
+
merchant_id: ENV.fetch('AFTERPAY_MERCHANT_ID', 'dummy_merchant_id'),
|
8
|
+
secret_key: ENV.fetch('AFTERPAY_SECRET_KEY', 'dummy_secret_key'),
|
9
|
+
test_mode: true
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#authorize' do
|
14
|
+
subject(:response) { gateway.authorize(amount, payment_source, gateway_options) }
|
15
|
+
|
16
|
+
let(:order_token) { '002.m6d9jkrtv1p0j4jqslklhfq9k4nl54jo2530d58kf6snpqq1' }
|
17
|
+
let(:deferred?) { false }
|
18
|
+
let(:payment_method) { build(:afterpay_payment_method, preferred_deferred: deferred?) }
|
19
|
+
|
20
|
+
let(:amount) { 1000 }
|
21
|
+
let(:payment_source) { build(:afterpay_payment_source, token: order_token, payment_method: payment_method) }
|
22
|
+
let(:gateway_options) { {} }
|
23
|
+
|
24
|
+
context 'with the immediate flow' do
|
25
|
+
it 'returns a successful response' do
|
26
|
+
is_expected.to be_success
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with the deferred flow' do
|
31
|
+
let(:deferred?) { true }
|
32
|
+
|
33
|
+
context 'with valid params', vcr: 'deferred/authorize/valid' do
|
34
|
+
it 'authorize the afterpay payment with the order_token' do
|
35
|
+
is_expected.to be_success
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'with an invalid token', vcr: 'deferred/authorize/invalid' do
|
40
|
+
let(:order_token) { 'INVALID_TOKEN' }
|
41
|
+
|
42
|
+
it 'returns an unsuccesfull response' do
|
43
|
+
is_expected.not_to be_success
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns the error message from Afterpay in the response' do
|
47
|
+
expect(response.message).to eq('Cannot complete payment, expired or invalid token.')
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns the error_code from Afterpay in the response' do
|
51
|
+
expect(response.error_code).to eq('invalid_token')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'with an invalid credit card', vcr: 'deferred/authorize/declined_payment' do
|
56
|
+
let(:order_token) { '002.ijlqnvko1o4ou45uabplrl9pqao8u2v52njs2972r24hje65' }
|
57
|
+
|
58
|
+
it 'returns an unsuccesfull response' do
|
59
|
+
is_expected.not_to be_success
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'returns the error message from Afterpay in the response' do
|
63
|
+
expect(response.message).to eq(
|
64
|
+
'Payment declined. Please contact the Afterpay Customer Service team for more information.'
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns the error_code from Afterpay in the response' do
|
69
|
+
expect(response.error_code).to eq('payment_declined')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#capture' do
|
76
|
+
subject(:response) { gateway.capture(amount, response_code, gateway_options) }
|
77
|
+
|
78
|
+
let(:order_token) { '002.nt7e0ioqj00fh0ua1nbqcj6vcn9obtfsglqvrj9ijpo3edfc' }
|
79
|
+
let(:deferred?) { false }
|
80
|
+
let(:payment_source) { build(:afterpay_payment_source, token: order_token) }
|
81
|
+
let(:payment_method) { build(:afterpay_payment_method, preferred_deferred: deferred?) }
|
82
|
+
let(:payment) { build(:afterpay_payment, source: payment_source, payment_method: payment_method) }
|
83
|
+
|
84
|
+
let(:amount) { 1000 }
|
85
|
+
let(:response_code) { '100101782114' }
|
86
|
+
let(:gateway_options) { { originator: payment, currency: 'USD' } }
|
87
|
+
|
88
|
+
context 'with the immediate flow' do
|
89
|
+
context 'with valid params', vcr: 'immediate/capture/valid' do
|
90
|
+
it 'captures the afterpay payment with the order_token' do
|
91
|
+
is_expected.to be_success
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'with an invalid token', vcr: 'immediate/capture/invalid' do
|
96
|
+
let(:order_token) { 'INVALID_TOKEN' }
|
97
|
+
|
98
|
+
it 'returns an unsuccesfull response' do
|
99
|
+
is_expected.not_to be_success
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'returns the error message from Afterpay in the response' do
|
103
|
+
expect(response.message).to eq('Cannot complete payment, expired or invalid token.')
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'returns the error_code from Afterpay in the response' do
|
107
|
+
expect(response.error_code).to eq('invalid_token')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'with an invalid credit card', vcr: 'immediate/capture/declined_payment' do
|
112
|
+
let(:order_token) { '002.kj16plsn63eqfacueg767cp7l34e9ph5tms4ql14o2iid7l1' }
|
113
|
+
|
114
|
+
it 'returns an unsuccesfull response' do
|
115
|
+
is_expected.not_to be_success
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'returns the error message from Afterpay in the response' do
|
119
|
+
expect(response.message).to eq(
|
120
|
+
'Payment declined. Please contact the Afterpay Customer Service team for more information.'
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'returns the error_code from Afterpay in the response' do
|
125
|
+
expect(response.error_code).to eq('payment_declined')
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'with the deferred flow' do
|
131
|
+
let(:deferred?) { true }
|
132
|
+
|
133
|
+
context 'with valid params', vcr: 'deferred/capture/valid' do
|
134
|
+
it 'captures the afterpay payment with the order_id' do
|
135
|
+
is_expected.to be_success
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'with an invalid payment ID', vcr: 'deferred/capture/invalid' do
|
140
|
+
let(:response_code) { 'INVALID_RESPONSE_CODE' }
|
141
|
+
|
142
|
+
it 'returns an unsuccesfull response' do
|
143
|
+
is_expected.not_to be_success
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'returns the error message from Afterpay in the response' do
|
147
|
+
expect(response.message).to eq('Afterpay payment ID not found.')
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'returns the error_code from Afterpay in the response' do
|
151
|
+
expect(response.error_code).to eq('not_found')
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '#purchase' do
|
158
|
+
subject(:response) { gateway.purchase(amount, payment_source, gateway_options) }
|
159
|
+
|
160
|
+
let(:order_token) { '002.nt7e0ioqj00fh0ua1nbqcj6vcn9obtfsglqvrj9ijpo3edfc' }
|
161
|
+
let(:deferred?) { false }
|
162
|
+
let(:payment_method) { build(:afterpay_payment_method, preferred_deferred: deferred?) }
|
163
|
+
let(:payment) { build(:afterpay_payment, source: payment_source, payment_method: payment_method) }
|
164
|
+
|
165
|
+
let(:amount) { 1000 }
|
166
|
+
let(:payment_source) { build(:afterpay_payment_source, token: order_token, payment_method: payment_method) }
|
167
|
+
let(:gateway_options) { { originator: payment, currency: 'USD' } }
|
168
|
+
|
169
|
+
context 'with the immediate flow' do
|
170
|
+
context 'with valid params', vcr: 'immediate/capture/valid' do
|
171
|
+
it 'authorize and captures the afterpay payment with the order_token' do
|
172
|
+
is_expected.to be_success
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'with an invalid token', vcr: 'immediate/capture/invalid' do
|
177
|
+
let(:order_token) { 'INVALID_TOKEN' }
|
178
|
+
|
179
|
+
it 'returns an unsuccesfull response' do
|
180
|
+
is_expected.not_to be_success
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'returns the error message from Afterpay in the response' do
|
184
|
+
expect(response.message).to eq('Cannot complete payment, expired or invalid token.')
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'returns the error_code from Afterpay in the response' do
|
188
|
+
expect(response.error_code).to eq('invalid_token')
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'with an invalid credit card', vcr: 'immediate/capture/declined_payment' do
|
193
|
+
let(:order_token) { '002.kj16plsn63eqfacueg767cp7l34e9ph5tms4ql14o2iid7l1' }
|
194
|
+
|
195
|
+
it 'returns an unsuccesfull response' do
|
196
|
+
is_expected.not_to be_success
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'returns the error message from Afterpay in the response' do
|
200
|
+
expect(response.message).to eq(
|
201
|
+
'Payment declined. Please contact the Afterpay Customer Service team for more information.'
|
202
|
+
)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'returns the error_code from Afterpay in the response' do
|
206
|
+
expect(response.error_code).to eq('payment_declined')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context 'with the deferred flow' do
|
212
|
+
let(:deferred?) { true }
|
213
|
+
|
214
|
+
context 'with valid params', vcr: 'deferred/authorize/valid' do
|
215
|
+
it 'authorize and captures the afterpay payment with the order_token' do
|
216
|
+
VCR.use_cassette('deferred/capture/valid') do
|
217
|
+
is_expected.to be_success
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context 'with an invalid token', vcr: 'deferred/authorize/invalid' do
|
223
|
+
let(:order_token) { 'INVALID_TOKEN' }
|
224
|
+
|
225
|
+
it 'returns an unsuccesfull response' do
|
226
|
+
is_expected.not_to be_success
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'returns the error message from Afterpay in the response' do
|
230
|
+
expect(response.message).to eq('Cannot complete payment, expired or invalid token.')
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'returns the error_code from Afterpay in the response' do
|
234
|
+
expect(response.error_code).to eq('invalid_token')
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context 'with an invalid credit card', vcr: 'deferred/authorize/declined_payment' do
|
239
|
+
let(:order_token) { '002.kj16plsn63eqfacueg767cp7l34e9ph5tms4ql14o2iid7l1' }
|
240
|
+
|
241
|
+
it 'returns an unsuccesfull response' do
|
242
|
+
is_expected.not_to be_success
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'returns the error message from Afterpay in the response' do
|
246
|
+
expect(response.message).to eq(
|
247
|
+
'Payment declined. Please contact the Afterpay Customer Service team for more information.'
|
248
|
+
)
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'returns the error_code from Afterpay in the response' do
|
252
|
+
expect(response.error_code).to eq('payment_declined')
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
describe '#credit' do
|
259
|
+
subject(:response) { gateway.credit(amount, response_code, gateway_options) }
|
260
|
+
|
261
|
+
let(:payment) { build(:afterpay_payment) }
|
262
|
+
let(:refund) { build(:refund, payment: payment) }
|
263
|
+
|
264
|
+
let(:amount) { 1000 }
|
265
|
+
let(:response_code) { '100101768366' }
|
266
|
+
let(:gateway_options) { { originator: refund } }
|
267
|
+
|
268
|
+
context 'with valid params', vcr: 'credit/valid' do
|
269
|
+
it 'refunds the amount using the response_code' do
|
270
|
+
is_expected.to be_success
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
context 'with an invalid response_code', vcr: 'credit/invalid' do
|
275
|
+
let(:response_code) { 'INVALID_RESPONSE_CODE' }
|
276
|
+
|
277
|
+
it 'returns an unsuccesfull response' do
|
278
|
+
is_expected.not_to be_success
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'returns the error message from Afterpay in the response' do
|
282
|
+
expect(response.message).to eq('Afterpay payment ID not found.')
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'returns the error_code from Afterpay in the response' do
|
286
|
+
expect(response.error_code).to eq('not_found')
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
describe '#void' do
|
292
|
+
subject(:response) { gateway.void(response_code, gateway_options) }
|
293
|
+
|
294
|
+
let(:deferred?) { false }
|
295
|
+
let(:amount) { 10 }
|
296
|
+
let(:payment_method) { build(:afterpay_payment_method, preferred_deferred: deferred?) }
|
297
|
+
let(:payment) { build(:afterpay_payment, payment_method: payment_method, amount: amount) }
|
298
|
+
|
299
|
+
let(:response_code) { '100101785223' }
|
300
|
+
let(:gateway_options) { { originator: payment, currency: 'USD' } }
|
301
|
+
|
302
|
+
context 'with the immediate flow' do
|
303
|
+
it 'returns an unsuccessful response' do
|
304
|
+
is_expected.not_to be_success
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'returns the error message from Afterpay in the response' do
|
308
|
+
expect(response.message).to eq("Transaction can't be voided")
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'returns the error_code from Afterpay in the response' do
|
312
|
+
expect(response.error_code).to eq('void_not_allowed')
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
context 'with the deferred flow' do
|
317
|
+
let(:deferred?) { true }
|
318
|
+
|
319
|
+
context 'with valid params', vcr: 'deferred/void/valid' do
|
320
|
+
it 'voids the payment using the response_code' do
|
321
|
+
is_expected.to be_success
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
context 'with an invalid response_code', vcr: 'deferred/void/invalid' do
|
326
|
+
let(:response_code) { 'INVALID_RESPONSE_CODE' }
|
327
|
+
|
328
|
+
it 'returns an unsuccesfull response' do
|
329
|
+
is_expected.not_to be_success
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'returns the error message from Afterpay in the response' do
|
333
|
+
expect(response.message).to eq('Afterpay payment ID not found.')
|
334
|
+
end
|
335
|
+
|
336
|
+
it 'returns the error_code from Afterpay in the response' do
|
337
|
+
expect(response.error_code).to eq('not_found')
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
describe '#create_checkout' do
|
344
|
+
subject(:response) { gateway.create_checkout(order, gateway_options) }
|
345
|
+
|
346
|
+
let(:redirect_confirm_url) { 'https://merchantsite.com/confirm' }
|
347
|
+
let(:redirect_cancel_url) { 'https://merchantsite.com/cancel' }
|
348
|
+
|
349
|
+
let(:order) { build(:order_with_line_items) }
|
350
|
+
let(:gateway_options) { { redirect_confirm_url: redirect_confirm_url, redirect_cancel_url: redirect_cancel_url } }
|
351
|
+
|
352
|
+
context 'with valid params', vcr: 'create_checkout/valid' do
|
353
|
+
it 'creates the checkout' do
|
354
|
+
is_expected.to be_success
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'returns the order_token' do
|
358
|
+
expect(response.params).to include('token')
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
context 'with an invalid params', vcr: 'create_checkout/invalid' do
|
363
|
+
let(:redirect_confirm_url) { 'INVALID_URL' }
|
364
|
+
|
365
|
+
it 'returns an unsuccesfull response' do
|
366
|
+
is_expected.not_to be_success
|
367
|
+
end
|
368
|
+
|
369
|
+
it 'returns the error message from Afterpay in the response' do
|
370
|
+
expect(response.message).to eq('merchant.redirectConfirmUrl must be a valid URL')
|
371
|
+
end
|
372
|
+
|
373
|
+
it 'returns the error_code from Afterpay in the response' do
|
374
|
+
expect(response.error_code).to eq('invalid_object')
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
describe '#find_payment' do
|
380
|
+
subject(:response) { gateway.find_payment(order_id: order_id) }
|
381
|
+
|
382
|
+
let(:order_id) { '100101785223' }
|
383
|
+
|
384
|
+
context 'with valid params', vcr: 'find_payment/valid' do
|
385
|
+
it 'retrieves the Afterpay payment' do
|
386
|
+
expect(response).to include('id' => order_id)
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
context 'with an invalid params', vcr: 'find_payment/invalid' do
|
391
|
+
let(:order_id) { 'INVALID_ORDER_ID' }
|
392
|
+
|
393
|
+
it 'returns nil' do
|
394
|
+
is_expected.to be_nil
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
describe '#retrieve_configuration' do
|
400
|
+
subject(:response) { gateway.retrieve_configuration }
|
401
|
+
|
402
|
+
context 'with valid response', vcr: 'retrieve_configuration/valid' do
|
403
|
+
it 'retrieves the afterpay configuration' do
|
404
|
+
expect(response).to include('minimumAmount', 'maximumAmount')
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
context 'with invalid response' do
|
409
|
+
before do
|
410
|
+
allow(::Afterpay::API::Configuration::Retrieve).to receive(:call).and_raise(::Afterpay::BaseError.new(nil))
|
411
|
+
end
|
412
|
+
|
413
|
+
it 'retrieves the afterpay configuration' do
|
414
|
+
expect(response).to be_nil
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|