solidus_afterpay 0.1.0
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/.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
|