solidus_paypal_braintree 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +4 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +70 -1
- data/Gemfile +2 -5
- data/app/models/solidus_paypal_braintree/gateway.rb +2 -0
- data/app/models/solidus_paypal_braintree/transaction.rb +2 -2
- data/app/models/solidus_paypal_braintree/transaction_import.rb +2 -2
- data/lib/solidus_paypal_braintree/engine.rb +5 -3
- data/lib/solidus_paypal_braintree/version.rb +1 -1
- data/solidus_paypal_braintree.gemspec +0 -1
- metadata +7 -125
- data/spec/controllers/solidus_paypal_braintree/checkouts_controller_spec.rb +0 -99
- data/spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb +0 -55
- data/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb +0 -73
- data/spec/controllers/solidus_paypal_braintree/transactions_controller_spec.rb +0 -183
- data/spec/features/backend/configuration_spec.rb +0 -23
- data/spec/features/backend/new_payment_spec.rb +0 -137
- data/spec/features/frontend/braintree_credit_card_checkout_spec.rb +0 -191
- data/spec/features/frontend/paypal_checkout_spec.rb +0 -166
- data/spec/features/frontend/venmo_checkout_spec.rb +0 -189
- data/spec/fixtures/cassettes/admin/invalid_credit_card.yml +0 -63
- data/spec/fixtures/cassettes/admin/resubmit_credit_card.yml +0 -352
- data/spec/fixtures/cassettes/admin/valid_credit_card.yml +0 -412
- data/spec/fixtures/cassettes/braintree/create_profile.yml +0 -71
- data/spec/fixtures/cassettes/braintree/generate_token.yml +0 -63
- data/spec/fixtures/cassettes/braintree/token.yml +0 -63
- data/spec/fixtures/cassettes/checkout/invalid_credit_card.yml +0 -63
- data/spec/fixtures/cassettes/checkout/resubmit_credit_card.yml +0 -216
- data/spec/fixtures/cassettes/checkout/update.yml +0 -71
- data/spec/fixtures/cassettes/checkout/valid_credit_card.yml +0 -156
- data/spec/fixtures/cassettes/checkout/valid_venmo_transaction.yml +0 -599
- data/spec/fixtures/cassettes/gateway/authorize/credit_card/address.yml +0 -86
- data/spec/fixtures/cassettes/gateway/authorize/merchant_account/EUR.yml +0 -154
- data/spec/fixtures/cassettes/gateway/authorize/paypal/EUR.yml +0 -90
- data/spec/fixtures/cassettes/gateway/authorize/paypal/address.yml +0 -90
- data/spec/fixtures/cassettes/gateway/authorize.yml +0 -86
- data/spec/fixtures/cassettes/gateway/authorized_transaction.yml +0 -73
- data/spec/fixtures/cassettes/gateway/cancel/missing.yml +0 -63
- data/spec/fixtures/cassettes/gateway/cancel/refunds.yml +0 -272
- data/spec/fixtures/cassettes/gateway/cancel/void.yml +0 -201
- data/spec/fixtures/cassettes/gateway/capture.yml +0 -141
- data/spec/fixtures/cassettes/gateway/complete.yml +0 -157
- data/spec/fixtures/cassettes/gateway/credit.yml +0 -208
- data/spec/fixtures/cassettes/gateway/purchase.yml +0 -87
- data/spec/fixtures/cassettes/gateway/settled_transaction.yml +0 -140
- data/spec/fixtures/cassettes/gateway/void.yml +0 -137
- data/spec/fixtures/cassettes/source/card_type.yml +0 -267
- data/spec/fixtures/cassettes/source/last4.yml +0 -267
- data/spec/fixtures/cassettes/transaction/import/valid/capture.yml +0 -224
- data/spec/fixtures/cassettes/transaction/import/valid.yml +0 -71
- data/spec/fixtures/views/spree/orders/edit.html.erb +0 -50
- data/spec/helpers/solidus_paypal_braintree/braintree_admin_helper_spec.rb +0 -17
- data/spec/helpers/solidus_paypal_braintree/braintree_checkout_helper_spec.rb +0 -70
- data/spec/models/solidus_paypal_braintree/address_spec.rb +0 -71
- data/spec/models/solidus_paypal_braintree/avs_result_spec.rb +0 -317
- data/spec/models/solidus_paypal_braintree/gateway_spec.rb +0 -692
- data/spec/models/solidus_paypal_braintree/response_spec.rb +0 -280
- data/spec/models/solidus_paypal_braintree/source_spec.rb +0 -499
- data/spec/models/solidus_paypal_braintree/transaction_address_spec.rb +0 -235
- data/spec/models/solidus_paypal_braintree/transaction_import_spec.rb +0 -300
- data/spec/models/solidus_paypal_braintree/transaction_spec.rb +0 -85
- data/spec/models/spree/store_spec.rb +0 -14
- data/spec/requests/spree/api/orders_controller_spec.rb +0 -36
- data/spec/spec_helper.rb +0 -29
- data/spec/support/capybara.rb +0 -7
- data/spec/support/factories.rb +0 -2
- data/spec/support/gateway_helpers.rb +0 -29
- data/spec/support/order_ready_for_payment.rb +0 -37
- data/spec/support/vcr.rb +0 -42
- data/spec/support/views.rb +0 -1
@@ -1,692 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'webmock'
|
3
|
-
require 'support/order_ready_for_payment'
|
4
|
-
|
5
|
-
RSpec.describe SolidusPaypalBraintree::Gateway do
|
6
|
-
let(:gateway) do
|
7
|
-
new_gateway.tap(&:save)
|
8
|
-
end
|
9
|
-
|
10
|
-
let(:braintree) { gateway.braintree }
|
11
|
-
|
12
|
-
let(:user) { create :user }
|
13
|
-
|
14
|
-
let(:source) do
|
15
|
-
SolidusPaypalBraintree::Source.create!(
|
16
|
-
nonce: 'fake-valid-nonce',
|
17
|
-
user: user,
|
18
|
-
payment_type: payment_type,
|
19
|
-
payment_method: gateway
|
20
|
-
)
|
21
|
-
end
|
22
|
-
|
23
|
-
let(:payment_type) { SolidusPaypalBraintree::Source::PAYPAL }
|
24
|
-
|
25
|
-
describe "saving preference hashes as strings" do
|
26
|
-
subject { gateway.update(update_params) }
|
27
|
-
|
28
|
-
context "with valid hash syntax" do
|
29
|
-
let(:update_params) do
|
30
|
-
{
|
31
|
-
preferred_merchant_currency_map: '{"EUR" => "test_merchant_account_id"}',
|
32
|
-
preferred_paypal_payee_email_map: '{"CAD" => "bruce+wayne@example.com"}'
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
it "successfully updates the preference" do
|
37
|
-
subject
|
38
|
-
expect(gateway.preferred_merchant_currency_map).to eq({ "EUR" => "test_merchant_account_id" })
|
39
|
-
expect(gateway.preferred_paypal_payee_email_map).to eq({ "CAD" => "bruce+wayne@example.com" })
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
context "with invalid user input" do
|
44
|
-
let(:update_params) do
|
45
|
-
{ preferred_merchant_currency_map: '{this_is_not_a_valid_hash}' }
|
46
|
-
end
|
47
|
-
|
48
|
-
it "raise a JSON parser error" do
|
49
|
-
expect{ subject }.to raise_error(JSON::ParserError)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
describe 'making a payment on an order', vcr: {
|
55
|
-
cassette_name: 'gateway/complete',
|
56
|
-
match_requests_on: [:braintree_uri]
|
57
|
-
} do
|
58
|
-
include_context 'when order is ready for payment'
|
59
|
-
|
60
|
-
before do
|
61
|
-
order.update(number: "ORDER0")
|
62
|
-
payment.update(number: "PAYMENT0")
|
63
|
-
end
|
64
|
-
|
65
|
-
let(:payment) do
|
66
|
-
order.payments.create!(
|
67
|
-
payment_method: gateway,
|
68
|
-
source: source,
|
69
|
-
amount: 55
|
70
|
-
)
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'can complete an order' do
|
74
|
-
order.payments.reset
|
75
|
-
|
76
|
-
expect(order.total).to eq 55
|
77
|
-
|
78
|
-
expect(payment.capture_events.count).to eq 0
|
79
|
-
|
80
|
-
order.next!
|
81
|
-
expect(order.state).to eq "confirm"
|
82
|
-
|
83
|
-
order.complete!
|
84
|
-
expect(order.state).to eq "complete"
|
85
|
-
|
86
|
-
expect(order.outstanding_balance).to eq 0.0
|
87
|
-
|
88
|
-
expect(payment.capture_events.count).to eq 1
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
describe "instance methods" do
|
93
|
-
shared_examples "successful response" do
|
94
|
-
it 'returns a successful billing response', aggregate_failures: true do
|
95
|
-
expect(subject).to be_a ActiveMerchant::Billing::Response
|
96
|
-
expect(subject).to be_success
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
shared_examples "protects against connection errors" do
|
101
|
-
context 'when a timeout error happens' do
|
102
|
-
it 'raises ActiveMerchant::ConnectionError' do
|
103
|
-
expect_any_instance_of(Braintree::TransactionGateway).to receive(gateway_action) do
|
104
|
-
raise Braintree::BraintreeError
|
105
|
-
end
|
106
|
-
|
107
|
-
expect { subject }.to raise_error ActiveMerchant::ConnectionError
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
let(:authorized_id) do
|
113
|
-
braintree.transaction.sale(
|
114
|
-
amount: 40,
|
115
|
-
payment_method_nonce: source.nonce
|
116
|
-
).transaction.id
|
117
|
-
end
|
118
|
-
|
119
|
-
let(:sale_id) do
|
120
|
-
braintree.transaction.sale(
|
121
|
-
amount: 40,
|
122
|
-
payment_method_nonce: source.nonce,
|
123
|
-
options: {
|
124
|
-
submit_for_settlement: true
|
125
|
-
}
|
126
|
-
).transaction.id
|
127
|
-
end
|
128
|
-
|
129
|
-
let(:settled_id) do
|
130
|
-
braintree.testing.settle(sale_id).transaction.id
|
131
|
-
end
|
132
|
-
|
133
|
-
let(:currency) { 'USD' }
|
134
|
-
|
135
|
-
let(:gateway_options) do
|
136
|
-
{
|
137
|
-
currency: currency,
|
138
|
-
shipping_address: {
|
139
|
-
name: "Bruce Wayne",
|
140
|
-
address1: "42 Spruce Lane",
|
141
|
-
address2: "Apt 312",
|
142
|
-
city: "Gotham",
|
143
|
-
state: "CA",
|
144
|
-
zip: "90210",
|
145
|
-
country: "US"
|
146
|
-
},
|
147
|
-
billing_address: {
|
148
|
-
name: "Dick Grayson",
|
149
|
-
address1: "15 Robin Walk",
|
150
|
-
address2: "Apt 123",
|
151
|
-
city: "Blüdhaven",
|
152
|
-
state: "CA",
|
153
|
-
zip: "90210",
|
154
|
-
country: "US"
|
155
|
-
}
|
156
|
-
}
|
157
|
-
end
|
158
|
-
|
159
|
-
describe "#method_type" do
|
160
|
-
subject { gateway.method_type }
|
161
|
-
|
162
|
-
it { is_expected.to eq "paypal_braintree" }
|
163
|
-
end
|
164
|
-
|
165
|
-
describe '#gateway_options' do
|
166
|
-
subject(:gateway_options) { gateway.gateway_options }
|
167
|
-
|
168
|
-
it 'includes http_open_timeout' do
|
169
|
-
expect(subject).to have_key(:http_open_timeout)
|
170
|
-
expect(gateway_options[:http_open_timeout]).to eq(60)
|
171
|
-
end
|
172
|
-
|
173
|
-
it 'includes http_read_timeout' do
|
174
|
-
expect(subject).to have_key(:http_read_timeout)
|
175
|
-
expect(gateway_options[:http_read_timeout]).to eq(60)
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
describe '#purchase' do
|
180
|
-
subject(:purchase) { gateway.purchase(1000, source, gateway_options) }
|
181
|
-
|
182
|
-
context 'with successful purchase', vcr: {
|
183
|
-
cassette_name: 'gateway/purchase',
|
184
|
-
match_requests_on: [:braintree_uri]
|
185
|
-
} do
|
186
|
-
include_examples "successful response"
|
187
|
-
|
188
|
-
it 'submits the transaction for settlement', aggregate_failures: true do
|
189
|
-
expect(purchase.message).to eq 'submitted_for_settlement'
|
190
|
-
expect(purchase.authorization).to be_present
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
include_examples "protects against connection errors" do
|
195
|
-
let(:gateway_action) { :sale }
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
describe "#authorize" do
|
200
|
-
subject(:authorize) { gateway.authorize(1000, source, gateway_options) }
|
201
|
-
|
202
|
-
include_examples "protects against connection errors" do
|
203
|
-
let(:gateway_action) { :sale }
|
204
|
-
end
|
205
|
-
|
206
|
-
context 'with successful authorization', vcr: {
|
207
|
-
cassette_name: 'gateway/authorize',
|
208
|
-
match_requests_on: [:braintree_uri]
|
209
|
-
} do
|
210
|
-
include_examples "successful response"
|
211
|
-
|
212
|
-
it 'passes "Solidus" as the channel parameter in the request' do
|
213
|
-
expect_any_instance_of(Braintree::TransactionGateway).
|
214
|
-
to receive(:sale).
|
215
|
-
with(hash_including({ channel: "Solidus" })).and_call_original
|
216
|
-
authorize
|
217
|
-
end
|
218
|
-
|
219
|
-
it 'authorizes the transaction', aggregate_failures: true do
|
220
|
-
expect(authorize.message).to eq 'authorized'
|
221
|
-
expect(authorize.authorization).to be_present
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
|
-
context 'with different merchant account for currency', vcr: {
|
226
|
-
cassette_name: 'gateway/authorize/merchant_account/EUR',
|
227
|
-
match_requests_on: [:braintree_uri]
|
228
|
-
} do
|
229
|
-
let(:currency) { 'EUR' }
|
230
|
-
|
231
|
-
it 'settles with the correct currency' do
|
232
|
-
transaction = braintree.transaction.find(authorize.authorization)
|
233
|
-
expect(transaction.merchant_account_id).to eq 'stembolt_EUR'
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
context 'with different paypal payee email for currency', vcr: {
|
238
|
-
cassette_name: 'gateway/authorize/paypal/EUR',
|
239
|
-
match_requests_on: [:braintree_uri]
|
240
|
-
} do
|
241
|
-
let(:currency) { 'EUR' }
|
242
|
-
|
243
|
-
it 'uses the correct payee email' do
|
244
|
-
expect_any_instance_of(Braintree::TransactionGateway).
|
245
|
-
to receive(:sale).
|
246
|
-
with(hash_including({
|
247
|
-
options: {
|
248
|
-
store_in_vault_on_success: true,
|
249
|
-
paypal: {
|
250
|
-
payee_email: ENV.fetch('BRAINTREE_PAYPAL_PAYEE_EMAIL')
|
251
|
-
}
|
252
|
-
}
|
253
|
-
})).and_call_original
|
254
|
-
authorize
|
255
|
-
end
|
256
|
-
|
257
|
-
context "with PayPal transaction", vcr: {
|
258
|
-
cassette_name: 'gateway/authorize/paypal/address',
|
259
|
-
match_requests_on: [:braintree_uri]
|
260
|
-
} do
|
261
|
-
it 'includes the shipping address in the request' do
|
262
|
-
expect_any_instance_of(Braintree::TransactionGateway).
|
263
|
-
to receive(:sale).
|
264
|
-
with(hash_including({
|
265
|
-
shipping: {
|
266
|
-
first_name: "Bruce",
|
267
|
-
last_name: "Wayne",
|
268
|
-
street_address: "42 Spruce Lane Apt 312",
|
269
|
-
locality: "Gotham",
|
270
|
-
postal_code: "90210",
|
271
|
-
region: "CA",
|
272
|
-
country_code_alpha2: "US"
|
273
|
-
}
|
274
|
-
})).and_call_original
|
275
|
-
authorize
|
276
|
-
end
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
context "with CreditCard transaction", vcr: {
|
281
|
-
cassette_name: 'gateway/authorize/credit_card/address',
|
282
|
-
match_requests_on: [:braintree_uri]
|
283
|
-
} do
|
284
|
-
let(:payment_type) { SolidusPaypalBraintree::Source::CREDIT_CARD }
|
285
|
-
|
286
|
-
it 'includes the billing address in the request' do
|
287
|
-
expect_any_instance_of(Braintree::TransactionGateway).to receive(:sale).
|
288
|
-
with(hash_including({
|
289
|
-
billing: {
|
290
|
-
first_name: "Dick",
|
291
|
-
last_name: "Grayson",
|
292
|
-
street_address: "15 Robin Walk Apt 123",
|
293
|
-
locality: "Blüdhaven",
|
294
|
-
postal_code: "90210",
|
295
|
-
region: "CA",
|
296
|
-
country_code_alpha2: "US"
|
297
|
-
}
|
298
|
-
})).and_call_original
|
299
|
-
authorize
|
300
|
-
end
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
describe "#capture" do
|
305
|
-
subject(:capture) { gateway.capture(1000, authorized_id, {}) }
|
306
|
-
|
307
|
-
context 'with successful capture', vcr: {
|
308
|
-
cassette_name: 'gateway/capture',
|
309
|
-
match_requests_on: [:braintree_uri]
|
310
|
-
} do
|
311
|
-
include_examples "successful response"
|
312
|
-
|
313
|
-
it 'submits the transaction for settlement' do
|
314
|
-
expect(capture.message).to eq "submitted_for_settlement"
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
context 'with authorized transaction', vcr: {
|
319
|
-
cassette_name: 'gateway/authorized_transaction',
|
320
|
-
match_requests_on: [:braintree_uri]
|
321
|
-
} do
|
322
|
-
include_examples "protects against connection errors" do
|
323
|
-
let(:gateway_action) { :submit_for_settlement }
|
324
|
-
end
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
describe "#credit" do
|
329
|
-
subject(:credit) { gateway.credit(2000, source, settled_id, {}) }
|
330
|
-
|
331
|
-
context 'with successful credit', vcr: {
|
332
|
-
cassette_name: 'gateway/credit',
|
333
|
-
match_requests_on: [:braintree_uri]
|
334
|
-
} do
|
335
|
-
include_examples "successful response"
|
336
|
-
|
337
|
-
it 'credits the transaction' do
|
338
|
-
expect(credit.message).to eq 'submitted_for_settlement'
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
context 'with settled transaction', vcr: {
|
343
|
-
cassette_name: 'gateway/settled_transaction',
|
344
|
-
match_requests_on: [:braintree_uri]
|
345
|
-
} do
|
346
|
-
include_examples "protects against connection errors" do
|
347
|
-
let(:gateway_action) { :refund }
|
348
|
-
end
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
|
-
describe "#void" do
|
353
|
-
subject(:void) { gateway.void(authorized_id, source, {}) }
|
354
|
-
|
355
|
-
context 'when successfully voided', vcr: {
|
356
|
-
cassette_name: 'gateway/void',
|
357
|
-
match_requests_on: [:braintree_uri]
|
358
|
-
} do
|
359
|
-
include_examples "successful response"
|
360
|
-
|
361
|
-
it 'voids the transaction' do
|
362
|
-
expect(void.message).to eq 'voided'
|
363
|
-
end
|
364
|
-
end
|
365
|
-
|
366
|
-
context 'with authorized transaction', vcr: {
|
367
|
-
cassette_name: 'gateway/authorized_transaction',
|
368
|
-
match_requests_on: [:braintree_uri]
|
369
|
-
} do
|
370
|
-
include_examples "protects against connection errors" do
|
371
|
-
let(:gateway_action) { :void }
|
372
|
-
end
|
373
|
-
end
|
374
|
-
end
|
375
|
-
|
376
|
-
describe "#cancel", vcr: {
|
377
|
-
cassette_name: 'gateway/cancel',
|
378
|
-
match_requests_on: [:braintree_uri]
|
379
|
-
} do
|
380
|
-
subject(:cancel) { gateway.cancel(transaction_id) }
|
381
|
-
|
382
|
-
let(:transaction_id) { "fake_transaction_id" }
|
383
|
-
|
384
|
-
context "when the transaction is found" do
|
385
|
-
context "when it is voidable", vcr: {
|
386
|
-
cassette_name: 'gateway/cancel/void',
|
387
|
-
match_requests_on: [:braintree_uri]
|
388
|
-
} do
|
389
|
-
let(:transaction_id) { authorized_id }
|
390
|
-
|
391
|
-
include_examples "successful response"
|
392
|
-
|
393
|
-
it 'voids the transaction' do
|
394
|
-
expect(cancel.message).to eq 'voided'
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
context "when it is not voidable", vcr: {
|
399
|
-
cassette_name: 'gateway/cancel/refunds',
|
400
|
-
match_requests_on: [:braintree_uri]
|
401
|
-
} do
|
402
|
-
let(:transaction_id) { settled_id }
|
403
|
-
|
404
|
-
include_examples "successful response"
|
405
|
-
|
406
|
-
it 'refunds the transaction' do
|
407
|
-
expect(cancel.message).to eq 'submitted_for_settlement'
|
408
|
-
end
|
409
|
-
end
|
410
|
-
end
|
411
|
-
|
412
|
-
context "when the transaction is not found", vcr: {
|
413
|
-
cassette_name: 'gateway/cancel/missing',
|
414
|
-
match_requests_on: [:braintree_uri]
|
415
|
-
} do
|
416
|
-
it 'raises an error' do
|
417
|
-
expect{ cancel }.to raise_error ActiveMerchant::ConnectionError
|
418
|
-
end
|
419
|
-
end
|
420
|
-
end
|
421
|
-
|
422
|
-
describe '#try_void' do
|
423
|
-
subject { gateway.try_void(instance_double('Spree::Payment', response_code: source.token)) }
|
424
|
-
|
425
|
-
let(:transaction_request) do
|
426
|
-
class_double('Braintree::Transaction',
|
427
|
-
find: transaction_response)
|
428
|
-
end
|
429
|
-
|
430
|
-
before do
|
431
|
-
client = instance_double('Braintree::Gateway')
|
432
|
-
allow(client).to receive(:transaction) { transaction_request }
|
433
|
-
allow(gateway).to receive(:braintree) { client }
|
434
|
-
end
|
435
|
-
|
436
|
-
context 'with voidable payment' do
|
437
|
-
let(:transaction_response) do
|
438
|
-
instance_double('Braintree::Transaction',
|
439
|
-
status: Braintree::Transaction::Status::Authorized)
|
440
|
-
end
|
441
|
-
|
442
|
-
it 'voids the payment' do
|
443
|
-
expect(gateway).to receive(:void)
|
444
|
-
subject
|
445
|
-
end
|
446
|
-
|
447
|
-
context 'with error response mentioning an unvoidable transaction' do
|
448
|
-
before do
|
449
|
-
allow(gateway).to receive(:void) do
|
450
|
-
raise ActiveMerchant::ConnectionError.new(
|
451
|
-
'Transaction can only be voided if status is authorized',
|
452
|
-
double
|
453
|
-
)
|
454
|
-
end
|
455
|
-
end
|
456
|
-
|
457
|
-
it { is_expected.to be(false) }
|
458
|
-
end
|
459
|
-
|
460
|
-
context 'with other error response' do
|
461
|
-
before do
|
462
|
-
allow(gateway).to receive(:void) do
|
463
|
-
raise ActiveMerchant::ConnectionError.new(
|
464
|
-
'Server unreachable',
|
465
|
-
double
|
466
|
-
)
|
467
|
-
end
|
468
|
-
end
|
469
|
-
|
470
|
-
it { expect { subject }.to raise_error ActiveMerchant::ConnectionError }
|
471
|
-
end
|
472
|
-
end
|
473
|
-
|
474
|
-
context 'with voidable paypal payment' do
|
475
|
-
let(:transaction_response) do
|
476
|
-
instance_double('Braintree::Transaction',
|
477
|
-
status: Braintree::Transaction::Status::SettlementPending)
|
478
|
-
end
|
479
|
-
|
480
|
-
it 'voids the payment' do
|
481
|
-
expect(gateway).to receive(:void)
|
482
|
-
subject
|
483
|
-
end
|
484
|
-
end
|
485
|
-
|
486
|
-
context 'with non-voidable payment' do
|
487
|
-
let(:transaction_response) do
|
488
|
-
instance_double('Braintree::Transaction',
|
489
|
-
status: Braintree::Transaction::Status::Settled)
|
490
|
-
end
|
491
|
-
|
492
|
-
it { is_expected.to be(false) }
|
493
|
-
end
|
494
|
-
end
|
495
|
-
|
496
|
-
describe "#create_profile" do
|
497
|
-
subject(:profile) { gateway.create_profile(payment) }
|
498
|
-
|
499
|
-
let(:payment) do
|
500
|
-
build(:payment, {
|
501
|
-
payment_method: gateway,
|
502
|
-
source: source
|
503
|
-
})
|
504
|
-
end
|
505
|
-
|
506
|
-
cassette_options = {
|
507
|
-
cassette_name: "braintree/create_profile",
|
508
|
-
match_requests_on: [:braintree_uri]
|
509
|
-
}
|
510
|
-
context "with no existing customer profile", vcr: cassette_options do
|
511
|
-
it 'creates and returns a new customer profile', aggregate_failures: true do
|
512
|
-
expect(profile).to be_a SolidusPaypalBraintree::Customer
|
513
|
-
expect(profile.sources).to eq [source]
|
514
|
-
expect(profile.braintree_customer_id).to be_present
|
515
|
-
end
|
516
|
-
|
517
|
-
it "sets a token on the payment source" do
|
518
|
-
expect{ subject }.to change(source, :token)
|
519
|
-
end
|
520
|
-
end
|
521
|
-
|
522
|
-
context "when the source already has a token" do
|
523
|
-
before { source.token = "totally-a-valid-token" }
|
524
|
-
|
525
|
-
it "does not create a new customer profile" do
|
526
|
-
expect(profile).to be_nil
|
527
|
-
end
|
528
|
-
end
|
529
|
-
|
530
|
-
context "when the source already has a customer" do
|
531
|
-
before { source.build_customer }
|
532
|
-
|
533
|
-
it "does not create a new customer profile" do
|
534
|
-
expect(profile).to be_nil
|
535
|
-
end
|
536
|
-
end
|
537
|
-
|
538
|
-
context "when the source has no nonce" do
|
539
|
-
before { source.nonce = nil }
|
540
|
-
|
541
|
-
it "does not create a new customer profile" do
|
542
|
-
expect(profile).to be_nil
|
543
|
-
end
|
544
|
-
end
|
545
|
-
end
|
546
|
-
|
547
|
-
shared_examples "sources_by_order" do
|
548
|
-
let(:order) { FactoryBot.create :order, user: user, state: "complete", completed_at: Time.current }
|
549
|
-
let(:gateway) { new_gateway.tap(&:save!) }
|
550
|
-
|
551
|
-
let(:other_payment_method) { FactoryBot.create(:payment_method) }
|
552
|
-
|
553
|
-
let(:source_without_profile) do
|
554
|
-
SolidusPaypalBraintree::Source.create!(
|
555
|
-
payment_method_id: gateway.id,
|
556
|
-
payment_type: payment_type,
|
557
|
-
user_id: user.id
|
558
|
-
)
|
559
|
-
end
|
560
|
-
|
561
|
-
let(:source_with_profile) do
|
562
|
-
SolidusPaypalBraintree::Source.create!(
|
563
|
-
payment_method_id: gateway.id,
|
564
|
-
payment_type: payment_type,
|
565
|
-
user_id: user.id
|
566
|
-
).tap do |source|
|
567
|
-
source.create_customer!(user: user)
|
568
|
-
source.save!
|
569
|
-
end
|
570
|
-
end
|
571
|
-
|
572
|
-
before do
|
573
|
-
create(:payment, order: order, payment_method_id: payment_method_id, source: source)
|
574
|
-
end
|
575
|
-
|
576
|
-
context "when the order has payments with the braintree payment method" do
|
577
|
-
let(:payment_method_id) { gateway.id }
|
578
|
-
|
579
|
-
context "when the payment has a saved source with a profile" do
|
580
|
-
let(:source) { source_with_profile }
|
581
|
-
|
582
|
-
it "returns the source" do
|
583
|
-
expect(subject.to_a).to eql([source])
|
584
|
-
end
|
585
|
-
end
|
586
|
-
|
587
|
-
context "when the payment has a saved source without a profile" do
|
588
|
-
let(:source) { source_without_profile }
|
589
|
-
|
590
|
-
it "returns no result" do
|
591
|
-
expect(subject.to_a).to eql([])
|
592
|
-
end
|
593
|
-
end
|
594
|
-
end
|
595
|
-
|
596
|
-
context "when the order has no payments with the braintree payment method" do
|
597
|
-
let(:payment_method_id) { other_payment_method.id }
|
598
|
-
let(:source) { FactoryBot.create :credit_card }
|
599
|
-
|
600
|
-
it "returns no results" do
|
601
|
-
expect(subject.to_a).to eql([])
|
602
|
-
end
|
603
|
-
end
|
604
|
-
end
|
605
|
-
|
606
|
-
describe "#sources_by_order" do
|
607
|
-
subject { gateway.sources_by_order(order) }
|
608
|
-
|
609
|
-
let(:gateway) { new_gateway.tap(&:save!) }
|
610
|
-
let(:order) { FactoryBot.create :order, user: user, state: "complete", completed_at: Time.current }
|
611
|
-
|
612
|
-
include_examples "sources_by_order"
|
613
|
-
end
|
614
|
-
|
615
|
-
describe "#reusable_sources" do
|
616
|
-
subject { gateway.reusable_sources(order) }
|
617
|
-
|
618
|
-
let(:order) { FactoryBot.build :order, user: user }
|
619
|
-
let(:gateway) { new_gateway.tap(&:save!) }
|
620
|
-
|
621
|
-
context "when an order is completed" do
|
622
|
-
include_examples "sources_by_order"
|
623
|
-
end
|
624
|
-
|
625
|
-
context "when an order is not completed" do
|
626
|
-
context "when the order has a user id" do
|
627
|
-
let(:user) { FactoryBot.create(:user) }
|
628
|
-
|
629
|
-
let!(:source_without_profile) do
|
630
|
-
SolidusPaypalBraintree::Source.create!(
|
631
|
-
payment_method_id: gateway.id,
|
632
|
-
payment_type: payment_type,
|
633
|
-
user_id: user.id
|
634
|
-
)
|
635
|
-
end
|
636
|
-
|
637
|
-
let!(:source_with_profile) do
|
638
|
-
SolidusPaypalBraintree::Source.create!(
|
639
|
-
payment_method_id: gateway.id,
|
640
|
-
payment_type: payment_type,
|
641
|
-
user_id: user.id
|
642
|
-
).tap do |source|
|
643
|
-
source.create_customer!(user: user)
|
644
|
-
source.save!
|
645
|
-
end
|
646
|
-
end
|
647
|
-
|
648
|
-
it "includes saved sources with payment profiles" do
|
649
|
-
expect(subject).to include(source_with_profile)
|
650
|
-
end
|
651
|
-
|
652
|
-
it "excludes saved sources without payment profiles" do
|
653
|
-
expect(subject).not_to include(source_without_profile)
|
654
|
-
end
|
655
|
-
end
|
656
|
-
|
657
|
-
context "when the order does not have a user" do
|
658
|
-
let(:user) { nil }
|
659
|
-
|
660
|
-
it "returns no sources for guest users" do
|
661
|
-
expect(subject).to eql([])
|
662
|
-
end
|
663
|
-
end
|
664
|
-
end
|
665
|
-
end
|
666
|
-
end
|
667
|
-
|
668
|
-
describe '.generate_token' do
|
669
|
-
subject do
|
670
|
-
# dont VCR ignore generate token request, use the existing cassette
|
671
|
-
allow(VCR.request_ignorer.hooks).to receive(:[]).with(:ignore_request).and_return([])
|
672
|
-
gateway.generate_token
|
673
|
-
end
|
674
|
-
|
675
|
-
context 'with connection enabled', vcr: {
|
676
|
-
cassette_name: 'braintree/generate_token',
|
677
|
-
match_requests_on: [:braintree_uri]
|
678
|
-
} do
|
679
|
-
it { is_expected.to be_a(String).and be_present }
|
680
|
-
end
|
681
|
-
|
682
|
-
context 'when token generation is disabled' do
|
683
|
-
let(:gateway) do
|
684
|
-
gateway = described_class.create!(name: 'braintree')
|
685
|
-
gateway.preferred_token_generation_enabled = false
|
686
|
-
gateway
|
687
|
-
end
|
688
|
-
|
689
|
-
it { expect { subject }.to raise_error SolidusPaypalBraintree::Gateway::TokenGenerationDisabledError }
|
690
|
-
end
|
691
|
-
end
|
692
|
-
end
|