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.
- checksums.yaml +7 -0
- data/LICENSE +9 -0
- data/README.md +59 -0
- data/Rakefile +26 -0
- data/app/assets/config/spree_adyen_manifest.js +5 -0
- data/app/controllers/spree_adyen/apple_pay_domain_verification_controller.rb +11 -0
- data/app/controllers/spree_adyen/payment_sessions_controller.rb +67 -0
- data/app/controllers/spree_adyen/store_controller_decorator.rb +9 -0
- data/app/controllers/spree_adyen/webhooks_controller.rb +46 -0
- data/app/helpers/spree_adyen/base_helper.rb +18 -0
- data/app/javascript/spree_adyen/application.js +17 -0
- data/app/javascript/spree_adyen/controllers/checkout_adyen_controller.js +80 -0
- data/app/javascript/spree_adyen/controllers/redirect_adyen_controller.js +35 -0
- data/app/jobs/spree_adyen/add_allowed_origin_job.rb +24 -0
- data/app/jobs/spree_adyen/base_job.rb +5 -0
- data/app/jobs/spree_adyen/webhooks/process_authorisation_event_job.rb +10 -0
- data/app/jobs/spree_adyen/webhooks/process_cancellation_event_job.rb +10 -0
- data/app/jobs/spree_adyen/webhooks/process_capture_event_job.rb +10 -0
- data/app/models/spree/payment_sessions/adyen.rb +59 -0
- data/app/models/spree_adyen/base.rb +6 -0
- data/app/models/spree_adyen/custom_domain_decorator.rb +17 -0
- data/app/models/spree_adyen/gateway/payment_sessions.rb +151 -0
- data/app/models/spree_adyen/gateway.rb +487 -0
- data/app/models/spree_adyen/order_decorator.rb +23 -0
- data/app/models/spree_adyen/payment_decorator.rb +66 -0
- data/app/models/spree_adyen/payment_method_decorator.rb +15 -0
- data/app/models/spree_adyen/payment_session.rb +121 -0
- data/app/models/spree_adyen/payment_sources/ach_direct_debit.rb +17 -0
- data/app/models/spree_adyen/payment_sources/affirm.rb +17 -0
- data/app/models/spree_adyen/payment_sources/afterpay.rb +17 -0
- data/app/models/spree_adyen/payment_sources/alipay.rb +19 -0
- data/app/models/spree_adyen/payment_sources/alipay_hk.rb +17 -0
- data/app/models/spree_adyen/payment_sources/alma.rb +17 -0
- data/app/models/spree_adyen/payment_sources/ancv.rb +17 -0
- data/app/models/spree_adyen/payment_sources/apple_pay.rb +19 -0
- data/app/models/spree_adyen/payment_sources/atome.rb +17 -0
- data/app/models/spree_adyen/payment_sources/bacs.rb +17 -0
- data/app/models/spree_adyen/payment_sources/bancontact.rb +19 -0
- data/app/models/spree_adyen/payment_sources/bank_transfer.rb +17 -0
- data/app/models/spree_adyen/payment_sources/base.rb +7 -0
- data/app/models/spree_adyen/payment_sources/bcmc.rb +17 -0
- data/app/models/spree_adyen/payment_sources/bcmc_mobile.rb +17 -0
- data/app/models/spree_adyen/payment_sources/benefit.rb +17 -0
- data/app/models/spree_adyen/payment_sources/billie.rb +17 -0
- data/app/models/spree_adyen/payment_sources/bizum.rb +17 -0
- data/app/models/spree_adyen/payment_sources/blik.rb +15 -0
- data/app/models/spree_adyen/payment_sources/boleto.rb +17 -0
- data/app/models/spree_adyen/payment_sources/cash_app_afterpay.rb +17 -0
- data/app/models/spree_adyen/payment_sources/cashapp.rb +17 -0
- data/app/models/spree_adyen/payment_sources/clearpay.rb +17 -0
- data/app/models/spree_adyen/payment_sources/dana.rb +17 -0
- data/app/models/spree_adyen/payment_sources/doku.rb +19 -0
- data/app/models/spree_adyen/payment_sources/duitnow.rb +17 -0
- data/app/models/spree_adyen/payment_sources/ebanking_fi.rb +19 -0
- data/app/models/spree_adyen/payment_sources/eft_directdebit_ca.rb +19 -0
- data/app/models/spree_adyen/payment_sources/eftpos_australia.rb +19 -0
- data/app/models/spree_adyen/payment_sources/elo.rb +19 -0
- data/app/models/spree_adyen/payment_sources/eps.rb +19 -0
- data/app/models/spree_adyen/payment_sources/fastlane.rb +19 -0
- data/app/models/spree_adyen/payment_sources/fpx.rb +19 -0
- data/app/models/spree_adyen/payment_sources/gcash.rb +19 -0
- data/app/models/spree_adyen/payment_sources/gift_cards.rb +19 -0
- data/app/models/spree_adyen/payment_sources/giropay.rb +19 -0
- data/app/models/spree_adyen/payment_sources/givex.rb +19 -0
- data/app/models/spree_adyen/payment_sources/gopay_wallet.rb +19 -0
- data/app/models/spree_adyen/payment_sources/grabpay.rb +19 -0
- data/app/models/spree_adyen/payment_sources/grabpay_paylater.rb +19 -0
- data/app/models/spree_adyen/payment_sources/hipercard.rb +19 -0
- data/app/models/spree_adyen/payment_sources/ideal.rb +15 -0
- data/app/models/spree_adyen/payment_sources/kakaopay.rb +19 -0
- data/app/models/spree_adyen/payment_sources/kcp_naverpay.rb +19 -0
- data/app/models/spree_adyen/payment_sources/klarna.rb +32 -0
- data/app/models/spree_adyen/payment_sources/oney.rb +13 -0
- data/app/models/spree_adyen/payment_sources/online_banking_czech_republic.rb +15 -0
- data/app/models/spree_adyen/payment_sources/online_banking_poland.rb +15 -0
- data/app/models/spree_adyen/payment_sources/pay_by_bank.rb +15 -0
- data/app/models/spree_adyen/payment_sources/paypal.rb +15 -0
- data/app/models/spree_adyen/payment_sources/paypo.rb +15 -0
- data/app/models/spree_adyen/payment_sources/paysafecard.rb +15 -0
- data/app/models/spree_adyen/payment_sources/rate_pay_direct_debit.rb +15 -0
- data/app/models/spree_adyen/payment_sources/riverty.rb +15 -0
- data/app/models/spree_adyen/payment_sources/samsung_pay.rb +15 -0
- data/app/models/spree_adyen/payment_sources/scalapay.rb +13 -0
- data/app/models/spree_adyen/payment_sources/sepa_direct_debit.rb +15 -0
- data/app/models/spree_adyen/payment_sources/trustly.rb +15 -0
- data/app/models/spree_adyen/payment_sources/unknown.rb +19 -0
- data/app/models/spree_adyen/payment_sources/wechat_pay.rb +15 -0
- data/app/models/spree_adyen/store_decorator.rb +17 -0
- data/app/presenters/spree_adyen/address_presenter.rb +21 -0
- data/app/presenters/spree_adyen/application_info_presenter.rb +21 -0
- data/app/presenters/spree_adyen/cancel_payload_presenter.rb +31 -0
- data/app/presenters/spree_adyen/capture_payload_presenter.rb +36 -0
- data/app/presenters/spree_adyen/checkout_presenter.rb +51 -0
- data/app/presenters/spree_adyen/payment_sessions/request_payload_presenter.rb +140 -0
- data/app/presenters/spree_adyen/payments/request_payload_presenter.rb +85 -0
- data/app/presenters/spree_adyen/refund_payload_presenter.rb +38 -0
- data/app/presenters/spree_adyen/webhook_payload_presenter.rb +28 -0
- data/app/presenters/spree_adyen/webhooks/credit_card_presenter.rb +43 -0
- data/app/services/spree_adyen/gateways/add_allowed_origin.rb +36 -0
- data/app/services/spree_adyen/gateways/configuration.rb +41 -0
- data/app/services/spree_adyen/gateways/configure.rb +60 -0
- data/app/services/spree_adyen/payment_sessions/find_or_create.rb +51 -0
- data/app/services/spree_adyen/payment_sessions/process_with_result.rb +49 -0
- data/app/services/spree_adyen/webhooks/actions/create_source.rb +112 -0
- data/app/services/spree_adyen/webhooks/actions/find_or_create_credit_card.rb +33 -0
- data/app/services/spree_adyen/webhooks/event.rb +104 -0
- data/app/services/spree_adyen/webhooks/event_processors/authorisation_event_processor.rb +69 -0
- data/app/services/spree_adyen/webhooks/event_processors/cancellation_event_processor.rb +49 -0
- data/app/services/spree_adyen/webhooks/event_processors/capture_event_processor.rb +48 -0
- data/app/services/spree_adyen/webhooks/handle_event.rb +43 -0
- data/app/services/spree_adyen/webhooks/standard_hmac_validator.rb +37 -0
- data/app/views/layouts/spree_adyen/default.html.erb +14 -0
- data/app/views/spree/admin/payment_methods/configuration_guides/_spree_adyen.html.erb +14 -0
- data/app/views/spree/admin/payment_methods/custom_form_fields/_spree_adyen.html.erb +18 -0
- data/app/views/spree/admin/payment_methods/descriptions/_spree_adyen.html.erb +12 -0
- data/app/views/spree/admin/payments/source_forms/_spree_adyen.html.erb +7 -0
- data/app/views/spree/checkout/payment/_spree_adyen.html.erb +40 -0
- data/app/views/spree_adyen/_drop_in.html.erb +12 -0
- data/app/views/spree_adyen/_head.html.erb +2 -0
- data/app/views/spree_adyen/payment_sessions/redirect.html.erb +5 -0
- data/config/importmap.rb +7 -0
- data/config/initializers/spree.rb +34 -0
- data/config/initializers/spree_permitted_attributes.rb +5 -0
- data/config/locales/en.yml +11 -0
- data/config/routes.rb +35 -0
- data/db/migrate/20250630150000_setup_spree_adyen_models.rb +17 -0
- data/db/migrate/20250811140113_add_channel_to_adyen_payment_sessions.rb +10 -0
- data/db/migrate/20250813152608_add_return_url_to_spree_adyen_payment_sessions.rb +15 -0
- data/lib/generators/spree_adyen/install/install_generator.rb +20 -0
- data/lib/spree_adyen/configuration.rb +42 -0
- data/lib/spree_adyen/engine.rb +79 -0
- data/lib/spree_adyen/factories.rb +3 -0
- data/lib/spree_adyen/testing_support/factories/gateway_factory.rb +29 -0
- data/lib/spree_adyen/testing_support/factories/payment_session_factory.rb +40 -0
- data/lib/spree_adyen/version.rb +7 -0
- data/lib/spree_adyen.rb +39 -0
- data/lib/spree_api_v2/spree/api/v2/storefront/adyen/base_controller.rb +21 -0
- data/lib/spree_api_v2/spree/api/v2/storefront/adyen/payment_sessions_controller.rb +72 -0
- data/lib/spree_api_v2/spree_adyen/api/v2/storefront/payment_session_serializer.rb +21 -0
- data/vendor/javascript/@adyen--adyen-web.js +4 -0
- data/vendor/stylesheets/adyen.css +106 -0
- metadata +301 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: efdb74fe9f9d7848da506be961b930c3b2352d6726a1e5fdf81a160ee1688fc6
|
|
4
|
+
data.tar.gz: 997de8336c6d59668a1830c576d26778600bf20dcdf79c4307a64b22ee4ab69f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9d78966a7463a76cf3dd572d50b09845928be5e6842c30f79cb8139f1c47489cef675c8689ee5b9aaf21a7ec79026d80a081ca8f20e1204ab65f274c5f5f8174
|
|
7
|
+
data.tar.gz: 90c1367fa54b80f8194252cfe03e19157d38933a6ed64cf6d18e00467ad43c643cf97b9974626bdf814ac250174e8dec89e96088df70317d03b4f287dc23b765
|
data/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vendo Connect Inc., Vendo Sp. z o.o.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Spree Adyen
|
|
2
|
+
|
|
3
|
+
Official Adyen payments platform integration for [Spree Commerce](https://spreecommerce.org).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
1. Add this extension to your Gemfile with this line:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
bundle add spree_adyen
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
2. Run the install generator
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
bundle exec rails g spree_adyen:install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
3. Restart your server
|
|
20
|
+
|
|
21
|
+
If your server was running, restart it so that it can find the assets properly.
|
|
22
|
+
|
|
23
|
+
## Developing
|
|
24
|
+
|
|
25
|
+
1. Create a dummy app
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bundle update
|
|
29
|
+
bundle exec rake test_app
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
2. Add your new code
|
|
33
|
+
3. Run tests
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bundle exec rspec
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
When testing your applications integration with this extension you may use it's factories.
|
|
40
|
+
Simply add this require statement to your spec_helper:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
require 'spree_adyen/factories'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Releasing a new version
|
|
47
|
+
|
|
48
|
+
```shell
|
|
49
|
+
bundle exec gem bump -p -t
|
|
50
|
+
bundle exec gem release
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For more options please see [gem-release README](https://github.com/svenfuchs/gem-release)
|
|
54
|
+
|
|
55
|
+
## Contributing
|
|
56
|
+
|
|
57
|
+
If you'd like to contribute, please take a look at the
|
|
58
|
+
[instructions](CONTRIBUTING.md) for installing dependencies and crafting a good
|
|
59
|
+
pull request.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'bundler'
|
|
2
|
+
Bundler::GemHelper.install_tasks
|
|
3
|
+
|
|
4
|
+
require 'rspec/core/rake_task'
|
|
5
|
+
require 'spree/testing_support/extension_rake'
|
|
6
|
+
|
|
7
|
+
RSpec::Core::RakeTask.new
|
|
8
|
+
|
|
9
|
+
task :default do
|
|
10
|
+
if Dir['spec/dummy'].empty?
|
|
11
|
+
Rake::Task[:test_app].invoke
|
|
12
|
+
Dir.chdir('../../')
|
|
13
|
+
end
|
|
14
|
+
Rake::Task[:spec].invoke
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc 'Generates a dummy app for testing'
|
|
18
|
+
task :test_app do
|
|
19
|
+
ENV['LIB_NAME'] = 'spree_adyen'
|
|
20
|
+
Rake::Task['extension:test_app'].execute(
|
|
21
|
+
install_admin: true
|
|
22
|
+
)
|
|
23
|
+
system({ 'BUNDLE_GEMFILE' => File.expand_path('Gemfile', __dir__) }, 'rails g spree_posts:install')
|
|
24
|
+
system({ 'BUNDLE_GEMFILE' => File.expand_path('Gemfile', __dir__) }, 'rails g spree_legacy_product_properties:install')
|
|
25
|
+
system({ 'BUNDLE_GEMFILE' => File.expand_path('Gemfile', __dir__) }, 'rails g spree_legacy_api_v2:install')
|
|
26
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module SpreeAdyen
|
|
2
|
+
class ApplePayDomainVerificationController < ::Spree::BaseController
|
|
3
|
+
def show
|
|
4
|
+
gateway = current_store.adyen_gateway
|
|
5
|
+
|
|
6
|
+
raise ActiveRecord::RecordNotFound if gateway.nil? || !gateway.apple_developer_merchantid_domain_association.attached?
|
|
7
|
+
|
|
8
|
+
render plain: gateway.apple_domain_association_file_content
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# this is the endpoint that Adyen JS SDK will redirect customer to after payment
|
|
2
|
+
# it will handle the payment session status and process the payment
|
|
3
|
+
|
|
4
|
+
module SpreeAdyen
|
|
5
|
+
class PaymentSessionsController < defined?(Spree::StoreController) ? Spree::StoreController : Spree::BaseController
|
|
6
|
+
include Spree::CheckoutAnalyticsHelper if defined?(Spree::CheckoutAnalyticsHelper)
|
|
7
|
+
|
|
8
|
+
# GET /adyen/payment_sessions/redirect
|
|
9
|
+
def redirect
|
|
10
|
+
@payment_session = SpreeAdyen::PaymentSession.find_by(adyen_id: params[:sessionId])
|
|
11
|
+
render layout: 'spree_adyen/default'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# GET /adyen/payment_sessions
|
|
15
|
+
def show
|
|
16
|
+
@payment_session = SpreeAdyen::PaymentSession.find_by!(adyen_id: params[:sessionId])
|
|
17
|
+
@order = @payment_session.order
|
|
18
|
+
# handle duplicated requests or already processed through webhook
|
|
19
|
+
if @payment_session.canceled? || @order.canceled?
|
|
20
|
+
redirect_to spree.cart_path, status: :see_other
|
|
21
|
+
return
|
|
22
|
+
elsif @order.completed?
|
|
23
|
+
redirect_to spree.checkout_complete_path(@order.token), status: :see_other
|
|
24
|
+
return
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
SpreeAdyen::PaymentSessions::ProcessWithResult.new(payment_session: @payment_session, session_result: params[:sessionResult]).call
|
|
28
|
+
|
|
29
|
+
if @payment_session.completed?
|
|
30
|
+
handle_success
|
|
31
|
+
elsif @payment_session.pending?
|
|
32
|
+
handle_pending_payment
|
|
33
|
+
elsif @payment_session.canceled?
|
|
34
|
+
redirect_to spree.checkout_path(@order.token), status: :see_other
|
|
35
|
+
elsif @payment_session.refused?
|
|
36
|
+
handle_failure
|
|
37
|
+
end
|
|
38
|
+
rescue Spree::Core::GatewayError => e
|
|
39
|
+
handle_failure(e.message)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# TODO: handle pending payment
|
|
45
|
+
def handle_pending_payment; end
|
|
46
|
+
|
|
47
|
+
def handle_success
|
|
48
|
+
# update the payment session status
|
|
49
|
+
|
|
50
|
+
# set the session flag to indicate that the order was placed now
|
|
51
|
+
track_checkout_completed if @order.completed? && defined?(track_checkout_completed)
|
|
52
|
+
|
|
53
|
+
redirect_to spree.checkout_complete_path(@order.token), status: :see_other
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def handle_failure(message = nil)
|
|
57
|
+
flash[:error] = message || Spree.t("adyen.payment_session_errors.#{@payment_session.status}")
|
|
58
|
+
|
|
59
|
+
Rails.logger.error("Payment failed for order #{@order.id}: #{@payment_session.status}")
|
|
60
|
+
|
|
61
|
+
# this should be a rare race condition, but we need to handle it
|
|
62
|
+
@order.payments.valid.find_by(response_code: @payment_session.id)&.void!
|
|
63
|
+
|
|
64
|
+
redirect_to spree.checkout_path(@order.token), status: :see_other
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module SpreeAdyen
|
|
2
|
+
class WebhooksController < ActionController::API
|
|
3
|
+
include Spree::Core::ControllerHelpers::Store
|
|
4
|
+
|
|
5
|
+
before_action :validate_hmac!
|
|
6
|
+
|
|
7
|
+
def create
|
|
8
|
+
SpreeAdyen::Webhooks::HandleEvent.new(event_payload: webhook_params).call
|
|
9
|
+
|
|
10
|
+
head :ok
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def validate_hmac!
|
|
16
|
+
if hmac_validator_class.nil?
|
|
17
|
+
Rails.logger.info("[SpreeAdyen][#{event_code}]: Skipping not supported event")
|
|
18
|
+
head :ok
|
|
19
|
+
return
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
validator = hmac_validator_class.new(
|
|
23
|
+
request: request,
|
|
24
|
+
params: webhook_params,
|
|
25
|
+
gateway: current_store.adyen_gateway
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
return if validator.call
|
|
29
|
+
|
|
30
|
+
Rails.logger.error("[SpreeAdyen]: Failed to validate hmac for #{event_code}")
|
|
31
|
+
head :unauthorized
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def hmac_validator_class
|
|
35
|
+
SpreeAdyen.hmac_validators[event_code]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def event_code
|
|
39
|
+
webhook_params.dig('notificationItems', 0, 'NotificationRequestItem', 'eventCode') || webhook_params['type']
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def webhook_params
|
|
43
|
+
@webhook_params ||= JSON.parse(request.raw_post).with_indifferent_access
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module SpreeAdyen
|
|
2
|
+
module BaseHelper
|
|
3
|
+
def current_adyen_gateway
|
|
4
|
+
@current_adyen_gateway ||= current_store.adyen_gateway
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def current_adyen_payment_session
|
|
8
|
+
return if current_adyen_gateway.nil?
|
|
9
|
+
|
|
10
|
+
@current_adyen_payment_session ||= SpreeAdyen::PaymentSessions::FindOrCreate.new(
|
|
11
|
+
order: @order,
|
|
12
|
+
amount: @order.total_minus_store_credits,
|
|
13
|
+
user: @order.user,
|
|
14
|
+
payment_method: current_adyen_gateway
|
|
15
|
+
).call&.value
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import '@hotwired/turbo-rails'
|
|
2
|
+
|
|
3
|
+
let application
|
|
4
|
+
|
|
5
|
+
if (typeof window.Stimulus === "undefined") {
|
|
6
|
+
application = Application.start()
|
|
7
|
+
application.debug = false
|
|
8
|
+
window.Stimulus = application
|
|
9
|
+
} else {
|
|
10
|
+
application = window.Stimulus
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
import CheckoutAdyenController from 'spree_adyen/controllers/checkout_adyen_controller'
|
|
14
|
+
import RedirectAdyenController from 'spree_adyen/controllers/redirect_adyen_controller'
|
|
15
|
+
|
|
16
|
+
application.register('checkout-adyen', CheckoutAdyenController);
|
|
17
|
+
application.register('redirect-adyen', RedirectAdyenController);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
|
2
|
+
|
|
3
|
+
import { AdyenCheckout, Dropin } from '@adyen/adyen-web/auto'
|
|
4
|
+
|
|
5
|
+
export default class extends Controller {
|
|
6
|
+
async connect() {
|
|
7
|
+
await this.initCheckout();
|
|
8
|
+
this.initEventHandlers();
|
|
9
|
+
if (this.dropinElement().dataset.checkoutAdyenAutoMountValue == 'true') {
|
|
10
|
+
this.initDropin();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
dropinElement() {
|
|
15
|
+
return document.getElementById('dropin-container');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async initCheckout() {
|
|
19
|
+
const session = JSON.parse(this.dropinElement().dataset.checkoutAdyenCheckoutAttrubutes)
|
|
20
|
+
const eventHandlers = {
|
|
21
|
+
onPaymentCompleted: (result, _component) => {
|
|
22
|
+
// idk why 3ds are handled here instead of paymentMethodsConfiguration.card.onPaymentCompleted
|
|
23
|
+
this.redirectToPaymentSession(result);
|
|
24
|
+
},
|
|
25
|
+
onError: (error, component) => {
|
|
26
|
+
console.error(error.name, error.message, error.stack, component);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const adyenCheckout = await AdyenCheckout(Object.assign(session, eventHandlers));;
|
|
30
|
+
this.adyenCheckout = adyenCheckout;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
redirectToPaymentSession(result) {
|
|
34
|
+
const url = new URL(this.dropinElement().dataset.checkoutAdyenPaymentSessionPathValue);
|
|
35
|
+
url.searchParams.set('sessionResult', result.sessionResult);
|
|
36
|
+
window.location.replace(url.href);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async initDropin() {
|
|
40
|
+
const dropinConfiguration = {
|
|
41
|
+
showStoredPaymentMethods: false,
|
|
42
|
+
paymentMethodsConfiguration: {
|
|
43
|
+
card: {
|
|
44
|
+
onPaymentCompleted: (result, component) => {
|
|
45
|
+
this.redirectToPaymentSession(result);
|
|
46
|
+
},
|
|
47
|
+
onError: (error, component) => {
|
|
48
|
+
console.error(error.name, error.message, error.stack, component);
|
|
49
|
+
},
|
|
50
|
+
showPayButton: false,
|
|
51
|
+
hasHolderName: true,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const dropin = new Dropin(this.adyenCheckout, dropinConfiguration)
|
|
56
|
+
dropin.mount('#dropin-container');
|
|
57
|
+
this.dropin = dropin;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
initEventHandlers() {
|
|
61
|
+
document.getElementById('checkout-payment-submit').addEventListener('click', (e) => {
|
|
62
|
+
if (this.dropinElement().classList.contains('hidden')) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
this.dropin.submit();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
document.querySelectorAll('#existing_cards input[type="radio"]').forEach(elem => elem.addEventListener("change", (e) => {
|
|
70
|
+
if (e.target.dataset.show == 'dropin-container') {
|
|
71
|
+
this.initDropin();
|
|
72
|
+
this.dropinElement().classList.remove('hidden');
|
|
73
|
+
}
|
|
74
|
+
else if (this.dropin) {
|
|
75
|
+
this.dropin.unmount();
|
|
76
|
+
this.dropinElement().classList.add('hidden');
|
|
77
|
+
}
|
|
78
|
+
}))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
|
2
|
+
import { AdyenCheckout } from '@adyen/adyen-web/auto'
|
|
3
|
+
export default class extends Controller {
|
|
4
|
+
async connect() {
|
|
5
|
+
await this.initCheckout();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
redirectElement() {
|
|
9
|
+
return document.getElementById('dropin-container');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async initCheckout() {
|
|
13
|
+
const session = JSON.parse(this.redirectElement().dataset.checkoutAdyenCheckoutAttrubutes)
|
|
14
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
15
|
+
const redirectResult = urlParams.get('redirectResult');
|
|
16
|
+
|
|
17
|
+
const eventHandlers = {
|
|
18
|
+
onPaymentCompleted: (result, component) => {
|
|
19
|
+
this.redirectToPaymentSession(result);
|
|
20
|
+
},
|
|
21
|
+
onPaymentFailed: (result, component) => {
|
|
22
|
+
this.redirectToPaymentSession(result);
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
const configuration = Object.assign(session, eventHandlers);
|
|
26
|
+
const adyenCheckout = await AdyenCheckout(configuration);
|
|
27
|
+
adyenCheckout.submitDetails({ details: { redirectResult: redirectResult } });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
redirectToPaymentSession(result) {
|
|
31
|
+
const url = new URL(this.redirectElement().dataset.checkoutAdyenPaymentSessionPathValue);
|
|
32
|
+
url.searchParams.set('sessionResult', result.sessionResult);
|
|
33
|
+
window.location.replace(url.href);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module SpreeAdyen
|
|
2
|
+
class AddAllowedOriginJob < SpreeAdyen::BaseJob
|
|
3
|
+
def perform(record_id, gateway_id, klass_type = 'store')
|
|
4
|
+
@klass_type = klass_type.to_s
|
|
5
|
+
return unless klass
|
|
6
|
+
|
|
7
|
+
record = klass.find(record_id)
|
|
8
|
+
gateway = SpreeAdyen::Gateway.find(gateway_id)
|
|
9
|
+
|
|
10
|
+
SpreeAdyen::Gateways::AddAllowedOrigin.new(record, gateway).call
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def klass
|
|
16
|
+
@klass ||= case @klass_type
|
|
17
|
+
when 'store' then Spree::Store
|
|
18
|
+
when 'custom_domain' then defined?(Spree::CustomDomain) ? Spree::CustomDomain : nil
|
|
19
|
+
else
|
|
20
|
+
Rails.error.unexpected("Unexpected klass_type: #{@klass_type}")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeAdyen
|
|
2
|
+
module Webhooks
|
|
3
|
+
class ProcessAuthorisationEventJob < SpreeAdyen::BaseJob
|
|
4
|
+
def perform(payload)
|
|
5
|
+
event = SpreeAdyen::Webhooks::Event.new(event_data: payload)
|
|
6
|
+
SpreeAdyen::Webhooks::EventProcessors::AuthorisationEventProcessor.new(event).call
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeAdyen
|
|
2
|
+
module Webhooks
|
|
3
|
+
class ProcessCancellationEventJob < SpreeAdyen::BaseJob
|
|
4
|
+
def perform(payload)
|
|
5
|
+
event = SpreeAdyen::Webhooks::Event.new(event_data: payload)
|
|
6
|
+
SpreeAdyen::Webhooks::EventProcessors::CancellationEventProcessor.new(event).call
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module SpreeAdyen
|
|
2
|
+
module Webhooks
|
|
3
|
+
class ProcessCaptureEventJob < SpreeAdyen::BaseJob
|
|
4
|
+
def perform(payload)
|
|
5
|
+
event = SpreeAdyen::Webhooks::Event.new(event_data: payload)
|
|
6
|
+
SpreeAdyen::Webhooks::EventProcessors::CaptureEventProcessor.new(event).call
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class PaymentSessions::Adyen < PaymentSession
|
|
3
|
+
AVAILABLE_CHANNELS = {
|
|
4
|
+
ios: 'iOS',
|
|
5
|
+
android: 'Android',
|
|
6
|
+
web: 'Web'
|
|
7
|
+
}.freeze
|
|
8
|
+
|
|
9
|
+
# Adyen-specific accessors from external_data
|
|
10
|
+
def adyen_id
|
|
11
|
+
external_id
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def session_data
|
|
15
|
+
external_data&.dig('session_data')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def client_key
|
|
19
|
+
payment_method.preferred_client_key
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def channel
|
|
23
|
+
external_data&.dig('channel') || AVAILABLE_CHANNELS[:web]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def return_url
|
|
27
|
+
external_data&.dig('return_url')
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def accepted?
|
|
31
|
+
status == 'completed'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def successful?
|
|
35
|
+
status == 'completed'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def find_or_create_payment!(metadata = {})
|
|
39
|
+
return unless persisted?
|
|
40
|
+
return payment if payment.present?
|
|
41
|
+
|
|
42
|
+
order.with_lock do
|
|
43
|
+
existing_payment = order.payments.where(
|
|
44
|
+
payment_method: payment_method,
|
|
45
|
+
response_code: external_id
|
|
46
|
+
).first
|
|
47
|
+
|
|
48
|
+
return existing_payment if existing_payment.present?
|
|
49
|
+
|
|
50
|
+
order.payments.create!(
|
|
51
|
+
payment_method: payment_method,
|
|
52
|
+
amount: amount,
|
|
53
|
+
response_code: external_id,
|
|
54
|
+
skip_source_requirement: true
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module SpreeAdyen
|
|
2
|
+
module CustomDomainDecorator
|
|
3
|
+
def self.prepended(base)
|
|
4
|
+
base.after_commit :add_adyen_allowed_origin
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def add_adyen_allowed_origin
|
|
8
|
+
return if store.adyen_gateway.blank?
|
|
9
|
+
|
|
10
|
+
SpreeAdyen::AddAllowedOriginJob.perform_later(id, store.adyen_gateway.id, 'custom_domain')
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
if defined?(Spree::CustomDomain)
|
|
16
|
+
Spree::CustomDomain.prepend(SpreeAdyen::CustomDomainDecorator)
|
|
17
|
+
end
|