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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 94b2a582685d844a4e3c0b9f5db529400eebffc1c546a0783f68629fa74ca74b
|
4
|
+
data.tar.gz: 5e9b74ce81c07973564709d65920c17ec2fc44d92076c9914eb4da440fee3251
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4ba2a650993c03f57b304f8f0b3bef38654bdba35ae24d9f155404d5994f1328f8736ce387db46ef16a27454740d9205039256c6ea3535e270c964cedb998106
|
7
|
+
data.tar.gz: abb020bf31418d56d460de9dc07a3e83ce54473ddbc6750e011d51a47abc37a0ffdc62c2f5b73f4417fa86a4823fec9404ea4db0be7d0f01a35333c10ca90c40
|
@@ -0,0 +1,41 @@
|
|
1
|
+
version: 2.1
|
2
|
+
|
3
|
+
orbs:
|
4
|
+
# Always take the latest version of the orb, this allows us to
|
5
|
+
# run specs against Solidus supported versions only without the need
|
6
|
+
# to change this configuration every time a Solidus version is released
|
7
|
+
# or goes EOL.
|
8
|
+
solidusio_extensions: solidusio/extensions@0.2.27
|
9
|
+
|
10
|
+
jobs:
|
11
|
+
run-specs-with-postgres:
|
12
|
+
executor: solidusio_extensions/postgres
|
13
|
+
steps:
|
14
|
+
- solidusio_extensions/run-tests
|
15
|
+
run-specs-with-mysql:
|
16
|
+
executor: solidusio_extensions/mysql
|
17
|
+
steps:
|
18
|
+
- solidusio_extensions/run-tests
|
19
|
+
lint-code:
|
20
|
+
executor: solidusio_extensions/sqlite-memory
|
21
|
+
steps:
|
22
|
+
- solidusio_extensions/lint-code
|
23
|
+
|
24
|
+
workflows:
|
25
|
+
"Run specs on supported Solidus versions":
|
26
|
+
jobs:
|
27
|
+
- run-specs-with-postgres
|
28
|
+
- run-specs-with-mysql
|
29
|
+
- lint-code
|
30
|
+
|
31
|
+
"Weekly run specs against master":
|
32
|
+
triggers:
|
33
|
+
- schedule:
|
34
|
+
cron: "0 0 * * 4" # every Thursday
|
35
|
+
filters:
|
36
|
+
branches:
|
37
|
+
only:
|
38
|
+
- master
|
39
|
+
jobs:
|
40
|
+
- run-specs-with-postgres
|
41
|
+
- run-specs-with-mysql
|
data/.gem_release.yml
ADDED
data/.github/stale.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Number of days of inactivity before an issue becomes stale
|
2
|
+
daysUntilStale: 60
|
3
|
+
# Number of days of inactivity before a stale issue is closed
|
4
|
+
daysUntilClose: false
|
5
|
+
# Issues with these labels will never be considered stale
|
6
|
+
exemptLabels:
|
7
|
+
- pinned
|
8
|
+
- security
|
9
|
+
# Label to use when marking an issue as stale
|
10
|
+
staleLabel: stale
|
11
|
+
# Comment to post when marking an issue as stale. Set to `false` to disable
|
12
|
+
markComment: >
|
13
|
+
This issue has been automatically marked as stale because it has not had
|
14
|
+
recent activity. It might be closed if no further activity occurs. Thank you
|
15
|
+
for your contributions.
|
16
|
+
# Comment to post when closing a stale issue. Set to `false` to disable
|
17
|
+
closeComment: false
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Changelog
|
data/Gemfile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
5
|
+
|
6
|
+
branch = ENV.fetch('SOLIDUS_BRANCH', 'master')
|
7
|
+
gem 'solidus', github: 'solidusio/solidus', branch: branch
|
8
|
+
|
9
|
+
# Needed to help Bundler figure out how to resolve dependencies,
|
10
|
+
# otherwise it takes forever to resolve them.
|
11
|
+
# See https://github.com/bundler/bundler/issues/6677
|
12
|
+
gem 'rails', '>0.a'
|
13
|
+
|
14
|
+
# Provides basic authentication functionality for testing parts of your engine
|
15
|
+
gem 'solidus_auth_devise'
|
16
|
+
|
17
|
+
case ENV['DB']
|
18
|
+
when 'mysql'
|
19
|
+
gem 'mysql2'
|
20
|
+
when 'postgresql'
|
21
|
+
gem 'pg'
|
22
|
+
else
|
23
|
+
gem 'sqlite3'
|
24
|
+
end
|
25
|
+
|
26
|
+
gemspec
|
27
|
+
|
28
|
+
# Use a local Gemfile to include development dependencies that might not be
|
29
|
+
# relevant for the project or for other contributors, e.g. pry-byebug.
|
30
|
+
#
|
31
|
+
# We use `send` instead of calling `eval_gemfile` to work around an issue with
|
32
|
+
# how Dependabot parses projects: https://github.com/dependabot/dependabot-core/issues/1658.
|
33
|
+
send(:eval_gemfile, 'Gemfile-local') if File.exist? 'Gemfile-local'
|
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright (c) 2022 [name of plugin creator]
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice,
|
8
|
+
this list of conditions and the following disclaimer.
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
* Neither the name Solidus nor the names of its contributors may be used to
|
13
|
+
endorse or promote products derived from this software without specific
|
14
|
+
prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
17
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
18
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
19
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
20
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
21
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
22
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
23
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
24
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
25
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
26
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
# Solidus Bolt
|
2
|
+
|
3
|
+
[![CircleCI](https://circleci.com/gh/solidusio-contrib/solidus_bolt.svg?style=shield)](https://circleci.com/gh/solidusio-contrib/solidus_bolt)
|
4
|
+
[![codecov](https://codecov.io/gh/solidusio-contrib/solidus_bolt/branch/master/graph/badge.svg)](https://codecov.io/gh/solidusio-contrib/solidus_bolt)
|
5
|
+
|
6
|
+
<!-- Explain what your extension does. -->
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add solidus_bolt to your Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'solidus_bolt'
|
14
|
+
```
|
15
|
+
|
16
|
+
Bundle your dependencies and run the installation generator (before running the following command, we recommend setting up the environment variables and seeds as described in the sections below):
|
17
|
+
|
18
|
+
```shell
|
19
|
+
bin/rails generate solidus_bolt:install
|
20
|
+
```
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
### Setting up Bolt Configuration
|
25
|
+
|
26
|
+
Many of the API calls handled by this gem use the variables set in Bolt Configuration. Since this extension's seeds automatically generate a Bolt Configuration, the easiest and safest way to configure it would be by setting the following environment variables:
|
27
|
+
|
28
|
+
```
|
29
|
+
BOLT_BEARER_TOKEN
|
30
|
+
BOLT_ENVIRONMENT
|
31
|
+
BOLT_MERCHANT_PUBLIC_ID
|
32
|
+
BOLT_DIVISION_PUBLIC_ID
|
33
|
+
BOLT_API_KEY
|
34
|
+
BOLT_SIGNING_SECRET
|
35
|
+
BOLT_PUBLISHABLE_KEY
|
36
|
+
```
|
37
|
+
|
38
|
+
Alternatively you can setup the Bolt Configuration manually by visiting `/admin/bolt`
|
39
|
+
|
40
|
+
### Using solidus_bolt Seeds
|
41
|
+
|
42
|
+
Provided you setup the environment variables, you can simplify the setup of a Bolt application by running the [gem's seeds](https://github.com/nebulab/solidus_bolt/blob/master/db/seeds.rb). This will automatically create the following:
|
43
|
+
|
44
|
+
- BoltConfiguration
|
45
|
+
- AuthenticationMethod
|
46
|
+
- PaymentMethod
|
47
|
+
|
48
|
+
You can run solidus_bolt's seeds either by running
|
49
|
+
```shell
|
50
|
+
bin/rails db:seed:solidus_bolt
|
51
|
+
```
|
52
|
+
or by adding the following line to your seed file:
|
53
|
+
```ruby
|
54
|
+
SolidusBolt::Engine.load_seed if defined?(SolidusBolt)
|
55
|
+
```
|
56
|
+
|
57
|
+
### Creating a new Payment Method
|
58
|
+
|
59
|
+
Assuming you've used environment variables to configure your Bolt Configuration, creating a Bolt payment method is very easy:
|
60
|
+
|
61
|
+
1. Visit `/admin/payment_methods/new`
|
62
|
+
2. Set `provider` to SolidusBolt::PaymentMethod
|
63
|
+
3. Click "Save"
|
64
|
+
4. Choose `bolt_credentials` from the `Preference Source` select
|
65
|
+
5. Click `Update` to save
|
66
|
+
|
67
|
+
If you've instead decided to setup the Bolt Configuration manually, follow the same process mentioned above but at step 4 pick `bolt_config_credentials` instead of `bolt_credentials`.
|
68
|
+
|
69
|
+
In both cases you can alternatively create a payment method from the Rails console with:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
SolidusBolt::PaymentMethod.create(
|
73
|
+
name: "Bolt",
|
74
|
+
preference_source: "bolt_credentials" # or "bolt_config_credentials"
|
75
|
+
)
|
76
|
+
```
|
77
|
+
|
78
|
+
The final (not recommended) option is to not select any `Preference Source` at step 4 and instead fill up the inputs manually.
|
79
|
+
|
80
|
+
### How to set the webhooks
|
81
|
+
|
82
|
+
(For latest up to date guide check [Bolt's Documentation](https://help.bolt.com/developers/guides/webhooks/))
|
83
|
+
|
84
|
+
1. Login to your [Bolt Merchant Dashboard](https://merchant.bolt.com/).
|
85
|
+
|
86
|
+
2. Navigate to **Developers**.
|
87
|
+
|
88
|
+
3. Scroll to **Merchant API**.
|
89
|
+
|
90
|
+
4. Add your webhook endpoints (by default it's your store's url plus `/webhooks/bolt`)
|
91
|
+
|
92
|
+
Important use cases include:
|
93
|
+
|
94
|
+
- Notifying your e-commerce store when a transaction has been approved or rejected by Bolt.
|
95
|
+
- Sending your e-commerce store with the `transaction_id`, which is necessary for back-office operations.
|
96
|
+
- Sending your e-commerce store more information about a transaction such as credit card details.
|
97
|
+
|
98
|
+
## Development
|
99
|
+
|
100
|
+
### Testing the extension
|
101
|
+
|
102
|
+
First bundle your dependencies, then run `bin/rake`. `bin/rake` will default to building the dummy
|
103
|
+
app if it does not exist, then it will run specs. The dummy app can be regenerated by using
|
104
|
+
`bin/rake extension:test_app`.
|
105
|
+
|
106
|
+
```shell
|
107
|
+
bin/rake
|
108
|
+
```
|
109
|
+
|
110
|
+
To run [Rubocop](https://github.com/bbatsov/rubocop) static code analysis run
|
111
|
+
|
112
|
+
```shell
|
113
|
+
bundle exec rubocop
|
114
|
+
```
|
115
|
+
|
116
|
+
When testing your application's integration with this extension you may use its factories.
|
117
|
+
Simply add this require statement to your `spec/spec_helper.rb`:
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
require 'solidus_bolt/testing_support/factories'
|
121
|
+
```
|
122
|
+
|
123
|
+
Or, if you are using `FactoryBot.definition_file_paths`, you can load Solidus core
|
124
|
+
factories along with this extension's factories using this statement:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
SolidusDevSupport::TestingSupport::Factories.load_for(SolidusBolt::Engine)
|
128
|
+
```
|
129
|
+
|
130
|
+
#### Special Tests
|
131
|
+
|
132
|
+
A few tests in the test suite require some additional steps to execute successfully when they are modified. These are listed below along with the steps needed to execute these tests.
|
133
|
+
|
134
|
+
- `/spec/services/solidus_bolt/accounts/detail_service_spec.rb`
|
135
|
+
This test requires a valid `bolt_access_token` to execute successfully when modified.
|
136
|
+
Follow the steps below to get a `bolt_access_token`.
|
137
|
+
1. Login as a User using a Bolt Account.
|
138
|
+
2. Put a `binding.pry` on any view.
|
139
|
+
3. Print `session['bolt_access_token']` in the pry console.
|
140
|
+
4. Copy the result and set the value of the environment variable `BOLT_ACCESS_TOKEN` to this result.
|
141
|
+
|
142
|
+
### Running the sandbox
|
143
|
+
|
144
|
+
To run this extension in a sandboxed Solidus application, you can run `bin/sandbox`. The path for
|
145
|
+
the sandbox app is `./sandbox` and `bin/rails` will forward any Rails commands to
|
146
|
+
`sandbox/bin/rails`.
|
147
|
+
|
148
|
+
Here's an example:
|
149
|
+
|
150
|
+
```
|
151
|
+
$ bin/rails server
|
152
|
+
=> Booting Puma
|
153
|
+
=> Rails 6.0.2.1 application starting in development
|
154
|
+
* Listening on tcp://127.0.0.1:3000
|
155
|
+
Use Ctrl-C to stop
|
156
|
+
```
|
157
|
+
|
158
|
+
### Updating the changelog
|
159
|
+
|
160
|
+
Before and after releases the changelog should be updated to reflect the up-to-date status of
|
161
|
+
the project:
|
162
|
+
|
163
|
+
```shell
|
164
|
+
bin/rake changelog
|
165
|
+
git add CHANGELOG.md
|
166
|
+
git commit -m "Update the changelog"
|
167
|
+
```
|
168
|
+
|
169
|
+
### Releasing new versions
|
170
|
+
|
171
|
+
Please refer to the dedicated [page](https://github.com/solidusio/solidus/wiki/How-to-release-extensions) on Solidus wiki.
|
172
|
+
|
173
|
+
## License
|
174
|
+
|
175
|
+
Copyright (c) 2022 [name of extension author], released under the New BSD License.
|
data/Rakefile
ADDED
Binary file
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class AuthorizeAccount {
|
2
|
+
constructor(emailField, pubKey, domain) {
|
3
|
+
this.emailField = emailField;
|
4
|
+
this.pubKey = pubKey;
|
5
|
+
this.domain = domain;
|
6
|
+
}
|
7
|
+
|
8
|
+
async accessRequest() {
|
9
|
+
const boltEmbedded = Bolt(this.pubKey);
|
10
|
+
|
11
|
+
if (this.emailField) {
|
12
|
+
const email = this.emailField.value
|
13
|
+
const encodedEmail = encodeURIComponent(email);
|
14
|
+
// Call the endpoint to check if the email already exist on bolt
|
15
|
+
const response = await fetch(this.domain + "/v1/account/exists?email=" + encodedEmail);
|
16
|
+
const responseAsJson = await response.json();
|
17
|
+
this.removeBoltLoginButton();
|
18
|
+
|
19
|
+
if(responseAsJson.has_bolt_account) {
|
20
|
+
// SHOW THE SIGN IN WITH BOLT BUTTON
|
21
|
+
const boltButton = this.createBoltLoginButton();
|
22
|
+
boltButton.addEventListener('click', async (e) => {
|
23
|
+
e.preventDefault();
|
24
|
+
|
25
|
+
this.emailField.parentNode.setAttribute("class", "email-div")
|
26
|
+
let authorizationComponent = boltEmbedded.create("authorization_component", {style: "callout"});
|
27
|
+
await authorizationComponent.mount(".email-div");
|
28
|
+
// start OTP modal
|
29
|
+
let authorizationResponse = await authorizationComponent.authorize({"email": email});
|
30
|
+
if (authorizationResponse) {
|
31
|
+
|
32
|
+
const authorizationCode = authorizationResponse.authorizationCode;
|
33
|
+
const scope = authorizationResponse.scope ;
|
34
|
+
|
35
|
+
document.location.href = `/users/auth/bolt/callback?authorization_code=${authorizationCode}&scope=${scope}`
|
36
|
+
}
|
37
|
+
});
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
removeBoltLoginButton() {
|
43
|
+
const parentNode = this.emailField.parentNode;
|
44
|
+
const eleList = parentNode.getElementsByClassName('bolt-ele');
|
45
|
+
|
46
|
+
while(eleList[0]) {
|
47
|
+
parentNode.removeChild(eleList[0]);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
createBoltLoginButton() {
|
52
|
+
const parentNode = this.emailField.parentNode;
|
53
|
+
const p = document.createElement('p');
|
54
|
+
const button = document.createElement('button');
|
55
|
+
|
56
|
+
p.innerHTML = 'Hey, we detected that you have a Bolt acocunt. Do you want to login with your Bolt account ?';
|
57
|
+
p.className = 'bolt-ele';
|
58
|
+
button.innerHTML = 'Login with Bolt';
|
59
|
+
button.className = 'btn btn-primary bolt-ele';
|
60
|
+
|
61
|
+
parentNode.appendChild(p);
|
62
|
+
parentNode.appendChild(button);
|
63
|
+
|
64
|
+
return button;
|
65
|
+
}
|
66
|
+
|
67
|
+
async hasBoltAccount(email) {
|
68
|
+
const encodedEmail = encodeURIComponent(email);
|
69
|
+
// Call the endpoint to check if the email already exist on bolt
|
70
|
+
const response = await fetch(this.domain + "/v1/account/exists?email=" + encodedEmail);
|
71
|
+
const responseAsJson = await response.json();
|
72
|
+
return responseAsJson.has_bolt_account
|
73
|
+
}
|
74
|
+
}
|
@@ -0,0 +1,91 @@
|
|
1
|
+
let createBoltAccount = false;
|
2
|
+
|
3
|
+
const displayBoltInput = (paymentField, boltContainer, accountCheckbox) => {
|
4
|
+
paymentField.mount(boltContainer);
|
5
|
+
const statusContainer = document.getElementById("payment-status-container");
|
6
|
+
statusContainer.style.display = "";
|
7
|
+
|
8
|
+
if (accountCheckbox) {
|
9
|
+
accountCheckbox.mount("#account-checkbox")
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
13
|
+
const tokenize = async (paymentField, paymentMethodId, frontend) => {
|
14
|
+
await paymentField.tokenize()
|
15
|
+
.then((result) => {
|
16
|
+
if (result["token"]) {
|
17
|
+
updateOrder(result, paymentMethodId, frontend)
|
18
|
+
} else {
|
19
|
+
console.log(`error ${result["type"]}: ${result["message"]}`);
|
20
|
+
}
|
21
|
+
});
|
22
|
+
}
|
23
|
+
|
24
|
+
const redirectToNextStep = (frontend) => {
|
25
|
+
if (frontend) {
|
26
|
+
window.location.href = '/checkout/confirm';
|
27
|
+
} else {
|
28
|
+
window.location.href = `/admin/orders/${Spree.current_order_id}/payments`
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
const updateOrder = async (card, paymentMethodId, frontend) => {
|
33
|
+
await fetch(`/api/checkouts/${Spree.current_order_id}`, {
|
34
|
+
method: 'PATCH',
|
35
|
+
headers: {
|
36
|
+
'Content-Type': 'application/json',
|
37
|
+
'X-Spree-Order-Token': Spree.current_order_token
|
38
|
+
},
|
39
|
+
body: JSON.stringify({
|
40
|
+
'order': {
|
41
|
+
'payments_attributes': [{
|
42
|
+
'payment_method_id': paymentMethodId,
|
43
|
+
'source_attributes': {
|
44
|
+
'card_token': card['token'],
|
45
|
+
'card_last4': card['last4'],
|
46
|
+
'card_bin': card['bin'],
|
47
|
+
'card_number': card['number'],
|
48
|
+
'card_expiration': card['expiration'],
|
49
|
+
'card_postal_code': card['postal_code'],
|
50
|
+
'create_bolt_account': createBoltAccount
|
51
|
+
}
|
52
|
+
}]
|
53
|
+
}
|
54
|
+
})
|
55
|
+
})
|
56
|
+
.then(() => {
|
57
|
+
redirectToNextStep(frontend)
|
58
|
+
})
|
59
|
+
.catch((response) => {
|
60
|
+
console.log('Error updating order')
|
61
|
+
console.log(response)
|
62
|
+
});
|
63
|
+
}
|
64
|
+
|
65
|
+
document.addEventListener("DOMContentLoaded", async function () {
|
66
|
+
const boltContainer = document.getElementById("bolt-container");
|
67
|
+
|
68
|
+
if (boltContainer) {
|
69
|
+
const boltEmbedded = Bolt(boltContainer.dataset.publishableKey);
|
70
|
+
let accountCheckbox = null;
|
71
|
+
const frontend = boltContainer.dataset.frontend == "true" ? true : false;
|
72
|
+
const paymentMethodId = boltContainer.dataset.paymentMethodId
|
73
|
+
const cardButton = document.getElementById("bolt-card-button");
|
74
|
+
|
75
|
+
if(boltContainer.dataset["boltUserSignedIn"] != "true") {
|
76
|
+
accountCheckbox = boltEmbedded.create("account_checkbox");
|
77
|
+
accountCheckbox.on("change", checked => createBoltAccount = checked);
|
78
|
+
}
|
79
|
+
cardButton.addEventListener("click", () => {
|
80
|
+
const submitButton = document.getElementById("bolt-submit-button")
|
81
|
+
const paymentField = boltEmbedded.create("payment_component");
|
82
|
+
displayBoltInput(paymentField, boltContainer, accountCheckbox);
|
83
|
+
cardButton.style.display = 'none';
|
84
|
+
|
85
|
+
submitButton.addEventListener("click", () => {
|
86
|
+
tokenize(paymentField, paymentMethodId, frontend);
|
87
|
+
submitButton.disabled = true;
|
88
|
+
})
|
89
|
+
})
|
90
|
+
}
|
91
|
+
})
|
@@ -0,0 +1,18 @@
|
|
1
|
+
// Placeholder manifest file.
|
2
|
+
// the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/frontend/all.js'
|
3
|
+
|
4
|
+
//= require solidus_bolt
|
5
|
+
//= require authorize_account
|
6
|
+
|
7
|
+
const publishableKey = document.querySelector("meta[name=bolt-publishable-key]").getAttribute("content");
|
8
|
+
const domain = document.querySelector("meta[name=bolt-domain]").getAttribute("content");
|
9
|
+
|
10
|
+
document.addEventListener('DOMContentLoaded', function () {
|
11
|
+
const emailField = document.getElementById('spree_user_email');
|
12
|
+
if (emailField) {
|
13
|
+
emailField.onblur = async function () {
|
14
|
+
const authorize = new AuthorizeAccount(emailField, publishableKey, domain);
|
15
|
+
await authorize.accessRequest();
|
16
|
+
}
|
17
|
+
}
|
18
|
+
})
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
class AccountsController < BaseController
|
5
|
+
def create
|
6
|
+
Spree::User.find_by!(email: permitted_params[:email])
|
7
|
+
|
8
|
+
head :ok
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def permitted_params
|
14
|
+
params[:account]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
module SolidusBolt
|
6
|
+
class BaseController < ::Spree::Api::BaseController
|
7
|
+
skip_before_action :authenticate_user
|
8
|
+
skip_before_action :verify_authenticity_token
|
9
|
+
before_action :verify_bolt_request
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def verify_bolt_request
|
14
|
+
hmac_header = request.headers['X-Bolt-Hmac-Sha256']
|
15
|
+
signing_secret = SolidusBolt::BoltConfiguration.fetch&.signing_secret || ''
|
16
|
+
computed_hmac = Base64.encode64(OpenSSL::HMAC.digest("SHA256", signing_secret, permitted_params.to_json)).strip
|
17
|
+
|
18
|
+
return render json: { error: 'Unauthorized request' }, status: :unauthorized unless hmac_header == computed_hmac
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusBolt
|
4
|
+
class WebhooksController < BaseController
|
5
|
+
def update
|
6
|
+
::SolidusBolt::Sorter.call(permitted_params)
|
7
|
+
|
8
|
+
render json: {}, status: :ok
|
9
|
+
rescue StandardError => e
|
10
|
+
error_message = e.to_s
|
11
|
+
logger.error error_message
|
12
|
+
render json: { error: error_message }, status: :unprocessable_entity
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def permitted_params
|
18
|
+
params[:webhook]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spree
|
4
|
+
module Admin
|
5
|
+
class BoltWebhooksController < Spree::Admin::BaseController
|
6
|
+
def new; end
|
7
|
+
|
8
|
+
def create
|
9
|
+
response = SolidusBolt::Webhooks::CreateService.call(
|
10
|
+
event: bolt_webhook_params[:event],
|
11
|
+
url: bolt_webhook_params[:webhook_url]
|
12
|
+
)
|
13
|
+
flash[:success] = "Successfully created webhook. Webhook ID #{response['webhook_id']}"
|
14
|
+
|
15
|
+
render :new
|
16
|
+
rescue SolidusBolt::ServerError => e
|
17
|
+
flash[:error] = e.message
|
18
|
+
|
19
|
+
render :new
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def bolt_webhook_params
|
25
|
+
params
|
26
|
+
.require(:bolt_webhook)
|
27
|
+
.permit(
|
28
|
+
:event,
|
29
|
+
:webhook_url,
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|