solidus_paypal_braintree 0.4.0 → 1.0.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.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +40 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/stale.yml +17 -0
  5. data/.gitignore +18 -0
  6. data/.rspec +2 -0
  7. data/.rubocop.yml +68 -0
  8. data/CHANGELOG.md +258 -0
  9. data/Gemfile +43 -0
  10. data/LICENSE +2 -2
  11. data/README.md +77 -23
  12. data/Rakefile +4 -25
  13. data/app/assets/javascripts/solidus_paypal_braintree/checkout.js +32 -3
  14. data/app/assets/javascripts/solidus_paypal_braintree/client.js +23 -4
  15. data/app/assets/javascripts/solidus_paypal_braintree/constants.js +19 -0
  16. data/app/assets/javascripts/solidus_paypal_braintree/frontend.js +1 -0
  17. data/app/assets/javascripts/solidus_paypal_braintree/hosted_form.js +15 -5
  18. data/app/assets/javascripts/solidus_paypal_braintree/paypal_button.js +47 -20
  19. data/app/assets/javascripts/solidus_paypal_braintree/paypal_messaging.js +22 -0
  20. data/app/decorators/controllers/solidus_paypal_braintree/admin_payments_controller_decorator.rb +11 -0
  21. data/app/decorators/controllers/solidus_paypal_braintree/checkout_controller_decorator.rb +11 -0
  22. data/app/decorators/controllers/solidus_paypal_braintree/client_tokens_controller.rb +41 -0
  23. data/app/decorators/controllers/solidus_paypal_braintree/orders_controller_decorator.rb +11 -0
  24. data/app/decorators/models/solidus_paypal_braintree/spree/store_decorator.rb +20 -0
  25. data/app/decorators/models/solidus_paypal_braintree/spree/user_decorator.rb +13 -0
  26. data/app/helpers/solidus_paypal_braintree/braintree_admin_helper.rb +23 -0
  27. data/app/helpers/solidus_paypal_braintree/braintree_checkout_helper.rb +46 -0
  28. data/app/models/application_record.rb +2 -0
  29. data/app/models/solidus_paypal_braintree/address.rb +30 -0
  30. data/app/models/solidus_paypal_braintree/configuration.rb +26 -3
  31. data/app/models/solidus_paypal_braintree/customer.rb +7 -3
  32. data/app/models/solidus_paypal_braintree/gateway.rb +52 -20
  33. data/app/models/solidus_paypal_braintree/response.rb +3 -2
  34. data/app/models/solidus_paypal_braintree/source.rb +21 -7
  35. data/app/models/solidus_paypal_braintree/transaction.rb +2 -0
  36. data/app/models/solidus_paypal_braintree/transaction_address.rb +30 -12
  37. data/app/models/solidus_paypal_braintree/transaction_import.rb +13 -9
  38. data/app/views/spree/api/payments/source_views/_paypal_braintree.json.jbuilder +3 -0
  39. data/app/views/spree/checkout/existing_payment/_paypal_braintree.html.erb +10 -0
  40. data/app/views/spree/shared/_apple_pay_button.html.erb +2 -2
  41. data/app/views/spree/shared/_braintree_errors.html.erb +11 -17
  42. data/app/views/spree/shared/_braintree_hosted_fields.html.erb +24 -9
  43. data/app/views/spree/shared/_paypal_braintree_head_scripts.html.erb +9 -6
  44. data/app/views/spree/shared/_paypal_cart_button.html.erb +16 -2
  45. data/app/views/spree/shared/_paypal_messaging.html.erb +13 -0
  46. data/bin/console +17 -0
  47. data/bin/rails +15 -0
  48. data/bin/setup +8 -0
  49. data/config/locales/en.yml +10 -0
  50. data/config/locales/it.yml +51 -8
  51. data/config/routes.rb +2 -0
  52. data/db/migrate/20160906201711_create_solidus_paypal_braintree_customers.rb +3 -1
  53. data/db/migrate/20161125172005_add_braintree_configuration_to_stores.rb +5 -7
  54. data/db/migrate/20170505193712_add_null_constraint_to_sources.rb +3 -1
  55. data/db/migrate/20190705115327_add_paypal_button_preferences_to_braintree_configurations.rb +5 -0
  56. data/db/migrate/20190911141712_add_3d_secure_to_braintree_configuration.rb +5 -0
  57. data/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb +20 -5
  58. data/lib/controllers/frontend/solidus_paypal_braintree/checkouts_controller.rb +25 -21
  59. data/lib/controllers/frontend/solidus_paypal_braintree/transactions_controller.rb +55 -51
  60. data/lib/generators/solidus_paypal_braintree/install/install_generator.rb +7 -5
  61. data/lib/solidus_paypal_braintree.rb +4 -0
  62. data/lib/solidus_paypal_braintree/country_mapper.rb +4 -2
  63. data/lib/solidus_paypal_braintree/engine.rb +11 -11
  64. data/lib/solidus_paypal_braintree/factories.rb +8 -4
  65. data/lib/solidus_paypal_braintree/request_protection.rb +3 -0
  66. data/lib/solidus_paypal_braintree/version.rb +3 -1
  67. data/lib/views/backend/solidus_paypal_braintree/configurations/list.html.erb +30 -5
  68. data/lib/views/backend/spree/admin/payments/source_forms/_paypal_braintree.html.erb +2 -2
  69. data/lib/views/backend/spree/admin/payments/source_views/_paypal_braintree.html.erb +2 -2
  70. data/lib/views/backend_v1.2/spree/admin/payments/source_forms/_paypal_braintree.html.erb +2 -2
  71. data/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb +4 -2
  72. data/lib/views/frontend/spree/shared/_paypal_checkout_button.html.erb +30 -0
  73. data/solidus_paypal_braintree.gemspec +42 -0
  74. data/spec/controllers/solidus_paypal_braintree/checkouts_controller_spec.rb +99 -0
  75. data/spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb +55 -0
  76. data/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb +73 -0
  77. data/spec/controllers/solidus_paypal_braintree/transactions_controller_spec.rb +183 -0
  78. data/spec/features/backend/configuration_spec.rb +23 -0
  79. data/spec/features/backend/new_payment_spec.rb +137 -0
  80. data/spec/features/frontend/braintree_credit_card_checkout_spec.rb +187 -0
  81. data/spec/features/frontend/paypal_checkout_spec.rb +166 -0
  82. data/spec/fixtures/cassettes/admin/invalid_credit_card.yml +63 -0
  83. data/spec/fixtures/cassettes/admin/resubmit_credit_card.yml +352 -0
  84. data/spec/fixtures/cassettes/admin/valid_credit_card.yml +412 -0
  85. data/spec/fixtures/cassettes/braintree/create_profile.yml +71 -0
  86. data/spec/fixtures/cassettes/braintree/generate_token.yml +63 -0
  87. data/spec/fixtures/cassettes/braintree/token.yml +63 -0
  88. data/spec/fixtures/cassettes/checkout/invalid_credit_card.yml +63 -0
  89. data/spec/fixtures/cassettes/checkout/resubmit_credit_card.yml +216 -0
  90. data/spec/fixtures/cassettes/checkout/update.yml +71 -0
  91. data/spec/fixtures/cassettes/checkout/valid_credit_card.yml +156 -0
  92. data/spec/fixtures/cassettes/gateway/authorize.yml +86 -0
  93. data/spec/fixtures/cassettes/gateway/authorize/credit_card/address.yml +86 -0
  94. data/spec/fixtures/cassettes/gateway/authorize/merchant_account/EUR.yml +154 -0
  95. data/spec/fixtures/cassettes/gateway/authorize/paypal/EUR.yml +90 -0
  96. data/spec/fixtures/cassettes/gateway/authorize/paypal/address.yml +90 -0
  97. data/spec/fixtures/cassettes/gateway/authorized_transaction.yml +73 -0
  98. data/spec/fixtures/cassettes/gateway/cancel/missing.yml +63 -0
  99. data/spec/fixtures/cassettes/gateway/cancel/refunds.yml +272 -0
  100. data/spec/fixtures/cassettes/gateway/cancel/void.yml +201 -0
  101. data/spec/fixtures/cassettes/gateway/capture.yml +141 -0
  102. data/spec/fixtures/cassettes/gateway/complete.yml +157 -0
  103. data/spec/fixtures/cassettes/gateway/credit.yml +208 -0
  104. data/spec/fixtures/cassettes/gateway/purchase.yml +87 -0
  105. data/spec/fixtures/cassettes/gateway/settled_transaction.yml +140 -0
  106. data/spec/fixtures/cassettes/gateway/void.yml +137 -0
  107. data/spec/fixtures/cassettes/source/card_type.yml +267 -0
  108. data/spec/fixtures/cassettes/source/last4.yml +267 -0
  109. data/spec/fixtures/cassettes/transaction/import/valid.yml +71 -0
  110. data/spec/fixtures/cassettes/transaction/import/valid/capture.yml +224 -0
  111. data/spec/fixtures/views/spree/orders/edit.html.erb +50 -0
  112. data/spec/helpers/solidus_paypal_braintree/braintree_admin_helper_spec.rb +17 -0
  113. data/spec/models/solidus_paypal_braintree/address_spec.rb +51 -0
  114. data/spec/models/solidus_paypal_braintree/avs_result_spec.rb +317 -0
  115. data/spec/models/solidus_paypal_braintree/gateway_spec.rb +692 -0
  116. data/spec/models/solidus_paypal_braintree/response_spec.rb +280 -0
  117. data/spec/models/solidus_paypal_braintree/source_spec.rb +360 -0
  118. data/spec/models/solidus_paypal_braintree/transaction_address_spec.rb +253 -0
  119. data/spec/models/solidus_paypal_braintree/transaction_import_spec.rb +283 -0
  120. data/spec/models/solidus_paypal_braintree/transaction_spec.rb +85 -0
  121. data/spec/models/spree/store_spec.rb +14 -0
  122. data/spec/requests/spree/api/orders_controller_spec.rb +36 -0
  123. data/spec/spec_helper.rb +26 -0
  124. data/spec/support/capybara.rb +7 -0
  125. data/spec/support/factories.rb +2 -0
  126. data/spec/support/gateway_helpers.rb +29 -0
  127. data/spec/support/order_ready_for_payment.rb +37 -0
  128. data/spec/support/vcr.rb +42 -0
  129. data/spec/support/views.rb +1 -0
  130. metadata +182 -194
  131. data/app/controllers/solidus_paypal_braintree/client_tokens_controller.rb +0 -22
  132. data/app/helpers/braintree_admin_helper.rb +0 -18
  133. data/app/models/spree/store_decorator.rb +0 -11
  134. data/app/views/spree/shared/_paypal_checkout_button.html.erb +0 -27
  135. data/config/initializers/braintree.rb +0 -1
@@ -0,0 +1,253 @@
1
+ require 'spec_helper'
2
+
3
+ describe SolidusPaypalBraintree::TransactionAddress do
4
+ describe "#valid?" do
5
+ subject { address.valid? }
6
+
7
+ let(:address) { described_class.new(valid_attributes) }
8
+
9
+ let(:valid_attributes) do
10
+ {
11
+ name: "Bruce Wayne",
12
+ address_line_1: "42 Spruce Lane",
13
+ city: "Gotham",
14
+ zip: "98201",
15
+ state_code: "WA",
16
+ country_code: "US"
17
+ }
18
+ end
19
+
20
+ let(:country) { create :country, iso: 'US', states_required: true }
21
+
22
+ before do
23
+ create :state, abbr: "WA", country: country
24
+ end
25
+
26
+ it { is_expected.to be true }
27
+
28
+ context 'without country matches' do
29
+ let(:valid_attributes) { super().merge({ country_code: 'CA' }) }
30
+
31
+ it { is_expected.to be false }
32
+ end
33
+
34
+ context "without name" do
35
+ let(:valid_attributes) { super().except(:name) }
36
+
37
+ it { is_expected.to be false }
38
+ end
39
+
40
+ context "without address_line_1" do
41
+ let(:valid_attributes) { super().except(:address_line_1) }
42
+
43
+ it { is_expected.to be false }
44
+ end
45
+
46
+ context "without city" do
47
+ let(:valid_attributes) { super().except(:city) }
48
+
49
+ it { is_expected.to be false }
50
+ end
51
+
52
+ context "without zip" do
53
+ let(:valid_attributes) { super().except(:zip) }
54
+
55
+ it { is_expected.to be false }
56
+ end
57
+
58
+ context "without state_code" do
59
+ let(:valid_attributes) { super().except(:state_code) }
60
+
61
+ it { is_expected.to be false }
62
+
63
+ context "when country does not requires states" do
64
+ let(:country) { create :country, iso: 'US', states_required: false }
65
+
66
+ it { is_expected.to be true }
67
+ end
68
+ end
69
+
70
+ context "without country_code" do
71
+ let(:valid_attributes) { super().except(:country_code) }
72
+
73
+ it { is_expected.to be true }
74
+
75
+ it "defaults to the US" do
76
+ subject
77
+ expect(address.country_code).to eq "us"
78
+ end
79
+ end
80
+
81
+ context "with a one word name" do
82
+ let(:valid_attributes) { super().merge({ name: "Bruce" }) }
83
+
84
+ it { is_expected.to be true }
85
+ end
86
+ end
87
+
88
+ describe "#attributes=" do
89
+ subject { described_class.new(attrs) }
90
+
91
+ context "when an ISO code is provided" do
92
+ let(:attrs) { { country_code: "US" } }
93
+
94
+ it "uses the ISO code provided" do
95
+ expect(subject.country_code).to eq "US"
96
+ end
97
+ end
98
+
99
+ context "when the ISO code is blank" do
100
+ context "with a valid country name provided" do
101
+ before do
102
+ create :country, name: "canada", iso: "CA"
103
+ end
104
+
105
+ let(:attrs) { { country_name: "Canada" } }
106
+
107
+ it "looks up the ISO code by the country name" do
108
+ expect(subject.country_code).to eq "CA"
109
+ end
110
+ end
111
+
112
+ context "without valid country name" do
113
+ let(:attrs) { { country_name: "Neverland" } }
114
+
115
+ it "leaves the country code blank" do
116
+ expect(subject.country_code).to be_nil
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ describe '#spree_country' do
123
+ subject { described_class.new(country_code: country_code).spree_country }
124
+
125
+ before do
126
+ create :country, name: 'United States', iso: 'US'
127
+ end
128
+
129
+ ['us', 'US'].each do |code|
130
+ let(:country_code) { code }
131
+
132
+ it 'looks up by iso' do
133
+ expect(subject.name).to eq 'United States'
134
+ end
135
+ end
136
+
137
+ context 'when country does not exist' do
138
+ let(:country_code) { 'NA' }
139
+
140
+ it { is_expected.to be_nil }
141
+ end
142
+ end
143
+
144
+ describe '#spree_state' do
145
+ subject { described_class.new(country_code: 'US', state_code: state_code).spree_state }
146
+
147
+ let(:state_code) { 'newy' }
148
+
149
+ it { is_expected.to be_nil }
150
+
151
+ context 'when state exists' do
152
+ before do
153
+ us = create :country, iso: 'US'
154
+ create :state, abbr: 'NY', name: 'New York', country: us
155
+ end
156
+
157
+ ['ny', ' ny', 'ny ', 'New York', 'new york', 'NY'].each do |code|
158
+ let(:state_code) { code }
159
+
160
+ it 'looks up the right state' do
161
+ expect(subject.abbr).to eq "NY"
162
+ end
163
+ end
164
+
165
+ context 'with no matching state' do
166
+ let(:state_code) { 'AL' }
167
+
168
+ it { is_expected.to be_nil }
169
+ end
170
+ end
171
+ end
172
+
173
+ describe '#should_match_state_model' do
174
+ subject { described_class.new(country_code: 'US').should_match_state_model? }
175
+
176
+ it { is_expected.to be_falsey }
177
+
178
+ context 'when country does not require states' do
179
+ before { create :country, iso: 'US', states_required: false }
180
+
181
+ it { is_expected.to be false }
182
+ end
183
+
184
+ context 'when country requires states' do
185
+ before { create :country, iso: 'US', states_required: true }
186
+
187
+ it { is_expected.to be true }
188
+ end
189
+ end
190
+
191
+ describe '#to_spree_address' do
192
+ subject { described_class.new(address_params).to_spree_address }
193
+
194
+ let(:address_params) do
195
+ {
196
+ country_code: 'US',
197
+ state_code: 'NY',
198
+ name: "Alfred"
199
+ }
200
+ end
201
+ let!(:us) { create :country, iso: 'US' }
202
+
203
+ it { is_expected.to be_a Spree::Address }
204
+
205
+ context 'when country exists with states' do
206
+ before do
207
+ create :state, country: us, abbr: 'NY', name: 'New York'
208
+ end
209
+
210
+ it 'uses state model' do
211
+ expect(subject.state.name).to eq 'New York'
212
+ end
213
+ end
214
+
215
+ context 'when country exist with no states' do
216
+ it 'uses state_name' do
217
+ expect(subject.state).to be_nil
218
+ expect(subject.state_text).to eq 'NY'
219
+ end
220
+ end
221
+
222
+ context 'when using first_name and last_name' do
223
+ let(:address_params) { super().merge({ first_name: "Bruce", last_name: "Wayne" }) }
224
+
225
+ it 'displays a deprecation warning' do
226
+ expect(Spree::Deprecation).to receive(:warn).
227
+ with("first_name and last_name are deprecated. Use name instead.", any_args)
228
+
229
+ subject
230
+ end
231
+ end
232
+ end
233
+
234
+ describe "#split" do
235
+ subject { described_class.new.split_name(name) }
236
+
237
+ context "with a one word name" do
238
+ let(:name) { "Bruce" }
239
+
240
+ it "correctly splits" do
241
+ expect(subject).to eq ["Bruce"]
242
+ end
243
+ end
244
+
245
+ context "with a multi word name" do
246
+ let(:name) { "Bruce Wayne The Batman" }
247
+
248
+ it "correctly splits" do
249
+ expect(subject).to eq ["Bruce", "Wayne The Batman"]
250
+ end
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,283 @@
1
+ require 'spec_helper'
2
+
3
+ describe SolidusPaypalBraintree::TransactionImport do
4
+ let(:order) { Spree::Order.new }
5
+ let!(:country) { create :country, iso: "US" }
6
+ let(:braintree_gateway) { SolidusPaypalBraintree::Gateway.new }
7
+ let(:transaction_address) { nil }
8
+ let(:transaction) do
9
+ SolidusPaypalBraintree::Transaction.new(
10
+ nonce: 'abcd1234',
11
+ payment_type: "ApplePayCard",
12
+ address: transaction_address,
13
+ payment_method: braintree_gateway,
14
+ email: "test@example.com",
15
+ phone: "123-456-6789"
16
+ )
17
+ end
18
+ let(:transaction_import) { described_class.new(order, transaction) }
19
+
20
+ describe "#valid?" do
21
+ subject { transaction_import.valid? }
22
+
23
+ it { is_expected.to be true }
24
+
25
+ context "with invalid transaction" do
26
+ let(:transaction) { SolidusPaypalBraintree::Transaction.new }
27
+
28
+ it { is_expected.to be false }
29
+ end
30
+
31
+ context "with invalid address" do
32
+ let(:transaction_address) do
33
+ SolidusPaypalBraintree::TransactionAddress.new(
34
+ name: "Bruce Wayne",
35
+ address_line_1: "42 Spruce Lane",
36
+ city: "Gotham",
37
+ state_code: "WA",
38
+ country_code: "US"
39
+ )
40
+ end
41
+
42
+ it { is_expected.to be false }
43
+
44
+ it "sets useful error messages" do
45
+ transaction_import.valid?
46
+ expect(transaction_import.errors.full_messages).
47
+ to eq ["Address is invalid", "Address zip can't be blank"]
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#source' do
53
+ subject { described_class.new(order, transaction).source }
54
+
55
+ it { is_expected.to be_a SolidusPaypalBraintree::Source }
56
+
57
+ it 'takes the nonce from the transaction' do
58
+ expect(subject.nonce).to eq 'abcd1234'
59
+ end
60
+
61
+ it 'takes the payment type from the transaction' do
62
+ expect(subject.payment_type).to eq 'ApplePayCard'
63
+ end
64
+
65
+ it 'takes the payment method from the transaction' do
66
+ expect(subject.payment_method).to eq braintree_gateway
67
+ end
68
+
69
+ context 'when order has a user' do
70
+ let(:user) { Spree.user_class.new }
71
+ let(:order) { Spree::Order.new user: user }
72
+
73
+ it 'associates user to the source' do
74
+ expect(subject.user).to eq user
75
+ end
76
+ end
77
+ end
78
+
79
+ describe '#user' do
80
+ subject { described_class.new(order, transaction).user }
81
+
82
+ it { is_expected.to be_nil }
83
+
84
+ context 'when order has a user' do
85
+ let(:user) { Spree.user_class.new }
86
+ let(:order) { Spree::Order.new user: user }
87
+
88
+ it { is_expected.to eq user }
89
+ end
90
+ end
91
+
92
+ describe '#import!' do
93
+ subject { described_class.new(order, transaction).import!(end_state) }
94
+
95
+ let(:store) { create :store }
96
+ let(:variant) { create :variant }
97
+ let(:line_item) { Spree::LineItem.new(variant: variant, quantity: 1, price: 10) }
98
+ let(:address) { create :address, country: country }
99
+ let(:order) {
100
+ Spree::Order.create(
101
+ number: "R999999999",
102
+ store: store,
103
+ line_items: [line_item],
104
+ ship_address: address,
105
+ currency: 'USD',
106
+ total: 10,
107
+ email: 'test@example.com'
108
+ )
109
+ }
110
+ let(:payment_method) { create_gateway }
111
+
112
+ let(:country) { create :country, iso: 'US', states_required: true }
113
+ let(:transaction_address) { nil }
114
+ let(:end_state) { 'confirm' }
115
+
116
+ let(:transaction) do
117
+ SolidusPaypalBraintree::Transaction.new(
118
+ nonce: 'fake-valid-nonce',
119
+ payment_method: payment_method,
120
+ address: transaction_address,
121
+ payment_type: SolidusPaypalBraintree::Source::PAYPAL,
122
+ phone: '123-456-7890',
123
+ email: 'user@example.com'
124
+ )
125
+ end
126
+
127
+ before do
128
+ # create a shipping method so we can push through to the end
129
+ country
130
+ create :shipping_method, cost: 5
131
+
132
+ # ensure payments have the same number so VCR matches the request body
133
+ allow_any_instance_of(Spree::Payment).
134
+ to receive(:generate_identifier).
135
+ and_return("ABCD1234")
136
+ end
137
+
138
+ context "with passing validation", vcr: {
139
+ cassette_name: 'transaction/import/valid',
140
+ match_requests_on: [:braintree_uri]
141
+ } do
142
+ context "when order end state is confirm" do
143
+ it 'advances order to confirm state' do
144
+ subject
145
+ expect(order.state).to eq 'confirm'
146
+ end
147
+
148
+ it 'has a payment for the cost of line items + shipment' do
149
+ subject
150
+ expect(order.payments.first.amount).to eq 15
151
+ end
152
+
153
+ it 'is complete and capturable', aggregate_failures: true, vcr: {
154
+ cassette_name: 'transaction/import/valid/capture',
155
+ match_requests_on: [:braintree_uri]
156
+ } do
157
+ subject
158
+ order.complete
159
+
160
+ expect(order).to be_complete
161
+ expect(order.payments.first).to be_pending
162
+
163
+ order.payments.first.capture!
164
+ # need to reload, as capture will update the order
165
+ expect(order.reload).to be_paid
166
+ end
167
+ end
168
+
169
+ context "when order end state is delivery" do
170
+ let(:end_state) { 'delivery' }
171
+
172
+ it "advances the order to delivery" do
173
+ subject
174
+ expect(order.state).to eq 'delivery'
175
+ end
176
+
177
+ it "has a payment for the cost of line items" do
178
+ subject
179
+ expect(order.payments.first.amount).to eq 10
180
+ end
181
+ end
182
+
183
+ context 'when transaction has address' do
184
+ let!(:new_york) { create :state, country: country, abbr: 'NY' }
185
+
186
+ let(:transaction_address) do
187
+ SolidusPaypalBraintree::TransactionAddress.new(
188
+ country_code: 'US',
189
+ name: 'Thaddeus Venture',
190
+ city: 'New York',
191
+ state_code: 'NY',
192
+ address_line_1: '350 5th Ave',
193
+ zip: '10118'
194
+ )
195
+ end
196
+
197
+ it 'uses the new address', aggregate_failures: true do
198
+ subject
199
+ expect(order.shipping_address.address1).to eq '350 5th Ave'
200
+ expect(order.shipping_address.country).to eq country
201
+ expect(order.shipping_address.state).to eq new_york
202
+ end
203
+
204
+ context 'with a tax category' do
205
+ before do
206
+ zone = Spree::Zone.create name: 'nyc tax'
207
+ zone.members << Spree::ZoneMember.new(zoneable: new_york)
208
+ create :tax_rate, zone: zone
209
+ end
210
+
211
+ it 'includes the tax in the payment' do
212
+ subject
213
+ expect(order.payments.first.amount).to eq 16
214
+ end
215
+ end
216
+
217
+ context 'with a less expensive tax category' do
218
+ before do
219
+ original_zone = Spree::Zone.create name: 'first address tax'
220
+ original_zone.members << Spree::ZoneMember.new(zoneable: address.state)
221
+ original_tax_rate = create :tax_rate, zone: original_zone, amount: 0.2
222
+
223
+ # new address is NY
224
+ ny_zone = Spree::Zone.create name: 'nyc tax'
225
+ ny_zone.members << Spree::ZoneMember.new(zoneable: new_york)
226
+ create :tax_rate, tax_categories: [original_tax_rate.tax_categories.first], zone: ny_zone, amount: 0.1
227
+ end
228
+
229
+ it 'includes the lower tax in the payment' do
230
+ # so shipments and shipment cost is calculated before transaction import
231
+ order.next!; order.next!
232
+ # precondition
233
+ expect(order.additional_tax_total).to eq 2
234
+ expect(order.total).to eq 17
235
+
236
+ subject
237
+ expect(order.additional_tax_total).to eq 1
238
+ expect(order.payments.first.amount).to eq 16
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ context "when validation fails" do
245
+ let(:transaction_address) do
246
+ SolidusPaypalBraintree::TransactionAddress.new(
247
+ country_code: 'US',
248
+ name: 'Thaddeus Venture',
249
+ city: 'New York',
250
+ state_code: 'NY',
251
+ address_line_1: '350 5th Ave'
252
+ )
253
+ end
254
+
255
+ it "raises an error with the validation messages" do
256
+ expect { subject }.to raise_error(
257
+ SolidusPaypalBraintree::TransactionImport::InvalidImportError
258
+ )
259
+ end
260
+ end
261
+
262
+ context "with checkout flow", vcr: {
263
+ cassette_name: 'transaction/import/valid',
264
+ match_requests_on: [:braintree_uri]
265
+ } do
266
+ it "is not restarted by default" do
267
+ expect(order).not_to receive(:restart_checkout_flow)
268
+ subject
269
+ end
270
+
271
+ context "with restart_checkout: true" do
272
+ subject do
273
+ described_class.new(order, transaction).import!(end_state, restart_checkout: true)
274
+ end
275
+
276
+ it "is restarted" do
277
+ expect(order).to receive(:restart_checkout_flow)
278
+ subject
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end