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,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Accounts::DetectAccountService, :vcr, :bolt_configuration do
|
6
|
+
subject(:api) { described_class.new(email: email) }
|
7
|
+
|
8
|
+
let(:email) { 'bolt@bolt.com' }
|
9
|
+
|
10
|
+
describe '#call', vcr: true do
|
11
|
+
it 'receives the correct response' do
|
12
|
+
expect(api.call).to eq({ "has_bolt_account" => false })
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::BaseService do
|
6
|
+
let!(:bolt_configuration) { create(:bolt_configuration) }
|
7
|
+
|
8
|
+
describe '#call' do
|
9
|
+
subject(:service) { described_class.new.call }
|
10
|
+
|
11
|
+
it 'raises a not implemented error' do
|
12
|
+
expect { service }.to raise_error(::NotImplementedError)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#api_base_url' do
|
17
|
+
it 'returns correct url' do
|
18
|
+
expect(described_class.new.send(:api_base_url)).to eq bolt_configuration.environment_url
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#api_version' do
|
23
|
+
it 'returns correct api version' do
|
24
|
+
expect(described_class.new.send(:api_version)).to eq 'v1'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#authentication_header' do
|
29
|
+
it 'returns the correct hash' do
|
30
|
+
expect(
|
31
|
+
described_class.new.send(:authentication_header)
|
32
|
+
).to eq({ 'X-API-KEY' => bolt_configuration.api_key })
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#generate_nonce' do
|
37
|
+
it 'returns a unique nonce everytime' do
|
38
|
+
nonce = described_class.new.send(:generate_nonce)
|
39
|
+
expect(described_class.new.send(:generate_nonce)).not_to eq(nonce)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#publishable_key' do
|
44
|
+
it 'returns the correct key' do
|
45
|
+
publishable_key = described_class.new.send(:publishable_key)
|
46
|
+
expect(described_class.new.send(:publishable_key)).to eq(publishable_key)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Oauth::TokenService, :vcr, :bolt_configuration do
|
6
|
+
subject(:api) {
|
7
|
+
described_class.new(authorization_code: 'Bolt Authorization Code', scope: 'openid bolt.account.manage')
|
8
|
+
}
|
9
|
+
|
10
|
+
describe '#call', vcr: true do
|
11
|
+
it 'makes the API call' do
|
12
|
+
expect { api.call }.to raise_error SolidusBolt::ServerError
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Payments::CaptureSyncService do
|
6
|
+
subject(:capture) do
|
7
|
+
described_class.call(payment: payment, capture_amount: capture_amount)
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:payment) { create(:bolt_payment, amount: 15.00) }
|
11
|
+
let(:capture_amount) { 1000 }
|
12
|
+
|
13
|
+
describe '#call' do
|
14
|
+
it 'completes the payment' do
|
15
|
+
expect { capture }.to change { payment.reload.state }.from('checkout').to('completed')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'updates the payment amount' do
|
19
|
+
expect { capture }.to change { payment.reload.amount }.from(15.00).to(10.00)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'creates a capture event' do
|
23
|
+
capture
|
24
|
+
expect(payment.reload.capture_events.count).to eq(1)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Payments::CreditSyncService do
|
6
|
+
subject(:credit) { described_class.call(payment: payment, amount: amount, transaction_id: '123') }
|
7
|
+
|
8
|
+
let(:payment) { create(:bolt_payment, state: 'completed', amount: 10.0, order: create(:completed_order_with_totals)) }
|
9
|
+
let(:amount) { (payment.amount * 100).to_i }
|
10
|
+
|
11
|
+
describe '#call' do
|
12
|
+
before { create(:refund_reason, name: 'Return processing', mutable: false) }
|
13
|
+
|
14
|
+
context 'when not refunded already' do
|
15
|
+
it 'creates a new refund' do
|
16
|
+
credit
|
17
|
+
expect(payment.reload.refunds.count).to eq(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'creates a refund for the passed amount' do
|
21
|
+
credit
|
22
|
+
expect(payment.reload.refunds.first.amount).to eq(amount.to_f / 100)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'recalculates the order amount' do
|
26
|
+
expect { credit }.to change { payment.reload.order.payment_total }.from(10.0).to(0.0)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when already refunded' do
|
31
|
+
before { create(:refund, transaction_id: '123') }
|
32
|
+
|
33
|
+
it 'silently fails' do
|
34
|
+
expect(credit).to be_nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Payments::VoidSyncService do
|
6
|
+
subject(:void) { described_class.call(payment: payment) }
|
7
|
+
|
8
|
+
let(:payment) { create(:bolt_payment) }
|
9
|
+
|
10
|
+
describe '#call' do
|
11
|
+
context 'when non-void payment' do
|
12
|
+
it 'voids the payment' do
|
13
|
+
expect { void }.to change { payment.reload.state }.from('checkout').to('void')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when void payment' do
|
18
|
+
before { payment.update(state: 'void') }
|
19
|
+
|
20
|
+
it 'returns nil' do
|
21
|
+
expect(void).to be_nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Transactions::AuthorizeService, :vcr, :bolt_configuration do
|
6
|
+
let(:credit_card_payload) do
|
7
|
+
tokenize_credit_card(credit_card_number: '4111111111111004', cvv: '111').merge(
|
8
|
+
expiration: (Time.current + 1.year).strftime('%Y-%m'),
|
9
|
+
token_type: 'bolt'
|
10
|
+
)
|
11
|
+
end
|
12
|
+
let(:order) { create(:order_with_line_items) }
|
13
|
+
let(:payment_method) { create(:bolt_payment_method) }
|
14
|
+
|
15
|
+
context 'without auto capture' do
|
16
|
+
subject(:authorize) do
|
17
|
+
described_class.call(
|
18
|
+
order: order, credit_card: credit_card_payload, create_bolt_account: false, payment_method: payment_method
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#call' do
|
23
|
+
it 'makes the API call' do
|
24
|
+
response = authorize
|
25
|
+
|
26
|
+
expect(response).to match hash_including('transaction')
|
27
|
+
expect(response['transaction']['status']).to eq('pending')
|
28
|
+
|
29
|
+
# The Authorize Call with auto_capture takes some time to update the state of the transaction from
|
30
|
+
# pending to completed, hence the sleep is necessary to get the correct output from the API Call.
|
31
|
+
|
32
|
+
sleep(1)
|
33
|
+
|
34
|
+
reference = response['transaction']['reference']
|
35
|
+
transaction_details = SolidusBolt::Transactions::DetailService.call(
|
36
|
+
transaction_reference: reference, payment_method: payment_method
|
37
|
+
)
|
38
|
+
expect(transaction_details['status']).to eq('authorized')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with auto capture' do
|
44
|
+
subject(:authorize) do
|
45
|
+
described_class.call(
|
46
|
+
order: order,
|
47
|
+
credit_card: credit_card_payload,
|
48
|
+
create_bolt_account: false,
|
49
|
+
payment_method: payment_method,
|
50
|
+
auto_capture: true
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '#call', vcr: true do
|
55
|
+
it 'makes the API call' do
|
56
|
+
response = authorize
|
57
|
+
|
58
|
+
expect(response).to match hash_including('transaction')
|
59
|
+
expect(response['transaction']['status']).to eq 'pending'
|
60
|
+
|
61
|
+
# The Authorize Call with auto_capture takes some time to update the state of the transaction from
|
62
|
+
# pending to completed, hence the sleep is necessary to get the correct output from the API Call.
|
63
|
+
|
64
|
+
sleep(1)
|
65
|
+
|
66
|
+
reference = response['transaction']['reference']
|
67
|
+
transaction_details = SolidusBolt::Transactions::DetailService.call(
|
68
|
+
transaction_reference: reference, payment_method: payment_method
|
69
|
+
)
|
70
|
+
expect(transaction_details['order']['cart']['shipments'].first['shipping_address']).to be_present
|
71
|
+
expect(transaction_details['status']).to eq('completed')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when repeat payment', vcr: true do
|
77
|
+
let(:access_token) { ENV['BOLT_ACCESS_TOKEN'] }
|
78
|
+
|
79
|
+
describe '#call' do
|
80
|
+
it 'makes the API call' do
|
81
|
+
credit_card_id = SolidusBolt::Accounts::AddPaymentMethodService.call(
|
82
|
+
access_token: access_token,
|
83
|
+
credit_card: credit_card_payload.merge(number: '4111111111111111', postal_code: order.bill_address.zipcode),
|
84
|
+
address: order.bill_address,
|
85
|
+
email: order.email
|
86
|
+
)['id']
|
87
|
+
|
88
|
+
# The AddPaymentMethodService call takes some time to create the credit card on Bolt's side, hence the sleep is
|
89
|
+
# necessary to get the correct output from the API Call.
|
90
|
+
|
91
|
+
sleep(1)
|
92
|
+
|
93
|
+
response = described_class.call(
|
94
|
+
order: order,
|
95
|
+
credit_card: { id: credit_card_id },
|
96
|
+
payment_method: payment_method,
|
97
|
+
repeat: true,
|
98
|
+
create_bolt_account: false
|
99
|
+
)
|
100
|
+
|
101
|
+
expect(response).to match hash_including('transaction')
|
102
|
+
expect(response['transaction']['status']).to eq('pending')
|
103
|
+
|
104
|
+
# The Authorize Call with auto_capture takes some time to update the state of the transaction from
|
105
|
+
# pending to completed, hence the sleep is necessary to get the correct output from the API Call.
|
106
|
+
|
107
|
+
sleep(1)
|
108
|
+
|
109
|
+
reference = response['transaction']['reference']
|
110
|
+
transaction_details = SolidusBolt::Transactions::DetailService.call(
|
111
|
+
transaction_reference: reference, payment_method: payment_method
|
112
|
+
)
|
113
|
+
expect(transaction_details['status']).to eq('authorized')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Transactions::BaseService do
|
6
|
+
let!(:payment_method) { create(:bolt_payment_method) }
|
7
|
+
|
8
|
+
describe '#call' do
|
9
|
+
subject(:service) { described_class.new(payment_method: payment_method).call }
|
10
|
+
|
11
|
+
it 'raises a not implemented error' do
|
12
|
+
expect { service }.to raise_error(::NotImplementedError)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#api_base_url' do
|
17
|
+
it 'returns correct url' do
|
18
|
+
expect(
|
19
|
+
described_class.new(payment_method: payment_method).send(:api_base_url)
|
20
|
+
).to eq payment_method.preferred_bolt_api_url
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#authentication_header' do
|
25
|
+
it 'returns the correct hash' do
|
26
|
+
expect(
|
27
|
+
described_class.new(payment_method: payment_method).send(:authentication_header)
|
28
|
+
).to eq({ 'X-API-KEY' => payment_method.preferred_bolt_api_key })
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#publishable_key' do
|
33
|
+
it 'returns the correct key' do
|
34
|
+
publishable_key = described_class.new(payment_method: payment_method).send(:publishable_key)
|
35
|
+
expect(described_class.new(payment_method: payment_method).send(:publishable_key)).to eq(publishable_key)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Transactions::CaptureService, :vcr, :bolt_configuration do
|
6
|
+
subject(:api) do
|
7
|
+
described_class.new(
|
8
|
+
transaction_reference: reference, amount: amount, currency: currency, payment_method: payment_method
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:transaction) do
|
13
|
+
SolidusBolt::Transactions::AuthorizeService.call(
|
14
|
+
order: order, credit_card: credit_card_payload, create_bolt_account: false, payment_method: payment_method
|
15
|
+
)
|
16
|
+
end
|
17
|
+
let(:credit_card_payload) do
|
18
|
+
tokenize_credit_card(credit_card_number: '4111111111111004', cvv: '111').merge(
|
19
|
+
expiration: (Time.current + 1.year).strftime('%Y-%m'),
|
20
|
+
token_type: 'bolt'
|
21
|
+
)
|
22
|
+
end
|
23
|
+
let(:order) { create(:order_with_line_items) }
|
24
|
+
let(:reference) { transaction['transaction']['reference'] }
|
25
|
+
let(:amount) { 10 }
|
26
|
+
let(:currency) { 'USD' }
|
27
|
+
let(:payment_method) { create(:bolt_payment_method) }
|
28
|
+
|
29
|
+
describe '#call', vcr: true do
|
30
|
+
it 'makes the API call' do
|
31
|
+
response = api.call
|
32
|
+
|
33
|
+
expect(response['reference']).to eq(reference)
|
34
|
+
expect(response['capture']['status']).to eq 'succeeded'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Transactions::DetailService, :vcr, :bolt_configuration do
|
6
|
+
subject(:api) { described_class.new(transaction_reference: reference, payment_method: payment_method) }
|
7
|
+
|
8
|
+
let(:transaction) do
|
9
|
+
SolidusBolt::Transactions::AuthorizeService.call(
|
10
|
+
order: order, credit_card: credit_card_payload, create_bolt_account: false, payment_method: payment_method
|
11
|
+
)
|
12
|
+
end
|
13
|
+
let(:credit_card_payload) do
|
14
|
+
tokenize_credit_card(credit_card_number: '4111111111111004', cvv: '111').merge(
|
15
|
+
expiration: (Time.current + 1.year).strftime('%Y-%m'),
|
16
|
+
token_type: 'bolt'
|
17
|
+
)
|
18
|
+
end
|
19
|
+
let(:order) { create(:order_with_line_items) }
|
20
|
+
let(:reference) { transaction['transaction']['reference'] }
|
21
|
+
let(:payment_method) { create(:bolt_payment_method) }
|
22
|
+
|
23
|
+
describe '#call', vcr: true do
|
24
|
+
it 'makes the API call' do
|
25
|
+
response = api.call
|
26
|
+
|
27
|
+
expect(response['reference']).to eq reference
|
28
|
+
expect(response['status']).to eq 'authorized'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Transactions::RefundService, :vcr, :bolt_configuration do
|
6
|
+
subject(:api) do
|
7
|
+
described_class.new(
|
8
|
+
transaction_reference: reference, amount: amount, currency: currency, payment_method: payment_method
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:transaction) do
|
13
|
+
SolidusBolt::Transactions::AuthorizeService.call(
|
14
|
+
order: order, credit_card: credit_card_payload, create_bolt_account: false, payment_method: payment_method
|
15
|
+
)
|
16
|
+
end
|
17
|
+
let(:credit_card_payload) do
|
18
|
+
tokenize_credit_card(credit_card_number: '4111111111111004', cvv: '111').merge(
|
19
|
+
expiration: (Time.current + 1.year).strftime('%Y-%m'),
|
20
|
+
token_type: 'bolt'
|
21
|
+
)
|
22
|
+
end
|
23
|
+
let(:order) { create(:order_with_line_items) }
|
24
|
+
let(:reference) { transaction['transaction']['reference'] }
|
25
|
+
let(:amount) { transaction['transaction']['amount']['amount'] }
|
26
|
+
let(:currency) { 'USD' }
|
27
|
+
let(:payment_method) { create(:bolt_payment_method) }
|
28
|
+
|
29
|
+
describe '#call', vcr: true do
|
30
|
+
before do
|
31
|
+
SolidusBolt::Transactions::CaptureService.call(
|
32
|
+
transaction_reference: reference, amount: amount, currency: currency, payment_method: payment_method
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'makes the API call' do
|
37
|
+
response = api.call
|
38
|
+
|
39
|
+
expect(response['credit']['status']).to eq 'succeeded'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Transactions::VoidService, :vcr, :bolt_configuration do
|
6
|
+
subject(:api) do
|
7
|
+
described_class.new(
|
8
|
+
transaction_reference: reference, transaction_id: transaction_id, payment_method: payment_method
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:transaction) do
|
13
|
+
SolidusBolt::Transactions::AuthorizeService.call(
|
14
|
+
order: order, credit_card: credit_card_payload, create_bolt_account: false, payment_method: payment_method
|
15
|
+
)
|
16
|
+
end
|
17
|
+
let(:credit_card_payload) do
|
18
|
+
tokenize_credit_card(credit_card_number: '4111111111111004', cvv: '111').merge(
|
19
|
+
expiration: (Time.current + 1.year).strftime('%Y-%m'),
|
20
|
+
token_type: 'bolt'
|
21
|
+
)
|
22
|
+
end
|
23
|
+
let(:order) { create(:order_with_line_items) }
|
24
|
+
let(:reference) { transaction['transaction']['reference'] }
|
25
|
+
let(:transaction_id) { transaction['transaction']['id'] }
|
26
|
+
let(:payment_method) { create(:bolt_payment_method) }
|
27
|
+
|
28
|
+
describe '#call', vcr: true do
|
29
|
+
it 'makes the API call' do
|
30
|
+
response = api.call
|
31
|
+
|
32
|
+
expect(response['id']).to eq transaction_id
|
33
|
+
expect(response['reference']).to eq reference
|
34
|
+
expect(response['status']).to eq 'cancelled'
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when transaction_id is missing' do
|
38
|
+
subject(:api) do
|
39
|
+
described_class.new(
|
40
|
+
transaction_reference: reference, payment_method: payment_method
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'makes the API call' do
|
45
|
+
response = api.call
|
46
|
+
|
47
|
+
expect(response['id']).to eq transaction_id
|
48
|
+
expect(response['reference']).to eq reference
|
49
|
+
expect(response['status']).to eq 'cancelled'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when transaction_reference is missing' do
|
54
|
+
subject(:api) do
|
55
|
+
described_class.new(
|
56
|
+
transaction_id: transaction_id,
|
57
|
+
payment_method: payment_method
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'makes the API call' do
|
62
|
+
response = api.call
|
63
|
+
|
64
|
+
expect(response['id']).to eq transaction_id
|
65
|
+
expect(response['reference']).to eq reference
|
66
|
+
expect(response['status']).to eq 'cancelled'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Users::RefreshAccessTokenService do
|
6
|
+
subject(:refresh_access_token) { described_class.call(session: session) }
|
7
|
+
|
8
|
+
let(:session) { { bolt_access_token: bolt_access_token, bolt_expiration_time: bolt_expiration_time } }
|
9
|
+
|
10
|
+
describe '#call' do
|
11
|
+
context 'when not bolt user' do
|
12
|
+
let(:bolt_access_token) { nil }
|
13
|
+
let(:bolt_expiration_time) { nil }
|
14
|
+
|
15
|
+
it 'returns nil' do
|
16
|
+
expect(refresh_access_token).to be_nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'with valid token' do
|
21
|
+
let(:bolt_access_token) { 'accesstoken' }
|
22
|
+
let(:bolt_expiration_time) { Time.now.utc + 10.minutes }
|
23
|
+
|
24
|
+
it 'returns current access token' do
|
25
|
+
expect(refresh_access_token).to eq(bolt_access_token)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with expired token' do
|
30
|
+
let(:bolt_access_token) { 'accesstoken' }
|
31
|
+
let(:bolt_expiration_time) { Time.now.utc - 10.minutes }
|
32
|
+
let(:result) { { 'access_token' => 'newaccesstoken', 'expires_in' => 3600 } }
|
33
|
+
|
34
|
+
before do
|
35
|
+
allow(HTTParty).to receive(:post).and_return(result)
|
36
|
+
allow(result).to receive(:success?).and_return(true)
|
37
|
+
allow(result).to receive(:parsed_response).and_return(result)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'refreshes the access token' do
|
41
|
+
expect(refresh_access_token).to eq('newaccesstoken')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Users::SyncAddressesService, :vcr, :bolt_configuration do
|
6
|
+
subject(:sync_addresses_service) do
|
7
|
+
described_class.call(user: user, access_token: ENV.fetch('BOLT_ACCESS_TOKEN', 'Fake_access_token'))
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:user) { create(:user) }
|
11
|
+
let(:country) { state.country }
|
12
|
+
let(:state) { create(:state) }
|
13
|
+
let(:payload) do
|
14
|
+
{
|
15
|
+
'profile' => { 'phone' => '987654321' },
|
16
|
+
'addresses' => [{
|
17
|
+
'id' => 'SA98t82gH2Grd',
|
18
|
+
'street_address1' => '380 DEGRAW ST',
|
19
|
+
'locality' => 'Brooklyn',
|
20
|
+
'region' => 'NEW YORK',
|
21
|
+
'region_code' => 'NY',
|
22
|
+
'postal_code' => '11231',
|
23
|
+
'country_code' => 'US',
|
24
|
+
'name' => 'Danny Dove',
|
25
|
+
'first_name' => 'Danny',
|
26
|
+
'last_name' => 'Dove',
|
27
|
+
'default' => true,
|
28
|
+
'phone_number' => phone
|
29
|
+
}]
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
before do
|
34
|
+
allow(Spree::Country).to receive(:find_by).and_return(country)
|
35
|
+
allow(Spree::State).to receive(:find_by).and_return(state)
|
36
|
+
allow(country).to receive(:states).and_return(Spree::State)
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#call', vcr: true do
|
40
|
+
it 'adds the ship_address to the user' do
|
41
|
+
expect { sync_addresses_service }.to change(user, :ship_address).from(nil)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'adds the bill_address to the user' do
|
45
|
+
expect { sync_addresses_service }.to change(user, :bill_address).from(nil)
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with address phone number', vcr: false do
|
49
|
+
let(:phone) { '123456789' }
|
50
|
+
|
51
|
+
before do
|
52
|
+
allow(SolidusBolt::Accounts::DetailService).to receive(:call).and_return(payload)
|
53
|
+
sync_addresses_service
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'creates an address with the address phone' do
|
57
|
+
expect(user.bill_address.phone).to eq(phone)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'without address phone number', vcr: false do
|
62
|
+
let(:phone) { nil }
|
63
|
+
|
64
|
+
before do
|
65
|
+
allow(SolidusBolt::Accounts::DetailService).to receive(:call).and_return(payload)
|
66
|
+
sync_addresses_service
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'creates an address with the user phone' do
|
70
|
+
expect(user.bill_address.phone).to eq('987654321')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
RSpec.describe SolidusBolt::Users::SyncPaymentSourcesService, :vcr, :bolt_configuration do
|
6
|
+
subject(:sync_payment_sources) do
|
7
|
+
described_class.call(user: user, access_token: ENV.fetch('BOLT_ACCESS_TOKEN', 'Fake_access_token'))
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:user) { create(:user) }
|
11
|
+
let(:bolt_payment_method) { create(:bolt_payment_method) }
|
12
|
+
|
13
|
+
describe '#call' do
|
14
|
+
before { bolt_payment_method }
|
15
|
+
|
16
|
+
it 'creates a new payment source with card ID' do
|
17
|
+
expect { sync_payment_sources }.to change { SolidusBolt::PaymentSource.count }.by(1)
|
18
|
+
bolt_payment_source = SolidusBolt::PaymentSource.last
|
19
|
+
expect(bolt_payment_source.card_id).to be_present
|
20
|
+
expect(bolt_payment_source.card_expiration).to be_present
|
21
|
+
expect(bolt_payment_source.payment_method_id).to eq(bolt_payment_method.id)
|
22
|
+
expect(user.wallet_payment_sources.last.payment_source).to eq(bolt_payment_source)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|