spree_adyen 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +9 -0
  3. data/README.md +59 -0
  4. data/Rakefile +26 -0
  5. data/app/assets/config/spree_adyen_manifest.js +5 -0
  6. data/app/controllers/spree_adyen/apple_pay_domain_verification_controller.rb +11 -0
  7. data/app/controllers/spree_adyen/payment_sessions_controller.rb +67 -0
  8. data/app/controllers/spree_adyen/store_controller_decorator.rb +9 -0
  9. data/app/controllers/spree_adyen/webhooks_controller.rb +46 -0
  10. data/app/helpers/spree_adyen/base_helper.rb +18 -0
  11. data/app/javascript/spree_adyen/application.js +17 -0
  12. data/app/javascript/spree_adyen/controllers/checkout_adyen_controller.js +80 -0
  13. data/app/javascript/spree_adyen/controllers/redirect_adyen_controller.js +35 -0
  14. data/app/jobs/spree_adyen/add_allowed_origin_job.rb +24 -0
  15. data/app/jobs/spree_adyen/base_job.rb +5 -0
  16. data/app/jobs/spree_adyen/webhooks/process_authorisation_event_job.rb +10 -0
  17. data/app/jobs/spree_adyen/webhooks/process_cancellation_event_job.rb +10 -0
  18. data/app/jobs/spree_adyen/webhooks/process_capture_event_job.rb +10 -0
  19. data/app/models/spree/payment_sessions/adyen.rb +59 -0
  20. data/app/models/spree_adyen/base.rb +6 -0
  21. data/app/models/spree_adyen/custom_domain_decorator.rb +17 -0
  22. data/app/models/spree_adyen/gateway/payment_sessions.rb +151 -0
  23. data/app/models/spree_adyen/gateway.rb +487 -0
  24. data/app/models/spree_adyen/order_decorator.rb +23 -0
  25. data/app/models/spree_adyen/payment_decorator.rb +66 -0
  26. data/app/models/spree_adyen/payment_method_decorator.rb +15 -0
  27. data/app/models/spree_adyen/payment_session.rb +121 -0
  28. data/app/models/spree_adyen/payment_sources/ach_direct_debit.rb +17 -0
  29. data/app/models/spree_adyen/payment_sources/affirm.rb +17 -0
  30. data/app/models/spree_adyen/payment_sources/afterpay.rb +17 -0
  31. data/app/models/spree_adyen/payment_sources/alipay.rb +19 -0
  32. data/app/models/spree_adyen/payment_sources/alipay_hk.rb +17 -0
  33. data/app/models/spree_adyen/payment_sources/alma.rb +17 -0
  34. data/app/models/spree_adyen/payment_sources/ancv.rb +17 -0
  35. data/app/models/spree_adyen/payment_sources/apple_pay.rb +19 -0
  36. data/app/models/spree_adyen/payment_sources/atome.rb +17 -0
  37. data/app/models/spree_adyen/payment_sources/bacs.rb +17 -0
  38. data/app/models/spree_adyen/payment_sources/bancontact.rb +19 -0
  39. data/app/models/spree_adyen/payment_sources/bank_transfer.rb +17 -0
  40. data/app/models/spree_adyen/payment_sources/base.rb +7 -0
  41. data/app/models/spree_adyen/payment_sources/bcmc.rb +17 -0
  42. data/app/models/spree_adyen/payment_sources/bcmc_mobile.rb +17 -0
  43. data/app/models/spree_adyen/payment_sources/benefit.rb +17 -0
  44. data/app/models/spree_adyen/payment_sources/billie.rb +17 -0
  45. data/app/models/spree_adyen/payment_sources/bizum.rb +17 -0
  46. data/app/models/spree_adyen/payment_sources/blik.rb +15 -0
  47. data/app/models/spree_adyen/payment_sources/boleto.rb +17 -0
  48. data/app/models/spree_adyen/payment_sources/cash_app_afterpay.rb +17 -0
  49. data/app/models/spree_adyen/payment_sources/cashapp.rb +17 -0
  50. data/app/models/spree_adyen/payment_sources/clearpay.rb +17 -0
  51. data/app/models/spree_adyen/payment_sources/dana.rb +17 -0
  52. data/app/models/spree_adyen/payment_sources/doku.rb +19 -0
  53. data/app/models/spree_adyen/payment_sources/duitnow.rb +17 -0
  54. data/app/models/spree_adyen/payment_sources/ebanking_fi.rb +19 -0
  55. data/app/models/spree_adyen/payment_sources/eft_directdebit_ca.rb +19 -0
  56. data/app/models/spree_adyen/payment_sources/eftpos_australia.rb +19 -0
  57. data/app/models/spree_adyen/payment_sources/elo.rb +19 -0
  58. data/app/models/spree_adyen/payment_sources/eps.rb +19 -0
  59. data/app/models/spree_adyen/payment_sources/fastlane.rb +19 -0
  60. data/app/models/spree_adyen/payment_sources/fpx.rb +19 -0
  61. data/app/models/spree_adyen/payment_sources/gcash.rb +19 -0
  62. data/app/models/spree_adyen/payment_sources/gift_cards.rb +19 -0
  63. data/app/models/spree_adyen/payment_sources/giropay.rb +19 -0
  64. data/app/models/spree_adyen/payment_sources/givex.rb +19 -0
  65. data/app/models/spree_adyen/payment_sources/gopay_wallet.rb +19 -0
  66. data/app/models/spree_adyen/payment_sources/grabpay.rb +19 -0
  67. data/app/models/spree_adyen/payment_sources/grabpay_paylater.rb +19 -0
  68. data/app/models/spree_adyen/payment_sources/hipercard.rb +19 -0
  69. data/app/models/spree_adyen/payment_sources/ideal.rb +15 -0
  70. data/app/models/spree_adyen/payment_sources/kakaopay.rb +19 -0
  71. data/app/models/spree_adyen/payment_sources/kcp_naverpay.rb +19 -0
  72. data/app/models/spree_adyen/payment_sources/klarna.rb +32 -0
  73. data/app/models/spree_adyen/payment_sources/oney.rb +13 -0
  74. data/app/models/spree_adyen/payment_sources/online_banking_czech_republic.rb +15 -0
  75. data/app/models/spree_adyen/payment_sources/online_banking_poland.rb +15 -0
  76. data/app/models/spree_adyen/payment_sources/pay_by_bank.rb +15 -0
  77. data/app/models/spree_adyen/payment_sources/paypal.rb +15 -0
  78. data/app/models/spree_adyen/payment_sources/paypo.rb +15 -0
  79. data/app/models/spree_adyen/payment_sources/paysafecard.rb +15 -0
  80. data/app/models/spree_adyen/payment_sources/rate_pay_direct_debit.rb +15 -0
  81. data/app/models/spree_adyen/payment_sources/riverty.rb +15 -0
  82. data/app/models/spree_adyen/payment_sources/samsung_pay.rb +15 -0
  83. data/app/models/spree_adyen/payment_sources/scalapay.rb +13 -0
  84. data/app/models/spree_adyen/payment_sources/sepa_direct_debit.rb +15 -0
  85. data/app/models/spree_adyen/payment_sources/trustly.rb +15 -0
  86. data/app/models/spree_adyen/payment_sources/unknown.rb +19 -0
  87. data/app/models/spree_adyen/payment_sources/wechat_pay.rb +15 -0
  88. data/app/models/spree_adyen/store_decorator.rb +17 -0
  89. data/app/presenters/spree_adyen/address_presenter.rb +21 -0
  90. data/app/presenters/spree_adyen/application_info_presenter.rb +21 -0
  91. data/app/presenters/spree_adyen/cancel_payload_presenter.rb +31 -0
  92. data/app/presenters/spree_adyen/capture_payload_presenter.rb +36 -0
  93. data/app/presenters/spree_adyen/checkout_presenter.rb +51 -0
  94. data/app/presenters/spree_adyen/payment_sessions/request_payload_presenter.rb +140 -0
  95. data/app/presenters/spree_adyen/payments/request_payload_presenter.rb +85 -0
  96. data/app/presenters/spree_adyen/refund_payload_presenter.rb +38 -0
  97. data/app/presenters/spree_adyen/webhook_payload_presenter.rb +28 -0
  98. data/app/presenters/spree_adyen/webhooks/credit_card_presenter.rb +43 -0
  99. data/app/services/spree_adyen/gateways/add_allowed_origin.rb +36 -0
  100. data/app/services/spree_adyen/gateways/configuration.rb +41 -0
  101. data/app/services/spree_adyen/gateways/configure.rb +60 -0
  102. data/app/services/spree_adyen/payment_sessions/find_or_create.rb +51 -0
  103. data/app/services/spree_adyen/payment_sessions/process_with_result.rb +49 -0
  104. data/app/services/spree_adyen/webhooks/actions/create_source.rb +112 -0
  105. data/app/services/spree_adyen/webhooks/actions/find_or_create_credit_card.rb +33 -0
  106. data/app/services/spree_adyen/webhooks/event.rb +104 -0
  107. data/app/services/spree_adyen/webhooks/event_processors/authorisation_event_processor.rb +69 -0
  108. data/app/services/spree_adyen/webhooks/event_processors/cancellation_event_processor.rb +49 -0
  109. data/app/services/spree_adyen/webhooks/event_processors/capture_event_processor.rb +48 -0
  110. data/app/services/spree_adyen/webhooks/handle_event.rb +43 -0
  111. data/app/services/spree_adyen/webhooks/standard_hmac_validator.rb +37 -0
  112. data/app/views/layouts/spree_adyen/default.html.erb +14 -0
  113. data/app/views/spree/admin/payment_methods/configuration_guides/_spree_adyen.html.erb +14 -0
  114. data/app/views/spree/admin/payment_methods/custom_form_fields/_spree_adyen.html.erb +18 -0
  115. data/app/views/spree/admin/payment_methods/descriptions/_spree_adyen.html.erb +12 -0
  116. data/app/views/spree/admin/payments/source_forms/_spree_adyen.html.erb +7 -0
  117. data/app/views/spree/checkout/payment/_spree_adyen.html.erb +40 -0
  118. data/app/views/spree_adyen/_drop_in.html.erb +12 -0
  119. data/app/views/spree_adyen/_head.html.erb +2 -0
  120. data/app/views/spree_adyen/payment_sessions/redirect.html.erb +5 -0
  121. data/config/importmap.rb +7 -0
  122. data/config/initializers/spree.rb +34 -0
  123. data/config/initializers/spree_permitted_attributes.rb +5 -0
  124. data/config/locales/en.yml +11 -0
  125. data/config/routes.rb +35 -0
  126. data/db/migrate/20250630150000_setup_spree_adyen_models.rb +17 -0
  127. data/db/migrate/20250811140113_add_channel_to_adyen_payment_sessions.rb +10 -0
  128. data/db/migrate/20250813152608_add_return_url_to_spree_adyen_payment_sessions.rb +15 -0
  129. data/lib/generators/spree_adyen/install/install_generator.rb +20 -0
  130. data/lib/spree_adyen/configuration.rb +42 -0
  131. data/lib/spree_adyen/engine.rb +79 -0
  132. data/lib/spree_adyen/factories.rb +3 -0
  133. data/lib/spree_adyen/testing_support/factories/gateway_factory.rb +29 -0
  134. data/lib/spree_adyen/testing_support/factories/payment_session_factory.rb +40 -0
  135. data/lib/spree_adyen/version.rb +7 -0
  136. data/lib/spree_adyen.rb +39 -0
  137. data/lib/spree_api_v2/spree/api/v2/storefront/adyen/base_controller.rb +21 -0
  138. data/lib/spree_api_v2/spree/api/v2/storefront/adyen/payment_sessions_controller.rb +72 -0
  139. data/lib/spree_api_v2/spree_adyen/api/v2/storefront/payment_session_serializer.rb +21 -0
  140. data/vendor/javascript/@adyen--adyen-web.js +4 -0
  141. data/vendor/stylesheets/adyen.css +106 -0
  142. metadata +301 -0
@@ -0,0 +1,2 @@
1
+ <%= javascript_import_module_tag 'application-spree-adyen' %>
2
+ <%= stylesheet_link_tag 'adyen.css', media: 'all' %>
@@ -0,0 +1,5 @@
1
+ <div data-controller="redirect-adyen"></div>
2
+
3
+ <%= render 'spree_adyen/drop_in', payment_session: @payment_session, auto_mount: false %>
4
+
5
+ <%= javascript_import_module_tag 'application-spree-adyen' %>
@@ -0,0 +1,7 @@
1
+ pin 'application-spree-adyen', to: 'spree_adyen/application.js', preload: false
2
+
3
+ pin_all_from SpreeAdyen::Engine.root.join('app/javascript/spree_adyen/controllers'),
4
+ under: 'spree_adyen/controllers',
5
+ to: 'spree_adyen/controllers',
6
+ preload: 'application-spree-adyen'
7
+ pin "@adyen/adyen-web/auto", to: "@adyen--adyen-web.js" # @6.18.0
@@ -0,0 +1,34 @@
1
+ # Uncomment lines below to add your own custom business logic
2
+ # such as promotions, shipping methods, etc.
3
+ Rails.application.config.after_initialize do
4
+ # Rails.application.config.spree.shipping_methods << Spree::ShippingMethods::SuperExpensiveNotVeryFastShipping
5
+ Rails.application.config.spree.payment_methods << SpreeAdyen::Gateway
6
+
7
+ # Rails.application.config.spree.calculators.tax_rates << Spree::TaxRates::FinanceTeamForcedMeToCodeThis
8
+
9
+ # Rails.application.config.spree.stock_splitters << Spree::Stock::Splitters::SecretLogicSplitter
10
+
11
+ # Rails.application.config.spree.adjusters << Spree::Adjustable::Adjuster::TaxTheRich
12
+
13
+ # Custom promotions
14
+ # Rails.application.config.spree.calculators.promotion_actions_create_adjustments << Spree::Calculators::PromotionActions::CreateAdjustments::AddDiscountForFriends
15
+ # Rails.application.config.spree.calculators.promotion_actions_create_item_adjustments << Spree::Calculators::PromotionActions::CreateItemAdjustments::FinanceTeamForcedMeToCodeThis
16
+ # Rails.application.config.spree.promotions.rules << Spree::Promotions::Rules::OnlyForVIPCustomers
17
+ # Rails.application.config.spree.promotions.actions << Spree::Promotions::Actions::GiftWithPurchase
18
+
19
+ # Rails.application.config.spree.taxon_rules << Spree::TaxonRules::ProductsWithColor
20
+
21
+ # Rails.application.config.spree.exports << Spree::Exports::Payments
22
+ # Rails.application.config.spree.reports << Spree::Reports::MassivelyOvercomplexReportForCfo
23
+
24
+ # Themes and page builder
25
+ # Rails.application.config.spree.themes << Spree::Themes::NewShinyTheme
26
+ # Rails.application.config.spree.theme_layout_sections << Spree::PageSections::SuperImportantCeoBio
27
+ # Rails.application.config.spree.page_sections << Spree::PageSections::ContactFormToGetInTouch
28
+ # Rails.application.config.spree.page_blocks << Spree::PageBlocks::BigRedButtonToCallSales
29
+
30
+ # Storefront partials
31
+ if Rails.application.config.respond_to?(:spree_storefront)
32
+ Rails.application.config.spree_storefront.head_partials << 'spree_adyen/head'
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ module Spree
2
+ module PermittedAttributes
3
+ @@payment_method_attributes << :apple_developer_merchantid_domain_association
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ ---
2
+ en:
3
+ spree:
4
+ adyen:
5
+ payment_session_errors:
6
+ canceled: Payment canceled
7
+ expired: Payment has been expired. Try again.
8
+ refused: Payment refused
9
+ payment_states:
10
+ capture_pending: Capture Pending
11
+ void_pending: Void Pending
data/config/routes.rb ADDED
@@ -0,0 +1,35 @@
1
+ Spree::Core::Engine.add_routes do
2
+ # Apple Pay domain verification certificate for Apple Pay
3
+ get '/.well-known/apple-developer-merchantid-domain-association' => '/spree_adyen/apple_pay_domain_verification#show'
4
+
5
+ # redirection after redirect flow payment (f.e. klarna)
6
+ # checking the redirect result status in frontend
7
+ get '/adyen/payment_sessions/redirect', to: '/spree_adyen/payment_sessions#redirect',
8
+ as: :redirect_adyen_payment_session,
9
+ controller: '/spree_adyen/payment_sessions'
10
+
11
+ # redirection after non-redirect flow payment for checking payment session result (f.e. credit cards)
12
+ # checking the session result status in frontend
13
+ get '/adyen/payment_sessions', to: '/spree_adyen/payment_sessions#show',
14
+ as: :adyen_payment_session,
15
+ controller: '/spree_adyen/payment_sessions'
16
+
17
+ post '/adyen/webhooks', to: '/spree_adyen/webhooks#create', controller: '/spree_adyen/webhooks'
18
+
19
+ # Storefront API v2 (only available when spree_legacy_api_v2 gem is installed)
20
+ if defined?(SpreeLegacyApiV2::Engine)
21
+ namespace :api, defaults: { format: 'json' } do
22
+ namespace :v2 do
23
+ namespace :storefront do
24
+ namespace :adyen do
25
+ resources :payment_sessions, only: %i[show create] do
26
+ member do
27
+ post :complete
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,17 @@
1
+ class SetupSpreeAdyenModels < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :spree_adyen_payment_sessions do |t|
4
+ t.decimal :amount, precision: 10, scale: 2, default: '0.0', null: false, index: true
5
+ t.string :currency, null: false
6
+ t.references :order, null: false, index: true
7
+ t.references :user, null: true, index: true
8
+ t.string :status, null: false, index: true
9
+ t.datetime :expires_at, null: false, index: true
10
+ t.datetime :deleted_at, index: true
11
+ t.references :payment_method, null: false, index: true
12
+ t.string :adyen_id, null: false, index: { unique: true }
13
+ t.text :adyen_data, null: false
14
+ t.timestamps
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ class AddChannelToAdyenPaymentSessions < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_column :spree_adyen_payment_sessions, :channel, :string
4
+
5
+ SpreeAdyen::PaymentSession.reset_column_information
6
+ SpreeAdyen::PaymentSession.where(channel: nil).update_all(channel: 'Web')
7
+
8
+ add_index :spree_adyen_payment_sessions, :channel
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ class AddReturnUrlToSpreeAdyenPaymentSessions < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_column :spree_adyen_payment_sessions, :return_url, :string
4
+
5
+ SpreeAdyen::PaymentSession.reset_column_information
6
+ Spree::Store.find_each do |store|
7
+ redirect_to = Spree::Core::Engine.routes.url_helpers.redirect_adyen_payment_session_url(host: store.url_or_custom_domain)
8
+ store.payment_methods.adyen.find_each do |gateway|
9
+ store.adyen_gateway.payment_sessions.with_deleted.where(return_url: nil).update_all(return_url: redirect_to)
10
+ end
11
+ end
12
+
13
+ add_index :spree_adyen_payment_sessions, :return_url
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ module SpreeAdyen
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ class_option :migrate, type: :boolean, default: true
5
+
6
+ def add_migrations
7
+ run 'bundle exec rake railties:install:migrations FROM=spree_adyen'
8
+ end
9
+
10
+ def run_migrations
11
+ run_migrations = options[:migrate] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
12
+ if run_migrations
13
+ run 'bin/rails db:migrate'
14
+ else
15
+ puts 'Skipping rails db:migrate, don\'t forget to run it!'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,42 @@
1
+ module SpreeAdyen
2
+ class Configuration < Spree::Preferences::Configuration
3
+ # Some example preferences are shown below, for more information visit:
4
+ # https://docs.spreecommerce.org/developer/contributing/creating-an-extension
5
+
6
+ # preference :enabled, :boolean, default: true
7
+ # preference :dark_chocolate, :boolean, default: true
8
+ # preference :color, :string, default: 'Red'
9
+ # preference :favorite_number, :integer
10
+ # preference :supported_locales, :array, default: [:en]
11
+
12
+ preference :payment_session_expiration_in_minutes, :integer, default: 60
13
+ preference :webhook_delay_in_seconds, :integer, default: 5
14
+ preference :use_legacy_webhook_handlers, :boolean, default: false
15
+ preference :use_legacy_adyen_payment_sessions, :boolean, default: false
16
+
17
+ preference :credit_card_sources, :array, default: %i[
18
+ accel
19
+ accel_googlepay
20
+ amex
21
+ amex_googlepay
22
+ jcb
23
+ carnet
24
+ cartebancaire
25
+ cup
26
+ diners
27
+ discover
28
+ discover_googlepay
29
+ eftpos_australia
30
+ elo
31
+ googlepay
32
+ maestro
33
+ maestro_googlepay
34
+ maestro_usa
35
+ maestro_usa_googlepay
36
+ mc
37
+ mc_googlepay
38
+ visa
39
+ visa_googlepay
40
+ ]
41
+ end
42
+ end
@@ -0,0 +1,79 @@
1
+ module SpreeAdyen
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ isolate_namespace Spree
5
+ engine_name 'spree_adyen'
6
+
7
+ Environment = Struct.new(:event_handlers, :events, :hmac_validators)
8
+
9
+ config.eager_load_paths += %W(#{config.root}/app/services)
10
+
11
+ # Only load API v2 controllers and serializers when spree_legacy_api_v2 gem is available
12
+ if defined?(SpreeLegacyApiV2::Engine)
13
+ config.autoload_paths << root.join('lib', 'spree_api_v2')
14
+ config.eager_load_paths << root.join('lib', 'spree_api_v2')
15
+ end
16
+
17
+ config.generators do |g| # use rspec for tests
18
+ g.test_framework :rspec
19
+ end
20
+
21
+ initializer 'spree_adyen.environment', before: :load_config_initializers do |app|
22
+ app.config.spree_adyen = Environment.new
23
+ app.config.spree_adyen.event_handlers = {}
24
+ app.config.spree_adyen.events = {}
25
+ app.config.spree_adyen.hmac_validators = {}
26
+ SpreeAdyen::Config = SpreeAdyen::Configuration.new
27
+ end
28
+
29
+ config.after_initialize do
30
+ Rails.application.config.spree_adyen.event_handlers.merge!(
31
+ 'AUTHORISATION' => SpreeAdyen::Webhooks::ProcessAuthorisationEventJob,
32
+ 'CAPTURE' => SpreeAdyen::Webhooks::ProcessCaptureEventJob,
33
+ 'CANCELLATION' => SpreeAdyen::Webhooks::ProcessCancellationEventJob
34
+ )
35
+
36
+ Rails.application.config.spree_adyen.events.merge!(
37
+ 'AUTHORISATION' => SpreeAdyen::Webhooks::Event,
38
+ 'CAPTURE' => SpreeAdyen::Webhooks::Event,
39
+ 'CANCELLATION' => SpreeAdyen::Webhooks::Event
40
+ )
41
+
42
+ Rails.application.config.spree_adyen.hmac_validators.merge!(
43
+ 'AUTHORISATION' => SpreeAdyen::Webhooks::StandardHmacValidator,
44
+ 'CAPTURE' => SpreeAdyen::Webhooks::StandardHmacValidator,
45
+ 'CANCELLATION' => SpreeAdyen::Webhooks::StandardHmacValidator
46
+ )
47
+ end
48
+
49
+ initializer 'spree_adyen.assets' do |app|
50
+ if app.config.respond_to?(:assets)
51
+ app.config.assets.paths << root.join('app/javascript')
52
+ app.config.assets.paths << root.join('vendor/javascript')
53
+ app.config.assets.paths << root.join('vendor/stylesheets')
54
+ app.config.assets.precompile += %w[spree_adyen_manifest]
55
+ end
56
+ end
57
+
58
+ initializer 'spree_adyen.importmap', before: 'importmap' do |app|
59
+ if app.config.respond_to?(:importmap)
60
+ app.config.importmap.paths << root.join('config/importmap.rb')
61
+ # https://github.com/rails/importmap-rails?tab=readme-ov-file#sweeping-the-cache-in-development-and-test
62
+ app.config.importmap.cache_sweepers << root.join('app/javascript')
63
+ end
64
+ end
65
+
66
+ def self.activate
67
+ glob_paths = [File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')]
68
+ glob_paths << File.join(File.dirname(__FILE__), '../../lib/spree_api_v2/**/*_decorator*.rb') if defined?(SpreeLegacyApiV2::Engine)
69
+
70
+ glob_paths.each do |glob_path|
71
+ Dir.glob(glob_path) do |c|
72
+ Rails.configuration.cache_classes ? require(c) : load(c)
73
+ end
74
+ end
75
+ end
76
+
77
+ config.to_prepare(&method(:activate).to_proc)
78
+ end
79
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/testing_support/factories/**"].each do |f|
2
+ load File.expand_path(f)
3
+ end
@@ -0,0 +1,29 @@
1
+ FactoryBot.define do
2
+ factory :adyen_gateway, parent: :payment_method, class: SpreeAdyen::Gateway do
3
+ name { 'Adyen' }
4
+ type { 'SpreeAdyen::Gateway' }
5
+
6
+ skip_auto_configuration { true }
7
+ skip_api_key_validation { true }
8
+
9
+ preferences do
10
+ {
11
+ api_key: 'secret',
12
+ merchant_account: 'SpreeCommerceECOM',
13
+ hmac_key: 'secret123',
14
+ client_key: 'client123',
15
+ webhook_id: 'webhook123'
16
+ }
17
+ end
18
+
19
+ trait :with_apple_domain_association_file do
20
+ transient do
21
+ apple_domain_association_file_path do
22
+ File.join(SpreeAdyen::Engine.root, 'spec', 'fixtures', 'files', 'apple-domain-association-file.txt')
23
+ end
24
+ end
25
+
26
+ apple_developer_merchantid_domain_association { Rack::Test::UploadedFile.new(apple_domain_association_file_path) }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,40 @@
1
+ FactoryBot.define do
2
+ factory :adyen_payment_session, class: SpreeAdyen::PaymentSession do
3
+ sequence(:adyen_id) { |n| "pi_#{n}" }
4
+ adyen_data { 'a very long string' }
5
+ amount { order.total_minus_store_credits }
6
+ currency { order.currency }
7
+ channel { 'Web' }
8
+ status { 'initial' }
9
+ expires_at { 1.hour.from_now }
10
+ payment_method { create(:adyen_gateway) }
11
+
12
+ association :user
13
+ association :order, factory: :order_with_line_items
14
+
15
+ trait :expired do
16
+ expires_at { 1.hour.ago }
17
+ skip_expiration_date_validation { true }
18
+ end
19
+
20
+ trait :initial do
21
+ status { 'initial' }
22
+ end
23
+
24
+ trait :pending do
25
+ status { 'pending' }
26
+ end
27
+
28
+ trait :completed do
29
+ status { 'completed' }
30
+ end
31
+
32
+ trait :outdated do
33
+ status { 'outdated' }
34
+ end
35
+
36
+ trait :canceled do
37
+ status { 'canceled' }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ module SpreeAdyen
2
+ VERSION = '0.10.0'.freeze
3
+
4
+ def gem_version
5
+ Gem::Version.new(VERSION)
6
+ end
7
+ end
@@ -0,0 +1,39 @@
1
+ require 'spree_core'
2
+ require 'spree_adyen/engine'
3
+ require 'spree_adyen/version'
4
+ require 'spree_adyen/configuration'
5
+ require 'adyen-ruby-api-library'
6
+
7
+ module SpreeAdyen
8
+ def self.queue
9
+ 'default'
10
+ end
11
+
12
+ def self.version
13
+ VERSION
14
+ end
15
+
16
+ def self.event_handlers
17
+ Rails.application.config.spree_adyen.event_handlers
18
+ end
19
+
20
+ def self.event_handlers=(value)
21
+ Rails.application.config.spree_adyen.event_handlers = value
22
+ end
23
+
24
+ def self.events
25
+ Rails.application.config.spree_adyen.events
26
+ end
27
+
28
+ def self.events=(value)
29
+ Rails.application.config.spree_adyen.events = value
30
+ end
31
+
32
+ def self.hmac_validators
33
+ Rails.application.config.spree_adyen.hmac_validators
34
+ end
35
+
36
+ def self.hmac_validators=(value)
37
+ Rails.application.config.spree_adyen.hmac_validators = value
38
+ end
39
+ end
@@ -0,0 +1,21 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module Adyen
6
+ class BaseController < ::Spree::Api::V2::BaseController
7
+ before_action :require_adyen_gateway
8
+
9
+ def require_adyen_gateway
10
+ render_error_payload('Adyen gateway is not present') unless adyen_gateway
11
+ end
12
+
13
+ def adyen_gateway
14
+ @adyen_gateway ||= current_store.adyen_gateway
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,72 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module Adyen
6
+ class PaymentSessionsController < BaseController
7
+ include Spree::Api::V2::Storefront::OrderConcern
8
+ before_action :ensure_order
9
+ before_action :load_payment_session, only: %i[show complete]
10
+
11
+ # POST /api/v2/storefront/adyen/payment_sessions
12
+ def create
13
+ spree_authorize! :update, spree_current_order, order_token
14
+
15
+ payment_session_result = SpreeAdyen::PaymentSessions::FindOrCreate.new(
16
+ order: spree_current_order,
17
+ amount: permitted_attributes[:amount],
18
+ user: spree_current_user,
19
+ payment_method: adyen_gateway,
20
+ channel: permitted_attributes[:channel],
21
+ return_url: permitted_attributes[:return_url]
22
+ ).call
23
+
24
+ if payment_session_result.success?
25
+ render_serialized_payload { serialize_resource(payment_session_result.value) }
26
+ else
27
+ error = payment_session_result.value&.errors || payment_session_result.error.value
28
+ render_error_payload(error)
29
+ end
30
+ end
31
+
32
+ # POST /api/v2/storefront/adyen/payment_sessions/:id/complete
33
+ def complete
34
+ spree_authorize! :update, spree_current_order, order_token
35
+
36
+ SpreeAdyen::PaymentSessions::ProcessWithResult.new(payment_session: @payment_session, session_result: params[:session_result]).call
37
+
38
+ if @payment_session.completed?
39
+ render_serialized_payload { serialize_resource(@payment_session) }
40
+ else
41
+ render_error_payload("Can't complete the order for the payment session in the #{@payment_session.status} state")
42
+ end
43
+ rescue Spree::Core::GatewayError => e
44
+ render_error_payload(e.message, :unprocessable_entity)
45
+ end
46
+
47
+ # GET /api/v2/storefront/adyen/payment_sessions/:id
48
+ def show
49
+ spree_authorize! :show, spree_current_order, order_token
50
+
51
+ render_serialized_payload { serialize_resource(@payment_session) }
52
+ end
53
+
54
+ private
55
+
56
+ def permitted_attributes
57
+ params.require(:payment_session).permit(:amount, :channel, :return_url)
58
+ end
59
+
60
+ def resource_serializer
61
+ SpreeAdyen::Api::V2::Storefront::PaymentSessionSerializer
62
+ end
63
+
64
+ def load_payment_session
65
+ @payment_session = spree_current_order.adyen_payment_sessions.find(params[:id])
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,21 @@
1
+ module SpreeAdyen
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ class PaymentSessionSerializer < ::Spree::Api::V2::BaseSerializer
6
+ set_type :adyen_payment_session
7
+
8
+ attributes :adyen_id, :amount, :currency, :adyen_data, :status, :expires_at, :channel, :return_url
9
+
10
+ attribute :client_key do |object|
11
+ object.payment_method.preferred_client_key
12
+ end
13
+
14
+ belongs_to :order, serializer: ::Spree::Api::Dependencies.storefront_cart_serializer.constantize
15
+ belongs_to :payment_method, serializer: ::Spree::Api::Dependencies.storefront_payment_method_serializer.constantize
16
+ belongs_to :user, serializer: ::Spree::Api::Dependencies.storefront_user_serializer.constantize
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end