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,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spree
|
4
|
+
module Admin
|
5
|
+
class BoltsController < Spree::Admin::BaseController
|
6
|
+
before_action :bolt_configuration
|
7
|
+
|
8
|
+
def show; end
|
9
|
+
|
10
|
+
def edit; end
|
11
|
+
|
12
|
+
def update
|
13
|
+
if @bolt_configuration.update(bolt_configuration_params)
|
14
|
+
flash[:success] = t('spree.admin.bolt.updated_successfully')
|
15
|
+
redirect_to admin_bolt_path
|
16
|
+
else
|
17
|
+
flash[:error] = @bolt_configuration.errors.full_messages.to_sentence
|
18
|
+
render :edit
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def bolt_configuration
|
25
|
+
@bolt_configuration = SolidusBolt::BoltConfiguration.fetch
|
26
|
+
end
|
27
|
+
|
28
|
+
def bolt_configuration_params
|
29
|
+
params
|
30
|
+
.require(:solidus_bolt_bolt_configuration)
|
31
|
+
.permit(
|
32
|
+
:bearer_token,
|
33
|
+
:environment,
|
34
|
+
:merchant_public_id,
|
35
|
+
:division_public_id,
|
36
|
+
:api_key,
|
37
|
+
:signing_secret,
|
38
|
+
:publishable_key
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/app/decorators/controllers/solidus_bolt/spree_checkout_controller/add_addresses_to_bolt.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
module SpreeCheckoutController
|
5
|
+
module AddAddressesToBolt
|
6
|
+
def finalize_order
|
7
|
+
if session[:bolt_access_token] && current_order.payments.last&.source_type == "SolidusBolt::PaymentSource"
|
8
|
+
spree_current_user.addresses.each do |address|
|
9
|
+
SolidusBolt::AddAddressJob.perform_later(
|
10
|
+
order: current_order,
|
11
|
+
access_token: SolidusBolt::Users::RefreshAccessTokenService.call(session: session),
|
12
|
+
address: address
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
Spree::CheckoutController.prepend self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/app/decorators/controllers/solidus_bolt/spree_checkout_controller/refresh_bolt_addresses.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
module SpreeCheckoutController
|
5
|
+
module RefreshBoltAddresses
|
6
|
+
def before_address
|
7
|
+
SolidusBolt::Users::SyncAddressesService.call(
|
8
|
+
user: spree_current_user, access_token: SolidusBolt::Users::RefreshAccessTokenService.call(session: session)
|
9
|
+
)
|
10
|
+
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
Spree::CheckoutController.prepend self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
module SpreeCheckoutController
|
5
|
+
module RefreshBoltPaymentSource
|
6
|
+
def before_payment
|
7
|
+
SolidusBolt::Users::SyncPaymentSourcesService.call(
|
8
|
+
user: spree_current_user, access_token: SolidusBolt::Users::RefreshAccessTokenService.call(session: session)
|
9
|
+
)
|
10
|
+
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
Spree::CheckoutController.prepend self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
module AddressDecorator
|
5
|
+
def bolt_address(email)
|
6
|
+
{
|
7
|
+
street_address1: address1,
|
8
|
+
street_address2: address2,
|
9
|
+
locality: city,
|
10
|
+
region: state.abbr,
|
11
|
+
postal_code: zipcode,
|
12
|
+
country_code: country.iso,
|
13
|
+
first_name: Spree::Address::Name.new(name).first_name,
|
14
|
+
last_name: Spree::Address::Name.new(name).last_name,
|
15
|
+
phone: phone,
|
16
|
+
email: email
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
Spree::Address.prepend(self)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
module OrderDecorator
|
5
|
+
def bolt_cart
|
6
|
+
{
|
7
|
+
total_amount: display_total.cents,
|
8
|
+
order_reference: number,
|
9
|
+
currency: currency,
|
10
|
+
items: line_items.map do |line_item|
|
11
|
+
{
|
12
|
+
sku: line_item.sku,
|
13
|
+
name: line_item.name,
|
14
|
+
unit_price: cents(line_item.price),
|
15
|
+
quantity: line_item.quantity
|
16
|
+
}
|
17
|
+
end
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def bolt_user_identifier
|
22
|
+
{
|
23
|
+
email: email,
|
24
|
+
phone: bill_address.phone
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def bolt_user_identity
|
29
|
+
name = bill_address.name.split(' ')
|
30
|
+
{
|
31
|
+
first_name: name.first,
|
32
|
+
last_name: name.last
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def cents(float)
|
39
|
+
(float * 100).to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
Spree::Order.prepend(self)
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
module OmniAuth::Strategies::BoltDecorator # rubocop:disable Style/ClassAndModuleChildren
|
5
|
+
def callback_phase
|
6
|
+
super.tap do
|
7
|
+
session[:bolt_access_token] = @access_token
|
8
|
+
session[:bolt_expiration_time] = @expiration_time
|
9
|
+
session[:bolt_refresh_token] = @refresh_token
|
10
|
+
session[:bolt_refresh_token_scope] = @refresh_token_scope
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
::OmniAuth::Strategies::Bolt.prepend self
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
class AddAddressJob < ApplicationJob
|
5
|
+
def perform(order:, access_token:, address:)
|
6
|
+
SolidusBolt::Accounts::AddAddressService.call(
|
7
|
+
order: order, access_token: access_token, address: address
|
8
|
+
)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency 'solidus_bolt'
|
4
|
+
|
5
|
+
module SolidusBolt
|
6
|
+
class BoltConfiguration < ApplicationRecord
|
7
|
+
after_commit :update_bolt_config_credentials
|
8
|
+
|
9
|
+
REGISTER_URL = 'https://merchant.bolt.com/register'
|
10
|
+
EVENTS = %w[all payment credit capture void auth pending rejected_irreversible rejected_reversible no_op
|
11
|
+
failed_payment newsletter_subscription risk_insights credit_card_deleted].freeze
|
12
|
+
|
13
|
+
enum environment: { production: 0, sandbox: 1, staging: 2 }
|
14
|
+
|
15
|
+
validate :config_can_be_created, on: :create
|
16
|
+
|
17
|
+
def self.fetch
|
18
|
+
first_or_create
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.can_create?
|
22
|
+
count.zero?
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.config_empty?
|
26
|
+
whitelist = %w[id created_at updated_at environment]
|
27
|
+
|
28
|
+
fetch.attributes.all? do |attr, val|
|
29
|
+
whitelist.include?(attr) || val.blank?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def environment_url
|
34
|
+
case BoltConfiguration.environments[environment]
|
35
|
+
when 0 then 'https://api.bolt.com'
|
36
|
+
when 1 then 'https://api-sandbox.bolt.com'
|
37
|
+
when 2 then 'https://api-staging.bolt.com'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def embed_js
|
42
|
+
case BoltConfiguration.environments[environment]
|
43
|
+
when 0 then 'https://connect.bolt.com/embed.js'
|
44
|
+
else 'https://connect-sandbox.bolt.com/embed.js'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def account_js
|
49
|
+
case BoltConfiguration.environments[environment]
|
50
|
+
when 0 then 'https://connect.bolt.com/account.js'
|
51
|
+
else
|
52
|
+
'https://connect-sandbox.bolt.com/account.js'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def update_bolt_config_credentials
|
59
|
+
bolt_config_credentials = Spree::Config
|
60
|
+
.static_model_preferences
|
61
|
+
.for_class(SolidusBolt::PaymentMethod)['bolt_config_credentials']
|
62
|
+
&.preferences
|
63
|
+
return unless bolt_config_credentials
|
64
|
+
|
65
|
+
bolt_config_credentials[:bolt_api_key] = api_key
|
66
|
+
bolt_config_credentials[:bolt_signing_secret] = signing_secret
|
67
|
+
bolt_config_credentials[:bolt_publishable_key] = publishable_key
|
68
|
+
end
|
69
|
+
|
70
|
+
def config_can_be_created
|
71
|
+
errors.add(:base, 'Can create only one record for this Model') unless SolidusBolt::BoltConfiguration.can_create?
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
class Gateway
|
5
|
+
def initialize(options = {}); end
|
6
|
+
|
7
|
+
def authorize(_amount, payment_source, gateway_options)
|
8
|
+
order_number = gateway_options[:order_id].split('-').first
|
9
|
+
order = fetch_spree_order(order_number)
|
10
|
+
|
11
|
+
authorization_response = ::SolidusBolt::Transactions::AuthorizeService.call(
|
12
|
+
order: order,
|
13
|
+
create_bolt_account: payment_source.create_bolt_account,
|
14
|
+
credit_card: credit_card_params(payment_source),
|
15
|
+
payment_method: payment_source.payment_method,
|
16
|
+
repeat: payment_source.card_id.present?
|
17
|
+
)
|
18
|
+
|
19
|
+
unless payment_source.card_id
|
20
|
+
payment_source.update(card_id: authorization_response['transaction']['from_credit_card']['id'])
|
21
|
+
end
|
22
|
+
|
23
|
+
ActiveMerchant::Billing::Response.new(true, 'Transaction approved', payment_source.attributes,
|
24
|
+
authorization: authorization_response['transaction']['reference'])
|
25
|
+
rescue ServerError => e
|
26
|
+
ActiveMerchant::Billing::Response.new(false, e, {})
|
27
|
+
end
|
28
|
+
|
29
|
+
def capture(float_amount, response_code, gateway_options)
|
30
|
+
payment = gateway_options[:originator]
|
31
|
+
payment_source = payment.source
|
32
|
+
payment_method = payment.payment_method
|
33
|
+
currency = gateway_options[:currency]
|
34
|
+
|
35
|
+
capture_response = SolidusBolt::Transactions::CaptureService.call(
|
36
|
+
transaction_reference: response_code,
|
37
|
+
amount: float_amount,
|
38
|
+
currency: currency,
|
39
|
+
payment_method: payment_method
|
40
|
+
)
|
41
|
+
|
42
|
+
ActiveMerchant::Billing::Response.new(
|
43
|
+
true,
|
44
|
+
'Transaction Captured', payment_source.attributes,
|
45
|
+
authorization: capture_response['reference']
|
46
|
+
)
|
47
|
+
rescue ServerError => e
|
48
|
+
ActiveMerchant::Billing::Response.new(false, e, {})
|
49
|
+
end
|
50
|
+
|
51
|
+
def void(response_code, gateway_options)
|
52
|
+
payment = gateway_options[:originator]
|
53
|
+
payment_source = payment.source
|
54
|
+
payment_method = payment.payment_method
|
55
|
+
|
56
|
+
void_response = SolidusBolt::Transactions::VoidService.call(
|
57
|
+
transaction_reference: response_code,
|
58
|
+
payment_method: payment_method
|
59
|
+
)
|
60
|
+
|
61
|
+
ActiveMerchant::Billing::Response.new(
|
62
|
+
true,
|
63
|
+
'Transaction Void', payment_source.attributes,
|
64
|
+
authorization: void_response['reference']
|
65
|
+
)
|
66
|
+
rescue ServerError => e
|
67
|
+
ActiveMerchant::Billing::Response.new(false, e, {})
|
68
|
+
end
|
69
|
+
|
70
|
+
def credit(amount, response_code, _gateway_options)
|
71
|
+
payment = Spree::Payment.find_by(response_code: response_code)
|
72
|
+
payment_source = payment.source
|
73
|
+
payment_method = payment.payment_method
|
74
|
+
currency = payment.currency
|
75
|
+
|
76
|
+
credit_response = SolidusBolt::Transactions::RefundService.call(
|
77
|
+
transaction_reference: response_code,
|
78
|
+
amount: amount,
|
79
|
+
currency: currency,
|
80
|
+
payment_method: payment_method
|
81
|
+
)
|
82
|
+
|
83
|
+
ActiveMerchant::Billing::Response.new(
|
84
|
+
true,
|
85
|
+
'Transaction Refunded', payment_source.attributes,
|
86
|
+
authorization: credit_response['reference']
|
87
|
+
)
|
88
|
+
rescue ServerError => e
|
89
|
+
ActiveMerchant::Billing::Response.new(false, e, {})
|
90
|
+
end
|
91
|
+
|
92
|
+
def purchase(_float_amount, payment_source, gateway_options)
|
93
|
+
order_number = gateway_options[:order_id].split('-').first
|
94
|
+
order = fetch_spree_order(order_number)
|
95
|
+
|
96
|
+
authorization_response = ::SolidusBolt::Transactions::AuthorizeService.call(
|
97
|
+
order: order,
|
98
|
+
create_bolt_account: payment_source.create_bolt_account,
|
99
|
+
credit_card: credit_card_params(payment_source),
|
100
|
+
payment_method: payment_source.payment_method,
|
101
|
+
auto_capture: true,
|
102
|
+
repeat: payment_source.card_id.present?
|
103
|
+
)
|
104
|
+
|
105
|
+
unless payment_source.card_id
|
106
|
+
payment_source.update(card_id: authorization_response['transaction']['from_credit_card']['id'])
|
107
|
+
end
|
108
|
+
|
109
|
+
ActiveMerchant::Billing::Response.new(true, 'Transaction approved and captured', payment_source.attributes,
|
110
|
+
authorization: authorization_response['transaction']['reference'])
|
111
|
+
rescue ServerError => e
|
112
|
+
ActiveMerchant::Billing::Response.new(false, e, {})
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def fetch_spree_order(order_number)
|
118
|
+
Spree::Order.find_by(number: order_number)
|
119
|
+
end
|
120
|
+
|
121
|
+
def credit_card_params(payment_source)
|
122
|
+
card_params = { token_type: 'bolt' }
|
123
|
+
|
124
|
+
payment_source.attributes.each do |k, v|
|
125
|
+
next unless k.include?('card_')
|
126
|
+
|
127
|
+
card_params[k.gsub('card_', '').to_sym] = v unless v.nil?
|
128
|
+
end
|
129
|
+
|
130
|
+
card_params
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
class PaymentMethod < SolidusSupport.payment_method_parent_class
|
5
|
+
preference :bolt_api_key, :string
|
6
|
+
preference :bolt_signing_secret, :string
|
7
|
+
preference :bolt_publishable_key, :string
|
8
|
+
|
9
|
+
def gateway_class
|
10
|
+
::SolidusBolt::Gateway
|
11
|
+
end
|
12
|
+
|
13
|
+
def payment_source_class
|
14
|
+
::SolidusBolt::PaymentSource
|
15
|
+
end
|
16
|
+
|
17
|
+
def partial_name
|
18
|
+
'bolt'
|
19
|
+
end
|
20
|
+
|
21
|
+
def preferred_bolt_base_url
|
22
|
+
bolt_config.embed_js
|
23
|
+
end
|
24
|
+
|
25
|
+
def preferred_bolt_api_url
|
26
|
+
bolt_config.environment_url
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def bolt_config
|
32
|
+
@bolt_config = SolidusBolt::BoltConfiguration.fetch
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_dependency 'solidus_bolt'
|
4
|
+
|
5
|
+
module SolidusBolt
|
6
|
+
class PaymentSource < SolidusSupport.payment_source_parent_class
|
7
|
+
validates :payment_method_id, presence: true
|
8
|
+
|
9
|
+
def reusable?
|
10
|
+
card_id.present?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<!-- insert_after 'meta[name="viewport"]' -->
|
2
|
+
|
3
|
+
<meta name="bolt-publishable-key" content="<%= SolidusBolt::BoltConfiguration.fetch.publishable_key %>" />
|
4
|
+
<meta name="bolt-domain" content="<%= SolidusBolt::BoltConfiguration.fetch.environment_url%>" />
|
5
|
+
<script id="bolt-embedded" type="text/javascript" src="<%= SolidusBolt::BoltConfiguration.fetch.embed_js %>"></script>
|
6
|
+
<script id="bolt-account" type="text/javascript" async="" src="<%= SolidusBolt::BoltConfiguration.fetch.account_js %>" data-publishable-key="<%= SolidusBolt::BoltConfiguration.fetch.publishable_key %>"></script>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
module Accounts
|
5
|
+
class AddAddressService < SolidusBolt::BaseService
|
6
|
+
attr_reader :order, :address, :access_token
|
7
|
+
|
8
|
+
def initialize(order:, address:, access_token:)
|
9
|
+
@order = order
|
10
|
+
@address = address
|
11
|
+
@access_token = access_token
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
return if address_already_uploaded?
|
17
|
+
|
18
|
+
add_address
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def bolt_addresses
|
24
|
+
@bolt_addresses ||= SolidusBolt::Accounts::DetailService.call(access_token: access_token)['addresses']
|
25
|
+
end
|
26
|
+
|
27
|
+
def address_already_uploaded?
|
28
|
+
bolt_addresses.any? do |bolt_address|
|
29
|
+
bolt_address['street_address1'] == address.address1 &&
|
30
|
+
bolt_address['locality'] == address.city &&
|
31
|
+
bolt_address['region'] == address.state&.abbr &&
|
32
|
+
bolt_address['postal_code'] == address.zipcode
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_address
|
37
|
+
handle_result(HTTParty.post("#{api_base_url}/#{api_version}/account/addresses", build_options))
|
38
|
+
end
|
39
|
+
|
40
|
+
def address_body
|
41
|
+
address.bolt_address(order.email).to_json
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_options
|
45
|
+
{
|
46
|
+
body: address_body,
|
47
|
+
headers: {
|
48
|
+
'Authorization' => "Bearer #{access_token}",
|
49
|
+
'Content-Type' => 'application/json'
|
50
|
+
}.merge(authentication_header)
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
module Accounts
|
5
|
+
class AddPaymentMethodService < SolidusBolt::BaseService
|
6
|
+
attr_reader :access_token, :credit_card, :address, :email
|
7
|
+
|
8
|
+
def initialize(access_token:, credit_card:, address:, email:)
|
9
|
+
@access_token = access_token
|
10
|
+
@credit_card = credit_card
|
11
|
+
@address = address
|
12
|
+
@email = email
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
add_payment_method
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def add_payment_method
|
23
|
+
handle_result(HTTParty.post("#{api_base_url}/#{api_version}/account/payment_methods", build_options))
|
24
|
+
end
|
25
|
+
|
26
|
+
def body
|
27
|
+
credit_card.merge(billing_address: address_body).to_json
|
28
|
+
end
|
29
|
+
|
30
|
+
def address_body
|
31
|
+
address.bolt_address(email)
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_options
|
35
|
+
{
|
36
|
+
headers: {
|
37
|
+
'Authorization' => "Bearer #{access_token}",
|
38
|
+
'Content-Type' => 'application/json'
|
39
|
+
}.merge(authentication_header),
|
40
|
+
body: body
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
module Accounts
|
5
|
+
class DetailService < SolidusBolt::BaseService
|
6
|
+
attr_reader :access_token
|
7
|
+
|
8
|
+
def initialize(access_token:)
|
9
|
+
@access_token = access_token
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
detail
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def detail
|
20
|
+
options = build_options
|
21
|
+
handle_result(
|
22
|
+
HTTParty.get(
|
23
|
+
"#{api_base_url}/#{api_version}/account",
|
24
|
+
options
|
25
|
+
)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_options
|
30
|
+
{
|
31
|
+
headers: {
|
32
|
+
'Authorization' => "Bearer #{access_token}",
|
33
|
+
}.merge(authentication_header)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
module Accounts
|
5
|
+
class DetectAccountService < SolidusBolt::BaseService
|
6
|
+
attr_reader :email
|
7
|
+
|
8
|
+
def initialize(email:)
|
9
|
+
@email = email
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
detect_account
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def detect_account
|
20
|
+
options = build_options
|
21
|
+
handle_result(
|
22
|
+
HTTParty.get(
|
23
|
+
"#{api_base_url}/#{api_version}/account/exists",
|
24
|
+
options
|
25
|
+
)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_options
|
30
|
+
{ query: { email: email } }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|