solidus_bolt 0.0.1
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 +11 -0
- data/CHANGELOG.md +1 -0
- data/Gemfile +33 -0
- data/LICENSE +26 -0
- data/README.md +175 -0
- data/Rakefile +6 -0
- data/app/assets/images/bolt_logo_standard.png +0 -0
- data/app/assets/javascripts/authorize_account.js +74 -0
- data/app/assets/javascripts/solidus_bolt.js +91 -0
- data/app/assets/javascripts/spree/backend/solidus_bolt.js +4 -0
- data/app/assets/javascripts/spree/frontend/solidus_bolt.js +18 -0
- data/app/assets/stylesheets/spree/backend/solidus_bolt.css +4 -0
- data/app/assets/stylesheets/spree/frontend/solidus_bolt.css +4 -0
- data/app/controllers/solidus_bolt/accounts_controller.rb +17 -0
- data/app/controllers/solidus_bolt/base_controller.rb +21 -0
- data/app/controllers/solidus_bolt/webhooks_controller.rb +21 -0
- data/app/controllers/spree/admin/bolt_webhooks_controller.rb +34 -0
- data/app/controllers/spree/admin/bolts_controller.rb +43 -0
- data/app/decorators/controllers/solidus_bolt/spree_checkout_controller/add_addresses_to_bolt.rb +23 -0
- data/app/decorators/controllers/solidus_bolt/spree_checkout_controller/refresh_bolt_addresses.rb +17 -0
- data/app/decorators/controllers/solidus_bolt/spree_checkout_controller/refresh_bolt_payment_source.rb +17 -0
- data/app/decorators/models/solidus_bolt/address_decorator.rb +22 -0
- data/app/decorators/models/solidus_bolt/log_entry_decorator.rb +11 -0
- data/app/decorators/models/solidus_bolt/order_decorator.rb +44 -0
- data/app/decorators/models/solidus_bolt/payment_decorator.rb +11 -0
- data/app/decorators/omniauth/strategies/bolt_decorator.rb +16 -0
- data/app/jobs/solidus_bolt/add_address_job.rb +11 -0
- data/app/models/solidus_bolt/bolt_configuration.rb +74 -0
- data/app/models/solidus_bolt/gateway.rb +133 -0
- data/app/models/solidus_bolt/payment_method.rb +35 -0
- data/app/models/solidus_bolt/payment_source.rb +13 -0
- data/app/models/solidus_bolt.rb +7 -0
- data/app/overrides/spree/shared/_head/add_bolt_embed_script.html.erb.deface +6 -0
- data/app/services/solidus_bolt/accounts/add_address_service.rb +55 -0
- data/app/services/solidus_bolt/accounts/add_payment_method_service.rb +45 -0
- data/app/services/solidus_bolt/accounts/detail_service.rb +38 -0
- data/app/services/solidus_bolt/accounts/detect_account_service.rb +34 -0
- data/app/services/solidus_bolt/base_service.rb +55 -0
- data/app/services/solidus_bolt/oauth/token_service.rb +43 -0
- data/app/services/solidus_bolt/payments/capture_sync_service.rb +24 -0
- data/app/services/solidus_bolt/payments/credit_sync_service.rb +44 -0
- data/app/services/solidus_bolt/payments/void_sync_service.rb +18 -0
- data/app/services/solidus_bolt/server_error.rb +6 -0
- data/app/services/solidus_bolt/transactions/authorize_service.rb +72 -0
- data/app/services/solidus_bolt/transactions/base_service.rb +28 -0
- data/app/services/solidus_bolt/transactions/capture_service.rb +46 -0
- data/app/services/solidus_bolt/transactions/detail_service.rb +38 -0
- data/app/services/solidus_bolt/transactions/refund_service.rb +46 -0
- data/app/services/solidus_bolt/transactions/void_service.rb +44 -0
- data/app/services/solidus_bolt/users/refresh_access_token_service.rb +44 -0
- data/app/services/solidus_bolt/users/sync_addresses_service.rb +49 -0
- data/app/services/solidus_bolt/users/sync_payment_sources_service.rb +50 -0
- data/app/services/solidus_bolt/webhooks/create_service.rb +52 -0
- data/app/views/spree/admin/bolt_webhooks/new.html.erb +22 -0
- data/app/views/spree/admin/bolts/_configuration.html.erb +32 -0
- data/app/views/spree/admin/bolts/_form.html.erb +29 -0
- data/app/views/spree/admin/bolts/edit.html.erb +6 -0
- data/app/views/spree/admin/bolts/show.html.erb +21 -0
- data/app/views/spree/admin/payments/source_forms/_bolt.html.erb +1 -0
- data/app/views/spree/admin/payments/source_views/_bolt.html.erb +2 -0
- data/app/views/spree/api/payments/source_views/_bolt.json.jbuilder +4 -0
- data/app/views/spree/checkout/existing_payment/_bolt.html.erb +7 -0
- data/app/views/spree/checkout/payment/_bolt.html.erb +1 -0
- data/app/views/spree/shared/payment/_bolt.html.erb +19 -0
- data/app/webhooks/solidus_bolt/handlers/base_handler.rb +27 -0
- data/app/webhooks/solidus_bolt/handlers/capture_handler.rb +13 -0
- data/app/webhooks/solidus_bolt/handlers/credit_handler.rb +18 -0
- data/app/webhooks/solidus_bolt/handlers/void_handler.rb +11 -0
- data/app/webhooks/solidus_bolt/sorter.rb +33 -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/config/initializers/menu_items.rb +14 -0
- data/config/locales/en.yml +17 -0
- data/config/routes.rb +11 -0
- data/db/migrate/20220330094232_create_solidus_bolt_payment_sources.rb +16 -0
- data/db/migrate/20220413063328_create_solidus_bolt_bolt_configurations.rb +14 -0
- data/db/migrate/20220502005041_swap_url_for_env_boolean_in_bolt_configuration.rb +6 -0
- data/db/migrate/20220509102309_rework_solidus_bolt_payment_sources.rb +19 -0
- data/db/migrate/20220510075227_add_create_bolt_account_to_solidus_bolt_payment_source.rb +5 -0
- data/db/migrate/20220519233043_add_user_to_solidus_bolt_payment_source.rb +5 -0
- data/db/migrate/20220526005619_remove_user_id_from_solidus_bolt_payment_source.rb +5 -0
- data/db/migrate/20220530102107_rename_bolt_configuration_merchant_id_to_division_public_id.rb +5 -0
- data/db/migrate/20220531075527_update_bolt_configuration_environment_column_restrictions.rb +6 -0
- data/db/seeds.rb +30 -0
- data/lib/generators/solidus_bolt/install/install_generator.rb +78 -0
- data/lib/generators/solidus_bolt/install/templates/initializer.rb +8 -0
- data/lib/solidus_bolt/configuration.rb +19 -0
- data/lib/solidus_bolt/engine.rb +62 -0
- data/lib/solidus_bolt/testing_support/factories.rb +32 -0
- data/lib/solidus_bolt/version.rb +5 -0
- data/lib/solidus_bolt.rb +9 -0
- data/lib/tasks/db/seed/solidus_bolt.rake +14 -0
- data/lib/views/frontend/spree/shared/_login_bar_items.html.erb +18 -0
- data/solidus_bolt.gemspec +46 -0
- data/spec/decorators/models/solidus_bolt/address_decorator_spec.rb +24 -0
- data/spec/decorators/models/solidus_bolt/order_decorator_spec.rb +36 -0
- data/spec/decorators/models/solidus_bolt/payment_decorator_spec.rb +30 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_AddAddressService/_call/with_correct_access_token/receives_a_successful_response.yml +137 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_AddAddressService/_call/with_existing_address/skips_the_add_address_call.yml +82 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_AddAddressService/_call/with_wrong_access_token/gives_an_error.yml +55 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_AddPaymentMethodService/_call/with_correct_access_token/receives_a_successful_response.yml +186 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_AddPaymentMethodService/_call/with_wrong_access_token/gives_an_error.yml +179 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_DetailService/_call/with_correct_access_token/receives_a_successful_response.yml +55 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_DetailService/_call/with_wrong_access_token/gives_an_error.yml +55 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_DetectAccountService/_call/receives_the_correct_response.yml +50 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_DetectAccountService/_call/returns_status_200.yml +50 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Oauth_TokenService/_call/makes_the_API_call.yml +59 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_AuthorizeService/when_repeat_payment/_call/makes_the_API_call.yml +305 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_AuthorizeService/with_auto_capture/_call/makes_the_API_call.yml +307 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_AuthorizeService/without_auto_capture/_call/makes_the_API_call.yml +252 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_CaptureService/_call/makes_the_API_call.yml +242 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_DetailService/_call/makes_the_API_call.yml +244 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_RefundService/_call/makes_the_API_call.yml +296 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_VoidService/_call/makes_the_API_call.yml +242 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_VoidService/_call/when_transaction_id_is_missing/makes_the_API_call.yml +242 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_VoidService/_call/when_transaction_reference_is_missing/makes_the_API_call.yml +242 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Users_SyncAddressesService/_call/adds_the_bill_address_to_the_user.yml +165 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Users_SyncAddressesService/_call/adds_the_ship_address_to_the_user.yml +165 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Users_SyncPaymentSourcesService/_call/creates_a_new_payment_source_with_card_ID.yml +55 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Webhooks_CreateService/_call/with_all_event/returns_a_webhook_id.yml +54 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Webhooks_CreateService/_call/with_an_event/returns_a_webhook_id.yml +54 -0
- data/spec/fixtures/vcr_cassettes/SolidusBolt_Webhooks_CreateService/_call/with_empty_event/raises_a_server_error.yml +57 -0
- data/spec/jobs/solidus_bolt/add_address_job_spec.rb +18 -0
- data/spec/models/solidus_bolt/bolt_configuration_spec.rb +173 -0
- data/spec/models/solidus_bolt/gateway_spec.rb +130 -0
- data/spec/models/solidus_bolt/payment_method_spec.rb +21 -0
- data/spec/models/solidus_bolt/payment_source_spec.rb +22 -0
- data/spec/requests/solidus_bolt/accounts_controller_spec.rb +41 -0
- data/spec/requests/solidus_bolt/webhooks_controller_spec.rb +122 -0
- data/spec/requests/spree/admin/bolt_spec.rb +71 -0
- data/spec/requests/spree/admin/bolt_webhook_spec.rb +35 -0
- data/spec/requests/spree/checkout_controller_spec.rb +117 -0
- data/spec/services/solidus_bolt/accounts/add_address_service_spec.rb +45 -0
- data/spec/services/solidus_bolt/accounts/add_payment_method_service_spec.rb +47 -0
- data/spec/services/solidus_bolt/accounts/detail_service_spec.rb +32 -0
- data/spec/services/solidus_bolt/accounts/detect_account_service_spec.rb +15 -0
- data/spec/services/solidus_bolt/base_service_spec.rb +49 -0
- data/spec/services/solidus_bolt/oauth/token_service_spec.rb +15 -0
- data/spec/services/solidus_bolt/payments/capture_sync_service_spec.rb +27 -0
- data/spec/services/solidus_bolt/payments/credit_sync_service_spec.rb +38 -0
- data/spec/services/solidus_bolt/payments/void_sync_service_spec.rb +25 -0
- data/spec/services/solidus_bolt/transactions/authorize_service_spec.rb +117 -0
- data/spec/services/solidus_bolt/transactions/base_service_spec.rb +38 -0
- data/spec/services/solidus_bolt/transactions/capture_service_spec.rb +37 -0
- data/spec/services/solidus_bolt/transactions/detail_service_spec.rb +31 -0
- data/spec/services/solidus_bolt/transactions/refund_service_spec.rb +42 -0
- data/spec/services/solidus_bolt/transactions/void_service_spec.rb +70 -0
- data/spec/services/solidus_bolt/users/refresh_access_token_service_spec.rb +45 -0
- data/spec/services/solidus_bolt/users/sync_addresses_service_spec.rb +74 -0
- data/spec/services/solidus_bolt/users/sync_payment_sources_service_spec.rb +25 -0
- data/spec/services/solidus_bolt/webhooks/create_service_spec.rb +33 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/support/bolt_configuration.rb +26 -0
- data/spec/support/bolt_helper.rb +66 -0
- data/spec/support/vcr.rb +29 -0
- data/spec/webhooks/solidus_bolt/handlers/base_handler_spec.rb +13 -0
- data/spec/webhooks/solidus_bolt/handlers/capture_handler_spec.rb +24 -0
- data/spec/webhooks/solidus_bolt/handlers/credit_handler_spec.rb +32 -0
- data/spec/webhooks/solidus_bolt/handlers/void_handler_spec.rb +19 -0
- data/spec/webhooks/solidus_bolt/sorter_spec.rb +39 -0
- metadata +492 -0
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe SolidusBolt::Gateway, type: :model do
|
4
|
+
let(:order) { create(:order_with_line_items) }
|
5
|
+
let(:amount) { (order.total * 100).to_i }
|
6
|
+
let(:payment_method) { create(:bolt_payment_method) }
|
7
|
+
let(:payment_source) { create(:bolt_payment_source, payment_method: payment_method) }
|
8
|
+
let(:payment) {
|
9
|
+
create(:payment, order: order, source_id: payment_source.id, source_type: SolidusBolt::PaymentSource,
|
10
|
+
payment_method: payment_method)
|
11
|
+
}
|
12
|
+
let(:gateway_options) {
|
13
|
+
{
|
14
|
+
order_id: "#{order.number}-123456",
|
15
|
+
originator: payment
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
describe '#authorize' do
|
20
|
+
subject(:authorize) { described_class.new.authorize(nil, payment_source, gateway_options) }
|
21
|
+
|
22
|
+
let(:response) { { 'transaction' => { 'reference' => 'fakereference', 'from_credit_card' => { 'id' => '1234' } } } }
|
23
|
+
|
24
|
+
before { allow(SolidusBolt::Transactions::AuthorizeService).to receive(:call).and_return(response) }
|
25
|
+
|
26
|
+
it 'updates the card_id of the payment_source' do
|
27
|
+
authorize
|
28
|
+
expect(payment_source.reload.card_id).to eq('1234')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns an active merchant billing response' do
|
32
|
+
expect(authorize).to be_an_instance_of(ActiveMerchant::Billing::Response)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'stores the transaction reference as response code' do
|
36
|
+
expect(authorize.authorization).to eq('fakereference')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#capture' do
|
41
|
+
subject(:capture) { described_class.new.capture(amount, response_code, gateway_options) }
|
42
|
+
|
43
|
+
let(:response_code) { 'the_amazing_spiderman' }
|
44
|
+
let(:response) {
|
45
|
+
{ 'reference' => response_code }
|
46
|
+
}
|
47
|
+
|
48
|
+
before do
|
49
|
+
allow(SolidusBolt::Transactions::CaptureService).to receive(:call).and_return(response)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns an active merchant billing response' do
|
53
|
+
expect(capture).to be_an_instance_of(ActiveMerchant::Billing::Response)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'stores the transaction reference as response code' do
|
57
|
+
expect(capture.authorization).to eq response_code
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#void' do
|
62
|
+
subject(:void) { described_class.new.void(response_code, gateway_options) }
|
63
|
+
|
64
|
+
let(:response_code) { 'the_amazing_spiderman' }
|
65
|
+
|
66
|
+
let(:response) {
|
67
|
+
{ 'id' => "id-#{response_code}", 'reference' => response_code }
|
68
|
+
}
|
69
|
+
|
70
|
+
before do
|
71
|
+
allow(SolidusBolt::Transactions::VoidService).to receive(:call).and_return(response)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'returns an active merchant billing response' do
|
75
|
+
expect(void).to be_an_instance_of(ActiveMerchant::Billing::Response)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'stores the transaction reference as response code' do
|
79
|
+
expect(void.authorization).to eq response_code
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#credit' do
|
84
|
+
subject(:credit) { described_class.new.credit(amount, response_code, gateway_options) }
|
85
|
+
|
86
|
+
let(:gateway_options) { { originator: Spree::Refund.new(payment_id: payment.id, amount: payment.amount) } }
|
87
|
+
let(:response_code) { 'the_amazing_spiderman' }
|
88
|
+
|
89
|
+
# Since reference returned by Refund API Call is different
|
90
|
+
# from the reference for the original transaction, the refernce has been
|
91
|
+
# randomised in the response here
|
92
|
+
let(:response) {
|
93
|
+
{ 'reference' => SecureRandom.hex }
|
94
|
+
}
|
95
|
+
|
96
|
+
before do
|
97
|
+
allow(SolidusBolt::Transactions::RefundService).to receive(:call).and_return(response)
|
98
|
+
payment.update(response_code: response_code)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'returns an active merchant billing response' do
|
102
|
+
expect(credit).to be_an_instance_of(ActiveMerchant::Billing::Response)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'stores the transaction reference as response code' do
|
106
|
+
expect(credit.authorization).to eq response['reference']
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#purchase' do
|
111
|
+
subject(:purchase) { described_class.new.purchase(nil, payment_source, gateway_options) }
|
112
|
+
|
113
|
+
let(:response) { { 'transaction' => { 'reference' => 'fakereference', 'from_credit_card' => { 'id' => '1234' } } } }
|
114
|
+
|
115
|
+
before { allow(SolidusBolt::Transactions::AuthorizeService).to receive(:call).and_return(response) }
|
116
|
+
|
117
|
+
it 'updates the card_id of the payment_source' do
|
118
|
+
purchase
|
119
|
+
expect(payment_source.reload.card_id).to eq('1234')
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'returns an active merchant billing response' do
|
123
|
+
expect(purchase).to be_an_instance_of(ActiveMerchant::Billing::Response)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'stores the transaction reference as response code' do
|
127
|
+
expect(purchase.authorization).to eq('fakereference')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe SolidusBolt::PaymentMethod, type: :model do
|
4
|
+
describe '#gateway_class' do
|
5
|
+
it 'has correct gateway class' do
|
6
|
+
expect(described_class.new.gateway_class).to eq SolidusBolt::Gateway
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#payment_source_class' do
|
11
|
+
it 'has correct payment_source class' do
|
12
|
+
expect(described_class.new.payment_source_class).to eq SolidusBolt::PaymentSource
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#partial_name' do
|
17
|
+
it 'has correct partial name' do
|
18
|
+
expect(described_class.new.partial_name).to eq 'bolt'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe SolidusBolt::PaymentSource, type: :model do
|
4
|
+
let(:payment_source) { build(:bolt_payment_source) }
|
5
|
+
|
6
|
+
describe 'validations' do
|
7
|
+
context 'with payment_method_id present' do
|
8
|
+
it 'is valid' do
|
9
|
+
expect(payment_source.valid?).to be(true)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'with payment_method_id absent' do
|
14
|
+
before { payment_source.payment_method_id = nil }
|
15
|
+
|
16
|
+
it 'is invalid' do
|
17
|
+
expect(payment_source.valid?).to be(false)
|
18
|
+
expect(payment_source.errors.messages.first.to_a).to eq([:payment_method_id, ["can't be blank"]])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::AccountsController, type: :request do
|
6
|
+
describe '#create' do
|
7
|
+
subject(:call) do
|
8
|
+
post '/api/accounts/bolt', params: params, headers: { 'X-Bolt-Hmac-Sha256' => bolt_hash }, as: :json
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:user) { create(:user, email: 'user@bolt.com') }
|
12
|
+
let(:params) { { account: { email: user.email } } }
|
13
|
+
|
14
|
+
before { call }
|
15
|
+
|
16
|
+
context 'when valid' do
|
17
|
+
let(:bolt_hash) { "+mDzzN0xsvB0UzO0NoAyMJYx/byPs++cccpR4tiEN0c=" }
|
18
|
+
|
19
|
+
it 'has http status ok' do
|
20
|
+
expect(response).to have_http_status(:ok)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when not valid' do
|
25
|
+
let(:bolt_hash) { "CaAA/XZsO4wl6q/G7cyWY9KVcaWvieH7UWM6XoFcsmU=" }
|
26
|
+
let(:params) { { account: { email: 'fake@email.com' } } }
|
27
|
+
|
28
|
+
it 'has http status not found' do
|
29
|
+
expect(response).to have_http_status(:not_found)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when not bolt request' do
|
34
|
+
let(:bolt_hash) { 'notBoltHash' }
|
35
|
+
|
36
|
+
it 'has http status unauthorized' do
|
37
|
+
expect(response).to have_http_status(:unauthorized)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::WebhooksController, type: :request do
|
6
|
+
describe '#update' do
|
7
|
+
subject(:endpoint_call) do
|
8
|
+
post '/webhooks/bolt', params: params, headers: { 'X-Bolt-Hmac-Sha256' => bolt_hash }, as: :json
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:bolt_hash) { "IvjuqQlACvmK3zaBqfMZI+9rf8ukq7VT2Sgjo+nVwl4=" }
|
12
|
+
let(:params) { { webhook: {} } }
|
13
|
+
let(:payment) { create(:bolt_payment, response_code: 'V2YW-NYNR-2MYM') }
|
14
|
+
|
15
|
+
context 'when valid' do
|
16
|
+
let(:expected_params) { ActionController::Parameters.new({}) }
|
17
|
+
|
18
|
+
before do
|
19
|
+
allow(::SolidusBolt::Sorter).to receive(:call)
|
20
|
+
endpoint_call
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'has http status success' do
|
24
|
+
expect(response).to have_http_status(:success)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'calls the webhook sorter with the correct params' do
|
28
|
+
expect(SolidusBolt::Sorter).to have_received(:call).with(expected_params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'when webhook type is `capture`' do
|
33
|
+
let(:params) do
|
34
|
+
{
|
35
|
+
webhook: {
|
36
|
+
type: 'capture',
|
37
|
+
data: { reference: payment.response_code, captures: [{ amount: { amount: 1000 } }] }
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
let(:bolt_hash) { "UlgQupKN61uY+5G126Ue0CvOPLtuHux36GZrAA1LKyo=" }
|
42
|
+
|
43
|
+
before do
|
44
|
+
allow(SolidusBolt::Payments::CaptureSyncService).to receive(:call).with(payment: payment, capture_amount: 1000)
|
45
|
+
endpoint_call
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'calls the Sorter, which calls the CaptureHandler, which calls the CaptureSyncService with params' do
|
49
|
+
expect(SolidusBolt::Payments::CaptureSyncService).to have_received(:call).with(
|
50
|
+
payment: payment, capture_amount: 1000
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when webhook type is `void`' do
|
56
|
+
let(:params) { { webhook: { type: 'void', data: { reference: payment.response_code } } } }
|
57
|
+
let(:bolt_hash) { "W4+7RvJLQaBkLdddmCnAy59QFPrF3No2olkTcfdNmVE=" }
|
58
|
+
|
59
|
+
before do
|
60
|
+
allow(SolidusBolt::Payments::VoidSyncService).to receive(:call).with(payment: payment)
|
61
|
+
endpoint_call
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'calls the Sorter, which calls the VoidHandler, which calls the VoidSyncService with params' do
|
65
|
+
expect(SolidusBolt::Payments::VoidSyncService).to have_received(:call).with(payment: payment)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when webhook type is `credit`' do
|
70
|
+
let(:transaction_id) { 'AAAA-BBBB-CCCC' }
|
71
|
+
let(:params) do
|
72
|
+
{
|
73
|
+
webhook: {
|
74
|
+
type: 'credit',
|
75
|
+
data: {
|
76
|
+
reference: transaction_id,
|
77
|
+
source_transaction: { reference: payment.response_code },
|
78
|
+
requested_refund_amount: { amount: 1000 }
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
end
|
83
|
+
let(:bolt_hash) { "My7opJkmglzXpNi1rn/UDmPeaeDHoSm2ebuWwBYJrW0=" }
|
84
|
+
|
85
|
+
before do
|
86
|
+
allow(SolidusBolt::Payments::CreditSyncService).to receive(:call).with(
|
87
|
+
payment: payment, amount: 1000, transaction_id: transaction_id
|
88
|
+
)
|
89
|
+
endpoint_call
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'calls the Sorter, which calls the CreditHandler, which calls the CreditSyncService with params' do
|
93
|
+
expect(SolidusBolt::Payments::CreditSyncService).to have_received(:call).with(
|
94
|
+
payment: payment, amount: 1000, transaction_id: transaction_id
|
95
|
+
)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'when not valid' do
|
100
|
+
before do
|
101
|
+
allow(::SolidusBolt::Sorter).to receive(:call).and_raise(StandardError)
|
102
|
+
endpoint_call
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'has http status unprocessable entity' do
|
106
|
+
expect(response).to have_http_status(:unprocessable_entity)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when not bolt request' do
|
111
|
+
let(:bolt_hash) { 'notBoltHash' }
|
112
|
+
|
113
|
+
before do
|
114
|
+
endpoint_call
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'has http status unauthorized' do
|
118
|
+
expect(response).to have_http_status(:unauthorized)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe "Spree::Admin::Bolts", type: :request do
|
4
|
+
stub_authorization!
|
5
|
+
|
6
|
+
let(:bolt_configuration_params) {
|
7
|
+
{
|
8
|
+
bearer_token: SecureRandom.hex,
|
9
|
+
environment: 'sandbox',
|
10
|
+
merchant_public_id: SecureRandom.hex,
|
11
|
+
division_public_id: SecureRandom.hex,
|
12
|
+
api_key: SecureRandom.hex,
|
13
|
+
signing_secret: SecureRandom.hex,
|
14
|
+
publishable_key: SecureRandom.hex
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
describe "GET /show" do
|
19
|
+
it 'returns a successful response' do
|
20
|
+
get '/admin/bolt'
|
21
|
+
expect(response.status).to eq 200
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'creates a new SolidusBolt::BoltConfiguration record if no records are present' do
|
25
|
+
expect { get '/admin/bolt' }.to change { SolidusBolt::BoltConfiguration.count }.by(1)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "GET /edit" do
|
30
|
+
let(:bolt_configuration) { create(:bolt_configuration) }
|
31
|
+
|
32
|
+
it 'returns a successful response' do
|
33
|
+
get '/admin/bolt/edit'
|
34
|
+
expect(response.status).to eq 200
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "PUT /update" do
|
39
|
+
subject(:request) {
|
40
|
+
put '/admin/bolt', params: { solidus_bolt_bolt_configuration: bolt_configuration_params }
|
41
|
+
}
|
42
|
+
|
43
|
+
let(:bolt_configuration) { create(:bolt_configuration) }
|
44
|
+
|
45
|
+
it 'successfully redirects' do
|
46
|
+
request
|
47
|
+
expect(response.status).to eq 302
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'redirects to index' do
|
51
|
+
request
|
52
|
+
expect(response).to redirect_to '/admin/bolt'
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'successfully updates the bolt configuration' do
|
56
|
+
request
|
57
|
+
|
58
|
+
updated_attributes = SolidusBolt::BoltConfiguration.fetch.attributes.slice(
|
59
|
+
'bearer_token',
|
60
|
+
'environment',
|
61
|
+
'merchant_public_id',
|
62
|
+
'division_public_id',
|
63
|
+
'api_key',
|
64
|
+
'signing_secret',
|
65
|
+
'publishable_key'
|
66
|
+
)
|
67
|
+
|
68
|
+
expect(updated_attributes).to eq(bolt_configuration_params.deep_stringify_keys)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe "Spree::Admin::BoltWebhooks", type: :request do
|
4
|
+
stub_authorization!
|
5
|
+
|
6
|
+
describe "GET /new" do
|
7
|
+
it 'returns a successful response' do
|
8
|
+
get '/admin/bolt_webhook/new'
|
9
|
+
expect(response.status).to eq 200
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "POST /create" do
|
14
|
+
subject(:request) {
|
15
|
+
post '/admin/bolt_webhook', params: { bolt_webhook: params }
|
16
|
+
}
|
17
|
+
|
18
|
+
let(:params) { { event: 'all', webhook_url: 'https://solidus-test.com/webhook' } }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(SolidusBolt::Webhooks::CreateService).to receive(:call).and_return({ 'webhook_id' => 'BOLT_WEBHOOK_ID' })
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'calls the correct service' do
|
25
|
+
request
|
26
|
+
expect(
|
27
|
+
SolidusBolt::Webhooks::CreateService
|
28
|
+
)
|
29
|
+
.to have_received(:call)
|
30
|
+
.with(
|
31
|
+
{ event: 'all', url: 'https://solidus-test.com/webhook' }
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe "Spree::CheckoutController", type: :request do
|
4
|
+
stub_authorization!
|
5
|
+
|
6
|
+
let(:user) { create(:user_with_addresses) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
# rubocop:disable RSpec/AnyInstance
|
10
|
+
allow_any_instance_of(Spree::CheckoutController).to receive(:current_order).and_return(order)
|
11
|
+
allow_any_instance_of(Spree::CheckoutController).to receive(:spree_current_user).and_return(user)
|
12
|
+
allow_any_instance_of(SolidusBolt::BoltConfiguration).to(
|
13
|
+
receive(:embed_js).and_return('https://connect-sandbox.bolt.com/embed.js')
|
14
|
+
)
|
15
|
+
# rubocop:enable RSpec/AnyInstance
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "GET /checkout/address" do
|
19
|
+
let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:address) }
|
20
|
+
|
21
|
+
before { allow(SolidusBolt::Users::SyncAddressesService).to receive(:call) }
|
22
|
+
|
23
|
+
it 'returns a successful response' do
|
24
|
+
get '/checkout/address'
|
25
|
+
|
26
|
+
expect(response.status).to eq 200
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'calls the service to sync addresses' do
|
30
|
+
get '/checkout/address'
|
31
|
+
|
32
|
+
expect(SolidusBolt::Users::SyncAddressesService).to have_received(:call)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "GET /checkout/payment" do
|
37
|
+
let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:payment) }
|
38
|
+
|
39
|
+
before { allow(SolidusBolt::Users::SyncPaymentSourcesService).to receive(:call) }
|
40
|
+
|
41
|
+
it 'returns a successful response' do
|
42
|
+
get '/checkout/payment'
|
43
|
+
|
44
|
+
expect(response.status).to eq 200
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'calls the service to sync payment sources' do
|
48
|
+
get '/checkout/payment'
|
49
|
+
|
50
|
+
expect(SolidusBolt::Users::SyncPaymentSourcesService).to have_received(:call)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'PATCH /checkout/update/confirm' do
|
55
|
+
subject(:confirm_order) { patch '/checkout/update/confirm' }
|
56
|
+
|
57
|
+
let(:order) { FactoryBot.create(:order_with_totals) }
|
58
|
+
let(:payment) { create(:payment, amount: order.total, order: order) }
|
59
|
+
let(:session) { { bolt_access_token: access_token } }
|
60
|
+
let(:access_token) { nil }
|
61
|
+
|
62
|
+
before do
|
63
|
+
# use test preparation from solidusio/solidus/frontend/spec/controllers/spree/checkout_controller_spec.rb
|
64
|
+
# because Spree::TestingSupport::OrderWalkthrough.up_to(:confirm) doesn't work
|
65
|
+
order.update! user: user
|
66
|
+
order.update(state: 'confirm')
|
67
|
+
payment
|
68
|
+
order.create_proposed_shipments
|
69
|
+
order.payments.reload
|
70
|
+
|
71
|
+
# request calls Gateway#authorize - need to stub it to test our action
|
72
|
+
allow(SolidusBolt::Transactions::AuthorizeService).to(receive(:call).and_return({
|
73
|
+
'transaction' => { 'from_credit_card' => { 'id' => 'CreditCardId' } }
|
74
|
+
}))
|
75
|
+
|
76
|
+
# rubocop:disable RSpec/AnyInstance
|
77
|
+
allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return(session)
|
78
|
+
# rubocop:enable RSpec/AnyInstance
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'redirects to completion route' do
|
82
|
+
confirm_order
|
83
|
+
expect(response).to redirect_to spree.order_path(order)
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'with logged in Bolt user and Bolt payment' do
|
87
|
+
let(:access_token) { 'accesstoken' }
|
88
|
+
let(:payment) { create(:bolt_payment, amount: order.total, order: order) }
|
89
|
+
|
90
|
+
before { allow(SolidusBolt::Users::RefreshAccessTokenService).to receive(:call).and_return(access_token) }
|
91
|
+
|
92
|
+
it 'calls the job to add addresses' do
|
93
|
+
expect { confirm_order }.to(have_enqueued_job(SolidusBolt::AddAddressJob).twice.with { |hash|
|
94
|
+
expect(hash[:order]).to eq(order)
|
95
|
+
expect(hash[:access_token]).to eq(access_token)
|
96
|
+
expect(user.addresses).to include(hash[:address])
|
97
|
+
})
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'with logged in Bolt user' do
|
102
|
+
let(:access_token) { 'accesstoken' }
|
103
|
+
|
104
|
+
it 'skips the job call to add addresses' do
|
105
|
+
expect { confirm_order }.not_to have_enqueued_job(SolidusBolt::AddAddressJob)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'with Bolt payment' do
|
110
|
+
let(:payment) { create(:bolt_payment, amount: order.total, order: order) }
|
111
|
+
|
112
|
+
it 'skips the job call to add addresses' do
|
113
|
+
expect { confirm_order }.not_to have_enqueued_job(SolidusBolt::AddAddressJob)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Accounts::AddAddressService, :vcr, :bolt_configuration do
|
6
|
+
describe '#call', vcr: true do
|
7
|
+
subject(:add_address) { described_class.call(access_token: access_token, order: order, address: address) }
|
8
|
+
|
9
|
+
let(:order) { build(:order) }
|
10
|
+
let(:address) { order.bill_address }
|
11
|
+
|
12
|
+
context 'with wrong access_token' do
|
13
|
+
let(:access_token) { 'Bolt Access Token' }
|
14
|
+
|
15
|
+
it 'gives an error' do
|
16
|
+
expect{ add_address }.to raise_error(SolidusBolt::ServerError, 'This action is forbidden.')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'with correct access_token' do
|
21
|
+
let(:access_token) { ENV['BOLT_ACCESS_TOKEN'] }
|
22
|
+
let(:address) { build(:address, address1: '6420') }
|
23
|
+
|
24
|
+
it 'receives a successful response' do
|
25
|
+
expect(add_address).to match hash_including('id')
|
26
|
+
expect(add_address['street_address1']).to eq(address.address1)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with existing address' do
|
31
|
+
let(:access_token) { ENV['BOLT_ACCESS_TOKEN'] }
|
32
|
+
|
33
|
+
let(:address) { build(:address, address1: 'PO Box 1337', zipcode: '10001') }
|
34
|
+
|
35
|
+
it 'skips the add_address call' do
|
36
|
+
expect(add_address).to be(nil)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# This spec depends on the bolt_access_token that needs to be generated manually
|
43
|
+
# and added to the environment variable BOLT_ACCESS_TOKEN.
|
44
|
+
# Generate a new access token by logging into an existing user bolt account on
|
45
|
+
# development environment and copying the token from the session['bolt_access_token'] key.
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Accounts::AddPaymentMethodService, :vcr, :bolt_configuration do
|
6
|
+
describe '#call', vcr: true do
|
7
|
+
subject(:add_payment_method) do
|
8
|
+
described_class.call(
|
9
|
+
access_token: access_token, credit_card: credit_card_payload, address: address, email: 'example@email.com'
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:address) { build(:address) }
|
14
|
+
let(:card_number) { '4111111111111004' }
|
15
|
+
let(:credit_card_payload) do
|
16
|
+
tokenize_credit_card(credit_card_number: card_number, cvv: '111')
|
17
|
+
.merge(
|
18
|
+
number: card_number,
|
19
|
+
expiration: (Time.current + 1.year).strftime('%Y-%m'),
|
20
|
+
token_type: 'bolt',
|
21
|
+
postal_code: address.zipcode
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with wrong access_token' do
|
26
|
+
let(:access_token) { 'Bolt Access Token' }
|
27
|
+
|
28
|
+
it 'gives an error' do
|
29
|
+
expect{ add_payment_method }.to raise_error(SolidusBolt::ServerError, 'This action is forbidden.')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with correct access_token' do
|
34
|
+
let(:access_token) { ENV['BOLT_ACCESS_TOKEN'] }
|
35
|
+
|
36
|
+
it 'receives a successful response' do
|
37
|
+
expect(add_payment_method).to match hash_including('id')
|
38
|
+
expect(add_payment_method['last4']).to eq('1004')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# This spec depends on the bolt_access_token that needs to be generated manually
|
45
|
+
# and added to the environment variable BOLT_ACCESS_TOKEN.
|
46
|
+
# Generate a new access token by logging into an existing user bolt account on
|
47
|
+
# development environment and copying the token from the session['bolt_access_token'] key.
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Accounts::DetailService, :vcr, :bolt_configuration do
|
6
|
+
describe '#call', vcr: true do
|
7
|
+
subject(:detail) { described_class.call(access_token: access_token) }
|
8
|
+
|
9
|
+
context 'with wrong access_token' do
|
10
|
+
let(:access_token) { 'Bolt Access Token' }
|
11
|
+
|
12
|
+
it 'gives an error' do
|
13
|
+
expect{ detail }.to raise_error(SolidusBolt::ServerError, 'This action is forbidden.')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'with correct access_token' do
|
18
|
+
let(:access_token) { ENV['BOLT_ACCESS_TOKEN'] }
|
19
|
+
|
20
|
+
it 'receives a successful response' do
|
21
|
+
expect(detail).to match hash_including('profile')
|
22
|
+
expect(detail).to match hash_including('addresses')
|
23
|
+
expect(detail).to match hash_including('payment_methods')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# This spec depends on the bolt_access_token that needs to be generated manually
|
30
|
+
# and added to the environment variable BOLT_ACCESS_TOKEN.
|
31
|
+
# Generate a new access token by logging into an existing user bolt account on
|
32
|
+
# development environment and copying the token from the session['bolt_access_token'] key.
|