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
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBolt
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ class_option :auto_run_migrations, type: :boolean, default: false
7
+ source_root File.expand_path('templates', __dir__)
8
+
9
+ def install_solidus_social
10
+ if File.exist? 'config/initializers/solidus_social.rb'
11
+ puts 'Skipping solidus_social:install' # rubocop:disable Rails/Output
12
+ else
13
+ say_status :install, 'solidus_social extension'
14
+ solidus_social_install_command = 'bin/rails generate solidus_social:install'
15
+ solidus_social_install_command += ' --auto-run-migrations' if options[:auto_run_migrations]
16
+
17
+ run solidus_social_install_command
18
+ end
19
+ end
20
+
21
+ def copy_initializer
22
+ template 'initializer.rb', 'config/initializers/solidus_bolt.rb'
23
+ end
24
+
25
+ def add_javascripts
26
+ append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/solidus_bolt\n"
27
+ append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/solidus_bolt\n"
28
+ end
29
+
30
+ def add_stylesheets
31
+ inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/solidus_bolt\n", before: %r{\*/}, verbose: true # rubocop:disable Layout/LineLength
32
+ inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/solidus_bolt\n", before: %r{\*/}, verbose: true # rubocop:disable Layout/LineLength
33
+ end
34
+
35
+ def add_migrations
36
+ run 'bin/rails railties:install:migrations FROM=solidus_bolt'
37
+ end
38
+
39
+ def add_bolt_omniauth_provider
40
+ solidus_social_initializer_file = 'config/initializers/solidus_social.rb'
41
+ return if File.readlines(solidus_social_initializer_file).grep(/bolt/).any?
42
+
43
+ matcher = /amazon: {\n\s*api_key:\sENV\['AMAZON_API_KEY'\],\n\s*api_secret:\sENV\['AMAZON_API_SECRET'\],\n\s*}\n\s*}/m # rubocop:disable Layout/LineLength
44
+ a = <<~BOLT_PROVIDER
45
+ amazon: {
46
+ api_key: ENV['AMAZON_API_KEY'],
47
+ api_secret: ENV['AMAZON_API_SECRET'],
48
+ }
49
+ }
50
+
51
+ begin
52
+ config.providers[:bolt] = {
53
+ api_key: SolidusBolt::BoltConfiguration.fetch.publishable_key,
54
+ api_secret: SolidusBolt::BoltConfiguration.fetch.api_key,
55
+ }
56
+ rescue StandardError
57
+ end
58
+ BOLT_PROVIDER
59
+
60
+ gsub_file solidus_social_initializer_file, matcher, a
61
+ end
62
+
63
+ def run_migrations
64
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]')) # rubocop:disable Layout/LineLength
65
+ if run_migrations
66
+ run 'bin/rails db:migrate'
67
+ else
68
+ puts 'Skipping bin/rails db:migrate, don\'t forget to run it!' # rubocop:disable Rails/Output
69
+ end
70
+ end
71
+
72
+ def populate_seed_data
73
+ say_status :loading, 'bolt seed data'
74
+ rake('db:seed:solidus_bolt')
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ SolidusBolt.configure do |config|
4
+ # TODO: Remember to change this with the actual preferences you have implemented!
5
+ # config.sample_preference = 'sample_value'
6
+ end
7
+
8
+ Rails.application.config.assets.precompile += %w(bolt_logo_standard.png)
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBolt
4
+ class Configuration
5
+ attr_accessor :bolt_api_key, :bolt_signing_secret, :bolt_publishable_key
6
+ end
7
+
8
+ class << self
9
+ def configuration
10
+ @configuration ||= Configuration.new
11
+ end
12
+
13
+ alias config configuration
14
+
15
+ def configure
16
+ yield configuration
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_core'
4
+ require 'solidus_support'
5
+
6
+ module SolidusBolt
7
+ class Engine < Rails::Engine
8
+ include SolidusSupport::EngineExtensions
9
+
10
+ isolate_namespace ::Spree
11
+
12
+ engine_name 'solidus_bolt'
13
+
14
+ initializer "solidus_bolt.add_static_preference", after: "spree.register.payment_methods" do |app|
15
+ app.config.spree.payment_methods << SolidusBolt::PaymentMethod
16
+ Spree::Config.static_model_preferences.add(
17
+ SolidusBolt::PaymentMethod,
18
+ 'bolt_credentials',
19
+ bolt_credentials_hash
20
+ )
21
+ Spree::Config.static_model_preferences.add(
22
+ SolidusBolt::PaymentMethod,
23
+ 'bolt_config_credentials',
24
+ bolt_config_credentials_hash
25
+ )
26
+
27
+ Spree::PermittedAttributes.source_attributes.concat(%i[
28
+ card_token card_last4 card_bin card_number card_expiration card_postal_code create_bolt_account
29
+ ])
30
+ end
31
+
32
+ # use rspec for tests
33
+ config.generators do |g|
34
+ g.test_framework :rspec
35
+ end
36
+
37
+ private
38
+
39
+ def bolt_credentials_hash
40
+ {
41
+ bolt_api_key: ENV['BOLT_API_KEY'],
42
+ bolt_signing_secret: ENV['BOLT_SIGNING_SECRET'],
43
+ bolt_publishable_key: ENV['BOLT_PUBLISHABLE_KEY'],
44
+ }
45
+ end
46
+
47
+ def bolt_config_credentials_hash
48
+ begin
49
+ bolt_config = SolidusBolt::BoltConfiguration.fetch
50
+ rescue ActiveRecord::StatementInvalid
51
+ bolt_config = nil
52
+ ensure
53
+ bolt_config_hash = {
54
+ bolt_api_key: bolt_config&.api_key,
55
+ bolt_signing_secret: bolt_config&.signing_secret,
56
+ bolt_publishable_key: bolt_config&.publishable_key,
57
+ }
58
+ end
59
+ bolt_config_hash
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ factory :bolt_configuration, class: SolidusBolt::BoltConfiguration do
5
+ bearer_token { SecureRandom.hex }
6
+ environment { 'sandbox' }
7
+ merchant_public_id { SecureRandom.hex }
8
+ division_public_id { SecureRandom.hex }
9
+ api_key { SecureRandom.hex }
10
+ signing_secret { SecureRandom.hex }
11
+ publishable_key { SecureRandom.hex }
12
+ end
13
+
14
+ factory :bolt_payment_method, class: SolidusBolt::PaymentMethod do
15
+ name { 'Bolt' }
16
+ preference_source { 'bolt_credentials' }
17
+ available_to_admin { true }
18
+ available_to_users { true }
19
+ end
20
+
21
+ factory :bolt_payment_source, class: SolidusBolt::PaymentSource do
22
+ create_bolt_account { false }
23
+ payment_method
24
+ end
25
+
26
+ factory :bolt_payment, class: Spree::Payment do
27
+ association(:payment_method, factory: :bolt_payment_method)
28
+ association(:source, factory: :bolt_payment_source)
29
+ order
30
+ state { 'checkout' }
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBolt
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_bolt/configuration'
4
+ require 'solidus_bolt/version'
5
+ require 'solidus_bolt/engine'
6
+ require 'httparty'
7
+ require 'solidus_social'
8
+ require 'omniauth-bolt'
9
+ require 'tweetnacl'
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :db do
4
+ namespace :seed do
5
+ desc 'Loads bolt sample data'
6
+ task solidus_bolt: :environment do
7
+ seed_file = Dir[SolidusBolt::Engine.root.join('db', 'seeds.rb')][0]
8
+ return unless File.exist?(seed_file)
9
+
10
+ puts "Seeding #{seed_file}..."
11
+ load(seed_file)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ <% if spree_current_user %>
2
+ <li><%= link_to t('spree.my_account'), spree.account_path %></li>
3
+ <li><%= link_to t('spree.logout'), spree.logout_path, method: Devise.sign_out_via %></li>
4
+ <% else %>
5
+ <li>
6
+ <div class="bolt-account-sso"></div>
7
+ </li>
8
+ <script>
9
+ function insertButtons() {
10
+ if (typeof BoltAccount === 'undefined') {
11
+ window.setTimeout(insertButtons, 100);
12
+ return;
13
+ }
14
+ BoltAccount.injectButtons();
15
+ }
16
+ insertButtons();
17
+ </script>
18
+ <% end %>
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/solidus_bolt/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'solidus_bolt'
7
+ spec.version = SolidusBolt::VERSION
8
+ spec.authors = ['piyushswain', 'Naokimi', 'DanielePalombo']
9
+ spec.email = 'contact@solidus.io'
10
+
11
+ spec.summary = 'Solidus extension for using Bolt Checkout Service.'
12
+ spec.homepage = 'https://github.com/nebulab/solidus_bolt#readme'
13
+ spec.license = 'Apache-2.0'
14
+
15
+ spec.metadata['homepage_uri'] = spec.homepage
16
+ spec.metadata['source_code_uri'] = 'https://github.com/nebulab/solidus_bolt'
17
+ spec.metadata['changelog_uri'] = 'https://github.com/nebulab/solidus_bolt/blob/master/CHANGELOG.md'
18
+
19
+ spec.required_ruby_version = Gem::Requirement.new('~> 2.5')
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
24
+
25
+ spec.files = files.grep_v(%r{^(test|spec|features)/})
26
+ spec.test_files = files.grep(%r{^(test|spec|features)/})
27
+ spec.bindir = "exe"
28
+ spec.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_dependency 'coffee-rails'
32
+ spec.add_dependency 'deface'
33
+ spec.add_dependency 'httparty'
34
+ spec.add_dependency 'omniauth-bolt'
35
+ spec.add_dependency 'rails', ['>0.a', '< 7.a']
36
+ spec.add_dependency 'solidus_auth_devise'
37
+ spec.add_dependency 'solidus_core', ['>= 2.0.0', '< 4']
38
+ spec.add_dependency 'solidus_social'
39
+ spec.add_dependency 'solidus_support', '~> 0.5'
40
+ spec.add_dependency 'tweetnacl'
41
+
42
+ spec.add_development_dependency 'pry'
43
+ spec.add_development_dependency 'solidus_dev_support', '~> 2.5'
44
+ spec.add_development_dependency 'vcr'
45
+ spec.add_development_dependency 'webmock'
46
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SolidusBolt::AddressDecorator do
4
+ let(:address) { create(:address) }
5
+ let(:email) { 'example@gmail.com' }
6
+
7
+ describe '#bolt_address' do
8
+ it 'returns a hash with address values and email' do
9
+ result = {
10
+ street_address1: address.address1,
11
+ street_address2: address.address2,
12
+ locality: address.city,
13
+ region: address.state.abbr,
14
+ postal_code: address.zipcode,
15
+ country_code: address.country.iso,
16
+ first_name: Spree::Address::Name.new(address.name).first_name,
17
+ last_name: Spree::Address::Name.new(address.name).last_name,
18
+ phone: address.phone,
19
+ email: email
20
+ }
21
+ expect(address.bolt_address(email)).to eq(result)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SolidusBolt::OrderDecorator do
4
+ let(:order) { create(:order_with_line_items) }
5
+
6
+ describe '#bolt_cart' do
7
+ it 'returns a hash with line items and price' do
8
+ result = {
9
+ total_amount: (order.total * 100).to_i,
10
+ order_reference: order.number,
11
+ currency: 'USD',
12
+ items: [{
13
+ sku: order.line_items.first.sku,
14
+ name: order.line_items.first.name,
15
+ unit_price: 1000,
16
+ quantity: 1
17
+ }]
18
+ }
19
+ expect(order.bolt_cart).to eq(result)
20
+ end
21
+ end
22
+
23
+ describe '#bolt_user_identifier' do
24
+ it 'returns a hash with email and phone' do
25
+ result = { email: order.email, phone: '555-555-0199' }
26
+ expect(order.bolt_user_identifier).to eq(result)
27
+ end
28
+ end
29
+
30
+ describe '#bolt_user_identity' do
31
+ it 'returns a hash with name' do
32
+ result = { 'first_name': 'John', 'last_name': 'Doe' }
33
+ expect(order.bolt_user_identity).to eq(result)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SolidusBolt::PaymentDecorator do
4
+ describe '#can_void?' do
5
+ [
6
+ 'checkout',
7
+ 'completed',
8
+ 'processing',
9
+ 'failed',
10
+ 'void',
11
+ 'invalid'
12
+ ].each do |state|
13
+ let(:payment) { Spree::Payment.new(state: state) }
14
+
15
+ context "when payment state is #{state}" do
16
+ it 'return false' do
17
+ expect(payment.can_void?).to eq false
18
+ end
19
+ end
20
+ end
21
+
22
+ context "when payment state is pending" do
23
+ let(:payment) { Spree::Payment.new(state: 'pending') }
24
+
25
+ it 'return true' do
26
+ expect(payment.can_void?).to eq true
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,137 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api-sandbox.bolt.com/v1/account
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - "<BEARER AUTHORIZATION>"
12
+ X-Api-Key:
13
+ - "<API_KEY>"
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ Accept:
17
+ - "*/*"
18
+ User-Agent:
19
+ - Ruby
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ Date:
26
+ - Tue, 31 May 2022 01:24:31 GMT
27
+ Content-Type:
28
+ - application/json; charset=UTF-8
29
+ Transfer-Encoding:
30
+ - chunked
31
+ Connection:
32
+ - keep-alive
33
+ Public-Key-Pins-Report-Only:
34
+ - max-age=2592000;pin-sha256="OGAVOYhLZd3ADKYGFZOED1c5m1ObMlRv9PyIWrO4Nd0=";pin-sha256="RRM1dGqnDFsCJXBTHky16vi1obOlCgFFn/yOhI/y+ho=";pin-sha256="IXHYSIdST+XY22J5ivybYkntMIfjA5P6pMKX2hWG1BE=";report-uri="https://77aa1bd121ef22d50247a23390ce6cff.report-uri.io/r/default/hpkp/reportOnly"
35
+ Set-Cookie:
36
+ - trk=6ed2577d-8b6e-40d7-b914-5f99f4232557; Path=/; Max-Age=31536000; HttpOnly;
37
+ Secure; SameSite=None
38
+ Strict-Transport-Security:
39
+ - max-age=31536000; includeSubDomains; preload
40
+ X-Bolt-Api-Version:
41
+ - '2022-01-01'
42
+ X-Bolt-Trace-Id:
43
+ - Root=1-62956e4f-1a1d5d9371616b3e0bd6cead
44
+ X-Device-Id:
45
+ - a86166e54134f40be90bf98bdf9f1fcac49d23ac4259202b5038010473c636ba
46
+ X-Envoy-Upstream-Service-Time:
47
+ - '36'
48
+ Server:
49
+ - envoy
50
+ body:
51
+ encoding: UTF-8
52
+ string: '{"profile":{"first_name":"asdasd","last_name":"fsfasdf","email":"paulodalberti@nebulab.com","phone":"","name":"asdasd
53
+ fsfasdf"},"addresses":[{"id":"SAM3dnUUNSbQ","street_address1":"PO Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
54
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email1@example.com","default":true},{"id":"SA728NNovMq9H","street_address1":"PO
55
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10003","country_code":"US","name":"John
56
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email2@example.com","default":false},{"id":"SAhkRT5iEmfuW","street_address1":"125
57
+ E Anderson St","locality":"Savannah","region":"GA","region_code":"GA","postal_code":"31401","country_code":"US","name":"asdasd
58
+ fsfasdf","first_name":"asdasd","last_name":"fsfasdf","phone_number":"2138739655","email_address":"paulodalberti@nebulab.com","default":false}],"payment_methods":[{"id":"CAmPJ4VxVomvF","type":"card","last4":"1004","billing_address":{"id":"AA3NQrvuvWVH1","street_address1":"PO
59
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
60
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email1@example.com"},"network":"visa","default":true,"exp_month":5,"exp_year":2023},{"id":"CAenwUpogeBSY","type":"card","last4":"1004","billing_address":{"id":"AA655ttsE5Htm","street_address1":"PO
61
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
62
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email1@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA7kkwvW31AoT","type":"card","last4":"1111","billing_address":{"id":"AA2PPbJDDiPtB","street_address1":"PO
63
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
64
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email1@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA3TiBuJ4g67n","type":"card","last4":"1004","billing_address":{"id":"AA5cLNYqwZyju","street_address1":"PO
65
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10003","country_code":"US","name":"John
66
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email2@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA3BEgomxDJa5","type":"card","last4":"1004","billing_address":{"id":"AAiTJHw4UuWHZ","street_address1":"PO
67
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10005","country_code":"US","name":"John
68
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email3@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA2pgKkgs8Dos","type":"card","last4":"1004","billing_address":{"id":"AAm26afVomLeX","street_address1":"PO
69
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10003","country_code":"US","name":"John
70
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email2@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA49oJyzVdLpw","type":"card","last4":"1004","billing_address":{"id":"AAixhuCPHkQAK","street_address1":"PO
71
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10005","country_code":"US","name":"John
72
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email3@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA5GPxSBpjzie","type":"card","last4":"1004","billing_address":{"id":"AA4JgwX9yDRUR","street_address1":"PO
73
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
74
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email1@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CAhGYDjM3THGj","type":"card","last4":"1004","billing_address":{"id":"AA5h6pHzyG4rq","street_address1":"PO
75
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10003","country_code":"US","name":"John
76
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email2@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CAeHu5S7Yjyek","type":"card","last4":"1004","billing_address":{"id":"AAecNe4HcX5WG","street_address1":"10
77
+ Lovely Street","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
78
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"example@email.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CAeZyt3phevU4","type":"card","last4":"1004","billing_address":{"id":"AAaGXye8QrAc1","street_address1":"10
79
+ Lovely Street","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
80
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"example@email.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CAgP9uNSEZBNL","type":"card","last4":"1111","network":"visa","default":false,"exp_month":4,"exp_year":2098}],"has_bolt_account":true}'
81
+ recorded_at: Tue, 31 May 2022 01:24:31 GMT
82
+ - request:
83
+ method: post
84
+ uri: https://api-sandbox.bolt.com/v1/account/addresses
85
+ body:
86
+ encoding: UTF-8
87
+ string: '{"street_address1":"6420","street_address2":"Northwest","locality":"Herndon","region":"AL","postal_code":"10005","country_code":"US","first_name":"John","last_name":"Von
88
+ Doe","phone":"555-555-0199","email":"email2@example.com"}'
89
+ headers:
90
+ Authorization:
91
+ - "<BEARER AUTHORIZATION>"
92
+ Content-Type:
93
+ - application/json
94
+ X-Api-Key:
95
+ - "<API_KEY>"
96
+ Accept-Encoding:
97
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
98
+ Accept:
99
+ - "*/*"
100
+ User-Agent:
101
+ - Ruby
102
+ response:
103
+ status:
104
+ code: 200
105
+ message: OK
106
+ headers:
107
+ Date:
108
+ - Tue, 31 May 2022 01:24:32 GMT
109
+ Content-Type:
110
+ - application/json; charset=UTF-8
111
+ Content-Length:
112
+ - '320'
113
+ Connection:
114
+ - keep-alive
115
+ Public-Key-Pins-Report-Only:
116
+ - max-age=2592000;pin-sha256="OGAVOYhLZd3ADKYGFZOED1c5m1ObMlRv9PyIWrO4Nd0=";pin-sha256="RRM1dGqnDFsCJXBTHky16vi1obOlCgFFn/yOhI/y+ho=";pin-sha256="IXHYSIdST+XY22J5ivybYkntMIfjA5P6pMKX2hWG1BE=";report-uri="https://77aa1bd121ef22d50247a23390ce6cff.report-uri.io/r/default/hpkp/reportOnly"
117
+ Set-Cookie:
118
+ - trk=26fa7207-763a-4620-a372-ac05b017834f; Path=/; Max-Age=31536000; HttpOnly;
119
+ Secure; SameSite=None
120
+ Strict-Transport-Security:
121
+ - max-age=31536000; includeSubDomains; preload
122
+ X-Bolt-Api-Version:
123
+ - '2022-01-01'
124
+ X-Bolt-Trace-Id:
125
+ - Root=1-62956e50-70f9b1fb090f35a065a34527
126
+ X-Device-Id:
127
+ - fa03959d09d885b5e85ab84c0070c6ce61dedeadc49829e6aef673d767a766df
128
+ X-Envoy-Upstream-Service-Time:
129
+ - '64'
130
+ Server:
131
+ - envoy
132
+ body:
133
+ encoding: UTF-8
134
+ string: '{"id":"SAfjEyWzmuxRs","street_address1":"6420","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10005","country_code":"US","name":"John
135
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email2@example.com","default":false}'
136
+ recorded_at: Tue, 31 May 2022 01:24:32 GMT
137
+ recorded_with: VCR 6.1.0
@@ -0,0 +1,82 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://api-sandbox.bolt.com/v1/account
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Authorization:
11
+ - "<BEARER AUTHORIZATION>"
12
+ X-Api-Key:
13
+ - "<API_KEY>"
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ Accept:
17
+ - "*/*"
18
+ User-Agent:
19
+ - Ruby
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ Date:
26
+ - Tue, 31 May 2022 01:23:36 GMT
27
+ Content-Type:
28
+ - application/json; charset=UTF-8
29
+ Transfer-Encoding:
30
+ - chunked
31
+ Connection:
32
+ - keep-alive
33
+ Public-Key-Pins-Report-Only:
34
+ - max-age=2592000;pin-sha256="OGAVOYhLZd3ADKYGFZOED1c5m1ObMlRv9PyIWrO4Nd0=";pin-sha256="RRM1dGqnDFsCJXBTHky16vi1obOlCgFFn/yOhI/y+ho=";pin-sha256="IXHYSIdST+XY22J5ivybYkntMIfjA5P6pMKX2hWG1BE=";report-uri="https://77aa1bd121ef22d50247a23390ce6cff.report-uri.io/r/default/hpkp/reportOnly"
35
+ Set-Cookie:
36
+ - trk=0049c155-2eb5-44fd-9a20-c73a847baa68; Path=/; Max-Age=31536000; HttpOnly;
37
+ Secure; SameSite=None
38
+ Strict-Transport-Security:
39
+ - max-age=31536000; includeSubDomains; preload
40
+ X-Bolt-Api-Version:
41
+ - '2022-01-01'
42
+ X-Bolt-Trace-Id:
43
+ - Root=1-62956e18-5aef499c7a23d91d0b98ba02
44
+ X-Device-Id:
45
+ - ade588f38215c8bd482908a0a3d00e03cfd2c3207c04ea86f1d04a9a83be77a8
46
+ X-Envoy-Upstream-Service-Time:
47
+ - '141'
48
+ Server:
49
+ - envoy
50
+ body:
51
+ encoding: UTF-8
52
+ string: '{"profile":{"first_name":"asdasd","last_name":"fsfasdf","email":"paulodalberti@nebulab.com","phone":"","name":"asdasd
53
+ fsfasdf"},"addresses":[{"id":"SAM3dnUUNSbQ","street_address1":"PO Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
54
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email1@example.com","default":true},{"id":"SA728NNovMq9H","street_address1":"PO
55
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10003","country_code":"US","name":"John
56
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email2@example.com","default":false},{"id":"SAhkRT5iEmfuW","street_address1":"125
57
+ E Anderson St","locality":"Savannah","region":"GA","region_code":"GA","postal_code":"31401","country_code":"US","name":"asdasd
58
+ fsfasdf","first_name":"asdasd","last_name":"fsfasdf","phone_number":"2138739655","email_address":"paulodalberti@nebulab.com","default":false}],"payment_methods":[{"id":"CAmPJ4VxVomvF","type":"card","last4":"1004","billing_address":{"id":"AA3NQrvuvWVH1","street_address1":"PO
59
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
60
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email1@example.com"},"network":"visa","default":true,"exp_month":5,"exp_year":2023},{"id":"CAenwUpogeBSY","type":"card","last4":"1004","billing_address":{"id":"AA655ttsE5Htm","street_address1":"PO
61
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
62
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email1@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA7kkwvW31AoT","type":"card","last4":"1111","billing_address":{"id":"AA2PPbJDDiPtB","street_address1":"PO
63
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
64
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email1@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA3TiBuJ4g67n","type":"card","last4":"1004","billing_address":{"id":"AA5cLNYqwZyju","street_address1":"PO
65
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10003","country_code":"US","name":"John
66
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email2@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA3BEgomxDJa5","type":"card","last4":"1004","billing_address":{"id":"AAiTJHw4UuWHZ","street_address1":"PO
67
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10005","country_code":"US","name":"John
68
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email3@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA2pgKkgs8Dos","type":"card","last4":"1004","billing_address":{"id":"AAm26afVomLeX","street_address1":"PO
69
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10003","country_code":"US","name":"John
70
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email2@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA49oJyzVdLpw","type":"card","last4":"1004","billing_address":{"id":"AAixhuCPHkQAK","street_address1":"PO
71
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10005","country_code":"US","name":"John
72
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email3@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CA5GPxSBpjzie","type":"card","last4":"1004","billing_address":{"id":"AA4JgwX9yDRUR","street_address1":"PO
73
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
74
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email1@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CAhGYDjM3THGj","type":"card","last4":"1004","billing_address":{"id":"AA5h6pHzyG4rq","street_address1":"PO
75
+ Box 1337","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10003","country_code":"US","name":"John
76
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"email2@example.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CAeHu5S7Yjyek","type":"card","last4":"1004","billing_address":{"id":"AAecNe4HcX5WG","street_address1":"10
77
+ Lovely Street","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
78
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"example@email.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CAeZyt3phevU4","type":"card","last4":"1004","billing_address":{"id":"AAaGXye8QrAc1","street_address1":"10
79
+ Lovely Street","street_address2":"Northwest","locality":"Herndon","region":"AL","region_code":"AL","postal_code":"10001","country_code":"US","name":"John
80
+ Von Doe","first_name":"John","last_name":"Von Doe","phone_number":"555-555-0199","email_address":"example@email.com"},"network":"visa","default":false,"exp_month":5,"exp_year":2023},{"id":"CAgP9uNSEZBNL","type":"card","last4":"1111","network":"visa","default":false,"exp_month":4,"exp_year":2098}],"has_bolt_account":true}'
81
+ recorded_at: Tue, 31 May 2022 01:23:36 GMT
82
+ recorded_with: VCR 6.1.0