solidus_bolt 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +41 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/stale.yml +17 -0
  5. data/.github_changelog_generator +2 -0
  6. data/.gitignore +20 -0
  7. data/.rspec +2 -0
  8. data/.rubocop.yml +11 -0
  9. data/CHANGELOG.md +1 -0
  10. data/Gemfile +33 -0
  11. data/LICENSE +26 -0
  12. data/README.md +175 -0
  13. data/Rakefile +6 -0
  14. data/app/assets/images/bolt_logo_standard.png +0 -0
  15. data/app/assets/javascripts/authorize_account.js +74 -0
  16. data/app/assets/javascripts/solidus_bolt.js +91 -0
  17. data/app/assets/javascripts/spree/backend/solidus_bolt.js +4 -0
  18. data/app/assets/javascripts/spree/frontend/solidus_bolt.js +18 -0
  19. data/app/assets/stylesheets/spree/backend/solidus_bolt.css +4 -0
  20. data/app/assets/stylesheets/spree/frontend/solidus_bolt.css +4 -0
  21. data/app/controllers/solidus_bolt/accounts_controller.rb +17 -0
  22. data/app/controllers/solidus_bolt/base_controller.rb +21 -0
  23. data/app/controllers/solidus_bolt/webhooks_controller.rb +21 -0
  24. data/app/controllers/spree/admin/bolt_webhooks_controller.rb +34 -0
  25. data/app/controllers/spree/admin/bolts_controller.rb +43 -0
  26. data/app/decorators/controllers/solidus_bolt/spree_checkout_controller/add_addresses_to_bolt.rb +23 -0
  27. data/app/decorators/controllers/solidus_bolt/spree_checkout_controller/refresh_bolt_addresses.rb +17 -0
  28. data/app/decorators/controllers/solidus_bolt/spree_checkout_controller/refresh_bolt_payment_source.rb +17 -0
  29. data/app/decorators/models/solidus_bolt/address_decorator.rb +22 -0
  30. data/app/decorators/models/solidus_bolt/log_entry_decorator.rb +11 -0
  31. data/app/decorators/models/solidus_bolt/order_decorator.rb +44 -0
  32. data/app/decorators/models/solidus_bolt/payment_decorator.rb +11 -0
  33. data/app/decorators/omniauth/strategies/bolt_decorator.rb +16 -0
  34. data/app/jobs/solidus_bolt/add_address_job.rb +11 -0
  35. data/app/models/solidus_bolt/bolt_configuration.rb +74 -0
  36. data/app/models/solidus_bolt/gateway.rb +133 -0
  37. data/app/models/solidus_bolt/payment_method.rb +35 -0
  38. data/app/models/solidus_bolt/payment_source.rb +13 -0
  39. data/app/models/solidus_bolt.rb +7 -0
  40. data/app/overrides/spree/shared/_head/add_bolt_embed_script.html.erb.deface +6 -0
  41. data/app/services/solidus_bolt/accounts/add_address_service.rb +55 -0
  42. data/app/services/solidus_bolt/accounts/add_payment_method_service.rb +45 -0
  43. data/app/services/solidus_bolt/accounts/detail_service.rb +38 -0
  44. data/app/services/solidus_bolt/accounts/detect_account_service.rb +34 -0
  45. data/app/services/solidus_bolt/base_service.rb +55 -0
  46. data/app/services/solidus_bolt/oauth/token_service.rb +43 -0
  47. data/app/services/solidus_bolt/payments/capture_sync_service.rb +24 -0
  48. data/app/services/solidus_bolt/payments/credit_sync_service.rb +44 -0
  49. data/app/services/solidus_bolt/payments/void_sync_service.rb +18 -0
  50. data/app/services/solidus_bolt/server_error.rb +6 -0
  51. data/app/services/solidus_bolt/transactions/authorize_service.rb +72 -0
  52. data/app/services/solidus_bolt/transactions/base_service.rb +28 -0
  53. data/app/services/solidus_bolt/transactions/capture_service.rb +46 -0
  54. data/app/services/solidus_bolt/transactions/detail_service.rb +38 -0
  55. data/app/services/solidus_bolt/transactions/refund_service.rb +46 -0
  56. data/app/services/solidus_bolt/transactions/void_service.rb +44 -0
  57. data/app/services/solidus_bolt/users/refresh_access_token_service.rb +44 -0
  58. data/app/services/solidus_bolt/users/sync_addresses_service.rb +49 -0
  59. data/app/services/solidus_bolt/users/sync_payment_sources_service.rb +50 -0
  60. data/app/services/solidus_bolt/webhooks/create_service.rb +52 -0
  61. data/app/views/spree/admin/bolt_webhooks/new.html.erb +22 -0
  62. data/app/views/spree/admin/bolts/_configuration.html.erb +32 -0
  63. data/app/views/spree/admin/bolts/_form.html.erb +29 -0
  64. data/app/views/spree/admin/bolts/edit.html.erb +6 -0
  65. data/app/views/spree/admin/bolts/show.html.erb +21 -0
  66. data/app/views/spree/admin/payments/source_forms/_bolt.html.erb +1 -0
  67. data/app/views/spree/admin/payments/source_views/_bolt.html.erb +2 -0
  68. data/app/views/spree/api/payments/source_views/_bolt.json.jbuilder +4 -0
  69. data/app/views/spree/checkout/existing_payment/_bolt.html.erb +7 -0
  70. data/app/views/spree/checkout/payment/_bolt.html.erb +1 -0
  71. data/app/views/spree/shared/payment/_bolt.html.erb +19 -0
  72. data/app/webhooks/solidus_bolt/handlers/base_handler.rb +27 -0
  73. data/app/webhooks/solidus_bolt/handlers/capture_handler.rb +13 -0
  74. data/app/webhooks/solidus_bolt/handlers/credit_handler.rb +18 -0
  75. data/app/webhooks/solidus_bolt/handlers/void_handler.rb +11 -0
  76. data/app/webhooks/solidus_bolt/sorter.rb +33 -0
  77. data/bin/console +17 -0
  78. data/bin/rails +7 -0
  79. data/bin/rails-engine +13 -0
  80. data/bin/rails-sandbox +16 -0
  81. data/bin/rake +7 -0
  82. data/bin/sandbox +86 -0
  83. data/bin/setup +8 -0
  84. data/config/initializers/menu_items.rb +14 -0
  85. data/config/locales/en.yml +17 -0
  86. data/config/routes.rb +11 -0
  87. data/db/migrate/20220330094232_create_solidus_bolt_payment_sources.rb +16 -0
  88. data/db/migrate/20220413063328_create_solidus_bolt_bolt_configurations.rb +14 -0
  89. data/db/migrate/20220502005041_swap_url_for_env_boolean_in_bolt_configuration.rb +6 -0
  90. data/db/migrate/20220509102309_rework_solidus_bolt_payment_sources.rb +19 -0
  91. data/db/migrate/20220510075227_add_create_bolt_account_to_solidus_bolt_payment_source.rb +5 -0
  92. data/db/migrate/20220519233043_add_user_to_solidus_bolt_payment_source.rb +5 -0
  93. data/db/migrate/20220526005619_remove_user_id_from_solidus_bolt_payment_source.rb +5 -0
  94. data/db/migrate/20220530102107_rename_bolt_configuration_merchant_id_to_division_public_id.rb +5 -0
  95. data/db/migrate/20220531075527_update_bolt_configuration_environment_column_restrictions.rb +6 -0
  96. data/db/seeds.rb +30 -0
  97. data/lib/generators/solidus_bolt/install/install_generator.rb +78 -0
  98. data/lib/generators/solidus_bolt/install/templates/initializer.rb +8 -0
  99. data/lib/solidus_bolt/configuration.rb +19 -0
  100. data/lib/solidus_bolt/engine.rb +62 -0
  101. data/lib/solidus_bolt/testing_support/factories.rb +32 -0
  102. data/lib/solidus_bolt/version.rb +5 -0
  103. data/lib/solidus_bolt.rb +9 -0
  104. data/lib/tasks/db/seed/solidus_bolt.rake +14 -0
  105. data/lib/views/frontend/spree/shared/_login_bar_items.html.erb +18 -0
  106. data/solidus_bolt.gemspec +46 -0
  107. data/spec/decorators/models/solidus_bolt/address_decorator_spec.rb +24 -0
  108. data/spec/decorators/models/solidus_bolt/order_decorator_spec.rb +36 -0
  109. data/spec/decorators/models/solidus_bolt/payment_decorator_spec.rb +30 -0
  110. data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_AddAddressService/_call/with_correct_access_token/receives_a_successful_response.yml +137 -0
  111. data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_AddAddressService/_call/with_existing_address/skips_the_add_address_call.yml +82 -0
  112. data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_AddAddressService/_call/with_wrong_access_token/gives_an_error.yml +55 -0
  113. data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_AddPaymentMethodService/_call/with_correct_access_token/receives_a_successful_response.yml +186 -0
  114. data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_AddPaymentMethodService/_call/with_wrong_access_token/gives_an_error.yml +179 -0
  115. data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_DetailService/_call/with_correct_access_token/receives_a_successful_response.yml +55 -0
  116. data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_DetailService/_call/with_wrong_access_token/gives_an_error.yml +55 -0
  117. data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_DetectAccountService/_call/receives_the_correct_response.yml +50 -0
  118. data/spec/fixtures/vcr_cassettes/SolidusBolt_Accounts_DetectAccountService/_call/returns_status_200.yml +50 -0
  119. data/spec/fixtures/vcr_cassettes/SolidusBolt_Oauth_TokenService/_call/makes_the_API_call.yml +59 -0
  120. data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_AuthorizeService/when_repeat_payment/_call/makes_the_API_call.yml +305 -0
  121. data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_AuthorizeService/with_auto_capture/_call/makes_the_API_call.yml +307 -0
  122. data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_AuthorizeService/without_auto_capture/_call/makes_the_API_call.yml +252 -0
  123. data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_CaptureService/_call/makes_the_API_call.yml +242 -0
  124. data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_DetailService/_call/makes_the_API_call.yml +244 -0
  125. data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_RefundService/_call/makes_the_API_call.yml +296 -0
  126. data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_VoidService/_call/makes_the_API_call.yml +242 -0
  127. data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_VoidService/_call/when_transaction_id_is_missing/makes_the_API_call.yml +242 -0
  128. data/spec/fixtures/vcr_cassettes/SolidusBolt_Transactions_VoidService/_call/when_transaction_reference_is_missing/makes_the_API_call.yml +242 -0
  129. data/spec/fixtures/vcr_cassettes/SolidusBolt_Users_SyncAddressesService/_call/adds_the_bill_address_to_the_user.yml +165 -0
  130. data/spec/fixtures/vcr_cassettes/SolidusBolt_Users_SyncAddressesService/_call/adds_the_ship_address_to_the_user.yml +165 -0
  131. data/spec/fixtures/vcr_cassettes/SolidusBolt_Users_SyncPaymentSourcesService/_call/creates_a_new_payment_source_with_card_ID.yml +55 -0
  132. data/spec/fixtures/vcr_cassettes/SolidusBolt_Webhooks_CreateService/_call/with_all_event/returns_a_webhook_id.yml +54 -0
  133. data/spec/fixtures/vcr_cassettes/SolidusBolt_Webhooks_CreateService/_call/with_an_event/returns_a_webhook_id.yml +54 -0
  134. data/spec/fixtures/vcr_cassettes/SolidusBolt_Webhooks_CreateService/_call/with_empty_event/raises_a_server_error.yml +57 -0
  135. data/spec/jobs/solidus_bolt/add_address_job_spec.rb +18 -0
  136. data/spec/models/solidus_bolt/bolt_configuration_spec.rb +173 -0
  137. data/spec/models/solidus_bolt/gateway_spec.rb +130 -0
  138. data/spec/models/solidus_bolt/payment_method_spec.rb +21 -0
  139. data/spec/models/solidus_bolt/payment_source_spec.rb +22 -0
  140. data/spec/requests/solidus_bolt/accounts_controller_spec.rb +41 -0
  141. data/spec/requests/solidus_bolt/webhooks_controller_spec.rb +122 -0
  142. data/spec/requests/spree/admin/bolt_spec.rb +71 -0
  143. data/spec/requests/spree/admin/bolt_webhook_spec.rb +35 -0
  144. data/spec/requests/spree/checkout_controller_spec.rb +117 -0
  145. data/spec/services/solidus_bolt/accounts/add_address_service_spec.rb +45 -0
  146. data/spec/services/solidus_bolt/accounts/add_payment_method_service_spec.rb +47 -0
  147. data/spec/services/solidus_bolt/accounts/detail_service_spec.rb +32 -0
  148. data/spec/services/solidus_bolt/accounts/detect_account_service_spec.rb +15 -0
  149. data/spec/services/solidus_bolt/base_service_spec.rb +49 -0
  150. data/spec/services/solidus_bolt/oauth/token_service_spec.rb +15 -0
  151. data/spec/services/solidus_bolt/payments/capture_sync_service_spec.rb +27 -0
  152. data/spec/services/solidus_bolt/payments/credit_sync_service_spec.rb +38 -0
  153. data/spec/services/solidus_bolt/payments/void_sync_service_spec.rb +25 -0
  154. data/spec/services/solidus_bolt/transactions/authorize_service_spec.rb +117 -0
  155. data/spec/services/solidus_bolt/transactions/base_service_spec.rb +38 -0
  156. data/spec/services/solidus_bolt/transactions/capture_service_spec.rb +37 -0
  157. data/spec/services/solidus_bolt/transactions/detail_service_spec.rb +31 -0
  158. data/spec/services/solidus_bolt/transactions/refund_service_spec.rb +42 -0
  159. data/spec/services/solidus_bolt/transactions/void_service_spec.rb +70 -0
  160. data/spec/services/solidus_bolt/users/refresh_access_token_service_spec.rb +45 -0
  161. data/spec/services/solidus_bolt/users/sync_addresses_service_spec.rb +74 -0
  162. data/spec/services/solidus_bolt/users/sync_payment_sources_service_spec.rb +25 -0
  163. data/spec/services/solidus_bolt/webhooks/create_service_spec.rb +33 -0
  164. data/spec/spec_helper.rb +37 -0
  165. data/spec/support/bolt_configuration.rb +26 -0
  166. data/spec/support/bolt_helper.rb +66 -0
  167. data/spec/support/vcr.rb +29 -0
  168. data/spec/webhooks/solidus_bolt/handlers/base_handler_spec.rb +13 -0
  169. data/spec/webhooks/solidus_bolt/handlers/capture_handler_spec.rb +24 -0
  170. data/spec/webhooks/solidus_bolt/handlers/credit_handler_spec.rb +32 -0
  171. data/spec/webhooks/solidus_bolt/handlers/void_handler_spec.rb +19 -0
  172. data/spec/webhooks/solidus_bolt/sorter_spec.rb +39 -0
  173. 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
@@ -0,0 +1,5 @@
1
+ bump:
2
+ recurse: false
3
+ file: 'lib/solidus_bolt/version.rb'
4
+ message: Bump SolidusBolt to %{version}
5
+ tag: true
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
@@ -0,0 +1,2 @@
1
+ issues=false
2
+ exclude-labels=infrastructure
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ \#*
3
+ *~
4
+ .#*
5
+ .DS_Store
6
+ .idea
7
+ .project
8
+ .sass-cache
9
+ coverage
10
+ Gemfile.lock
11
+ tmp
12
+ nbproject
13
+ pkg
14
+ *.swp
15
+ spec/dummy
16
+ spec/examples.txt
17
+ /sandbox
18
+ .rvmrc
19
+ .ruby-version
20
+ .ruby-gemset
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,11 @@
1
+ require:
2
+ - solidus_dev_support/rubocop
3
+
4
+ AllCops:
5
+ NewCops: disable
6
+
7
+ RSpec/MultipleMemoizedHelpers:
8
+ Enabled: false
9
+
10
+ RSpec/MultipleExpectations:
11
+ Max: 5
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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_dev_support/rake_tasks'
4
+ SolidusDevSupport::RakeTasks.install
5
+
6
+ task default: 'extension:specs'
@@ -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,4 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/backend/all.js'
3
+
4
+ //= require solidus_bolt
@@ -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,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
4
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/frontend/all.css'
4
+ */
@@ -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