spree_emerchantpay_genesis 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +52 -0
  3. data/LICENSE +21 -0
  4. data/README.md +269 -0
  5. data/Rakefile +50 -0
  6. data/app/assets/images/spree/emerchantpay_logo.png +0 -0
  7. data/app/assets/javascripts/spree/emerchantpay_threeds.js +126 -0
  8. data/app/assets/javascripts/spree/frontend/card.min.js +3 -0
  9. data/app/assets/stylesheets/spree/emerchantpay_threeds.css +40 -0
  10. data/app/assets/stylesheets/spree/frontend/card.css +35 -0
  11. data/app/controllers/spree/api/v2/storefront/checkout_controller_decorator.rb +66 -0
  12. data/app/controllers/spree/api/v2/storefront/emerchantpay_notification_controller.rb +47 -0
  13. data/app/controllers/spree/api/v2/storefront/emerchantpay_threeds_controller.rb +61 -0
  14. data/app/controllers/spree/emerchantpay_threeds_controller.rb +31 -0
  15. data/app/helpers/spree/admin/payment_methods_helper.rb +110 -0
  16. data/app/helpers/spree_emerchantpay_genesis/mappers/genesis.rb +332 -0
  17. data/app/helpers/spree_emerchantpay_genesis/mappers/order.rb +70 -0
  18. data/app/helpers/spree_emerchantpay_genesis/mappers/transaction.rb +45 -0
  19. data/app/helpers/spree_emerchantpay_genesis/threeds_helper.rb +116 -0
  20. data/app/helpers/spree_emerchantpay_genesis/transaction_helper.rb +200 -0
  21. data/app/models/spree/gateway/emerchantpay_direct.rb +66 -0
  22. data/app/models/spree/payment_decorator.rb +13 -0
  23. data/app/models/spree/payment_processing_decorator.rb +28 -0
  24. data/app/models/spree_emerchantpay_genesis/base/data.rb +33 -0
  25. data/app/models/spree_emerchantpay_genesis/base/gateway.rb +65 -0
  26. data/app/models/spree_emerchantpay_genesis/data/address.rb +23 -0
  27. data/app/models/spree_emerchantpay_genesis/data/provider.rb +23 -0
  28. data/app/models/spree_emerchantpay_genesis/data/user.rb +32 -0
  29. data/app/models/spree_emerchantpay_genesis/db/application_record.rb +10 -0
  30. data/app/models/spree_emerchantpay_genesis/db/emerchantpay_payment.rb +35 -0
  31. data/app/models/spree_emerchantpay_genesis/genesis_provider.rb +203 -0
  32. data/app/repositories/spree_emerchantpay_genesis/emerchantpay_payments_repository.rb +120 -0
  33. data/app/repositories/spree_emerchantpay_genesis/spree_order_repository.rb +51 -0
  34. data/app/repositories/spree_emerchantpay_genesis/spree_payments_repository.rb +58 -0
  35. data/app/services/spree/payments/create_decorator.rb +63 -0
  36. data/app/services/spree_emerchantpay_genesis/base/payment_service.rb +83 -0
  37. data/app/services/spree_emerchantpay_genesis/notifications/service_handler.rb +33 -0
  38. data/app/services/spree_emerchantpay_genesis/sources/create_credit_card.rb +47 -0
  39. data/app/services/spree_emerchantpay_genesis/threeds/callback.rb +62 -0
  40. data/app/services/spree_emerchantpay_genesis/threeds/method_continue.rb +57 -0
  41. data/app/views/layouts/spree/method_continue.html.erb +17 -0
  42. data/app/views/spree/admin/payments/source_views/_emerchantpay_direct.html.erb +80 -0
  43. data/app/views/spree/checkout/payment/_emerchantpay_direct.html.erb +51 -0
  44. data/app/views/spree/emerchantpay_threeds/method_continue.html.erb +26 -0
  45. data/config/locales/en.yml +45 -0
  46. data/config/routes.rb +24 -0
  47. data/db/migrate/20230927072418_add_emerchantpay_payments.rb +34 -0
  48. data/db/migrate/20231128153140_add_callback_status_to_emerchantpay_payments.rb +8 -0
  49. data/lib/generators/spree_emerchantpay_genesis/install/install_generator.rb +37 -0
  50. data/lib/spree_emerchantpay_genesis/engine.rb +38 -0
  51. data/lib/spree_emerchantpay_genesis/factories.rb +11 -0
  52. data/lib/spree_emerchantpay_genesis/version.rb +7 -0
  53. data/lib/spree_emerchantpay_genesis.rb +8 -0
  54. metadata +421 -0
@@ -0,0 +1,40 @@
1
+ body {
2
+ padding-top: 50px;
3
+ }
4
+ .center {
5
+ max-width: 819px;
6
+ height: 423px;
7
+ background-color: #ffffff;
8
+ border-radius: 10px;
9
+ color: #000000;
10
+ margin: 0 auto;
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
14
+ }
15
+ .center h3 {
16
+ font-family: 'Segoe UI', Arial, sans-serif;
17
+ text-align: center;
18
+ font-weight: 500;
19
+ font-size: 2em;
20
+ }
21
+ .center h3 span {
22
+ font-family: 'Segoe UI', Arial, sans-serif;
23
+ display: block;
24
+ text-align: center;
25
+ font-weight: 400;
26
+ font-size: 16px;
27
+ padding-top: 30px;
28
+ }
29
+ .hidden {
30
+ display: none;
31
+ }
32
+ .screen-logo
33
+ {
34
+ width: 152px;
35
+ height: 50px;
36
+ margin: 0 auto;
37
+ display: flex;
38
+ justify-content: center;
39
+ align-items: center;
40
+ }
@@ -0,0 +1,35 @@
1
+ /*
2
+ * Copyright (C) 2023 emerchantpay Ltd.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License
6
+ * as published by the Free Software Foundation; either version 2
7
+ * of the License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ * GNU General Public License for more details.
13
+ *
14
+ * @author emerchantpay
15
+ * @copyright 2023 emerchantpay Ltd.
16
+ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License, version 2 (GPL-2.0)
17
+ */
18
+
19
+ .emerchantpay-card-wrapper {
20
+ text-align: center;
21
+ }
22
+
23
+ .emerchantpay-payment-gateway .payment-gateway-fields {
24
+ text-align: center;
25
+ width: 80%;
26
+ margin: 0px auto;
27
+ }
28
+
29
+ .emerchantpay-payment-gateway .payment-gateway-fields input {
30
+ padding: 15px;
31
+ }
32
+
33
+ .emerchantpay-payment-gateway .payment-gateway-fields input::-webkit-input-placeholder {
34
+ text-transform: none;
35
+ }
@@ -0,0 +1,66 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ # Decorate Create Payment action
6
+ module CheckoutControllerDecorator
7
+
8
+ def create_payment
9
+ result = create_payment_service.call(order: spree_current_order, params: params)
10
+
11
+ if result.success?
12
+ return emerchantpay_direct_payment_handler if
13
+ result.value.payment_method.type == Spree::Gateway::EmerchantpayDirect.name
14
+
15
+ render_serialized_payload(201) { serialize_resource(spree_current_order.reload) }
16
+ else
17
+ render_error_payload(result.error)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ # Handle EmerchantpayDirect Payment Method Create Payment API Call
24
+ def emerchantpay_direct_payment_handler
25
+ return render_error_payload('You must authenticate in order to create Emerchanptpay payment') if
26
+ order_token.empty?
27
+
28
+ spree_authorize! :update, spree_current_order, order_token
29
+
30
+ # Complete the order. This will call the purchase method with source containing credit card number
31
+ loop { break unless spree_current_order.next }
32
+
33
+ handle_order_state
34
+ end
35
+
36
+ # Generate Spree Response
37
+ def handle_order_state
38
+ # spree_current_order.payments.last.source
39
+
40
+ if spree_current_order.completed?
41
+ return render_serialized_payload(201) do
42
+ response = serialize_resource(spree_current_order.reload)
43
+
44
+ response[:data].merge!(build_genesis_response_parameters)
45
+
46
+ response
47
+ end
48
+ end
49
+
50
+ render_error_payload(spree_current_order.errors[:base].join('|'))
51
+ end
52
+
53
+ # Build additional response parameters
54
+ def build_genesis_response_parameters
55
+ spree_payment = spree_current_order.payments.last.private_metadata
56
+
57
+ { emerchantpay_payment: { state: spree_payment[:state], redirect_url: spree_payment[:redirect_url] } }
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ ::Spree::Api::V2::Storefront::CheckoutController.prepend(Spree::Api::V2::Storefront::CheckoutControllerDecorator)
@@ -0,0 +1,47 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ # Emerchantpay IPN Controller handler
6
+ class EmerchantpayNotificationController < ::Spree::Api::V2::BaseController
7
+
8
+ before_action :validate_params
9
+
10
+ attr_reader :permitted_params
11
+
12
+ # Notification handler action
13
+ def index
14
+ process_params
15
+
16
+ notification = SpreeEmerchantpayGenesis::Notifications::ServiceHandler.call permitted_params
17
+
18
+ render xml: notification.generate_response
19
+ rescue StandardError => e
20
+ render status: 422, plain: "Spree notification handling exited with: #{e.message}"
21
+ end
22
+
23
+ private
24
+
25
+ # Validate params
26
+ def validate_params
27
+ params.require [:unique_id, :signature]
28
+ end
29
+
30
+ # Permit params
31
+ def process_params
32
+ @permitted_params = params.permit(
33
+ :transaction_id, :terminal_token, :unique_id, :transaction_type, :status, :signature, :amount,
34
+ :eci, :cvv_result_code, :retrieval_reference_number, :authorization_code, :scheme_transaction_identifier,
35
+ :scheme_settlement_date, :threeds_authentication_flow, :threeds_target_protocol_version,
36
+ :threeds_concrete_protocol_version, :threeds_method_status, :scheme_response_code, :avs_response_code,
37
+ :avs_response_text, :reference_transaction_unique_id, :threeds_authentication_status_reason_code,
38
+ :card_brand, :card_number, :card_type, :card_subtype, :card_issuing_bank, :card_holder, :expiration_year,
39
+ :expiration_month
40
+ )
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,61 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ # 3D Secure Helper methods
6
+ class EmerchantpayThreedsController < ::Spree::Api::V2::BaseController
7
+
8
+ before_action :validate_params, except: [:callback_status]
9
+ before_action :initialize_service
10
+ after_action :allow_iframe, only: :callback_handler
11
+
12
+ attr_reader :permitted_params
13
+
14
+ # 3DSv2 Callback webhook handler
15
+ def callback_handler
16
+ @service.store_callback_status
17
+
18
+ render status: 200, json: { status: @service.fetch_callback_status }
19
+ end
20
+
21
+ # 3DSv2 Callback webhook status
22
+ def callback_status
23
+ render status: 200, json: { status: @service.fetch_callback_status }
24
+ end
25
+
26
+ # 3DSv2 Secure Method Continue Request
27
+ def method_continue
28
+ render status: 200, json: { status: 'OK', redirect_url: @service.process_method_continue }
29
+ end
30
+
31
+ private
32
+
33
+ # Initialize 3DSv2 Callback Service
34
+ def initialize_service
35
+ process_params
36
+
37
+ @service = SpreeEmerchantpayGenesis::Threeds::Callback.call @permitted_params
38
+ rescue StandardError
39
+ render status: 422, json: { error: 'Given request is invalid!' }
40
+ end
41
+
42
+ # Permit params
43
+ def process_params
44
+ @permitted_params = params.permit :unique_id, :signature, :threeds_method_status
45
+ end
46
+
47
+ # Validate Params
48
+ def validate_params
49
+ params.require [:unique_id, :signature]
50
+ end
51
+
52
+ # Allow iFrame execution
53
+ def allow_iframe
54
+ response.headers.except! 'X-Frame-Options'
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,31 @@
1
+ module Spree
2
+ # 3DSv2 Secure Method Continue customer controller
3
+ class EmerchantpayThreedsController < ApplicationController
4
+
5
+ after_action :allow_iframe, only: :index
6
+
7
+ layout 'spree/method_continue'
8
+
9
+ def index
10
+ service = SpreeEmerchantpayGenesis::Threeds::MethodContinue.call permitted_params
11
+
12
+ render 'method_continue', locals: service.build_secure_method_params
13
+ rescue StandardError => e
14
+ Rails.logger.error "Emerchantpay Threeds Controller: #{e.message}"
15
+
16
+ render plain: 'Error during Emerchantpay 3DSv2 execution. Contact administrator!'
17
+ end
18
+
19
+ private
20
+
21
+ # Allow iFrame execution
22
+ def allow_iframe
23
+ response.headers.except! 'X-Frame-Options'
24
+ end
25
+
26
+ def permitted_params
27
+ params.permit :unique_id, :checksum
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,110 @@
1
+ module Spree
2
+ module Admin
3
+ # Spree Payment Methods Helper for adding HTML elements (ex. select) for the Payment Method
4
+ module PaymentMethodsHelper
5
+
6
+ def preference_field_for(form, field, options) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
7
+ case options[:type]
8
+ when :integer, :string
9
+ form.text_field(field, preference_field_options(options))
10
+ when :boolean
11
+ form.check_box(field, preference_field_options(options))
12
+ when :password
13
+ form.password_field(field, preference_field_options(options))
14
+ when :text
15
+ form.text_area(field, preference_field_options(options))
16
+ when :boolean_select
17
+ label_tag(field, Spree.t(field))
18
+ form.select(field, { Spree.t(:enabled) => true, Spree.t(:disabled) => false }, {}, class: 'select2')
19
+ when :select
20
+ label_tag(field, Spree.t(field))
21
+ form.select(
22
+ field,
23
+ options_for_select(
24
+ options[:values].map { |key| [I18n.t(key, scope: 'emerchantpay.preferences'), key] }, options[:selected]
25
+ ),
26
+ {},
27
+ class: 'select2'
28
+ )
29
+ else
30
+ form.text_field(field, preference_field_options(options))
31
+ end
32
+ end
33
+
34
+ def preference_field_tag(name, value, options) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
35
+ case options[:type]
36
+ when :integer, :string
37
+ text_field_tag(name, value, preference_field_options(options))
38
+ when :boolean
39
+ hidden_field_tag(name, 0, id: "#{name}_hidden") +
40
+ check_box_tag(name, 1, value, preference_field_options(options))
41
+ when :password
42
+ password_field_tag(name, value, preference_field_options(options))
43
+ when :text
44
+ text_area_tag(name, value, preference_field_options(options))
45
+ when :boolean_select
46
+ select_tag(name, value, preference_field_options(options))
47
+ when :select
48
+ select_tag(name, value, preference_field_options(options))
49
+ else
50
+ text_field_tag(name, value, preference_field_options(options))
51
+ end
52
+ end
53
+
54
+ def preference_fields(object, form)
55
+ return unless object.respond_to?(:preferences)
56
+
57
+ get_preference_fields(object, object.preferences.keys, form)
58
+ end
59
+
60
+ def get_preference_fields(object, keys, form)
61
+ keys.reject { |k| k == :currency_merchant_accounts }.map do |key|
62
+ next if !object.has_preference?(key) || default_preference_support?(object, key)
63
+
64
+ if object.preference_type(key) == :boolean
65
+ add_boolean_preference(object, key, form)
66
+ else
67
+ add_common_preference(object, key, form)
68
+ end
69
+ end.join(' ').html_safe
70
+ end
71
+
72
+ private
73
+
74
+ def add_boolean_preference(object, key, form)
75
+ content_tag(
76
+ :div,
77
+ preference_field_for(
78
+ form,
79
+ "preferred_#{key}", type: object.preference_type(key)
80
+ ) + form.label("preferred_#{key}", Spree.t(key), class: 'form-check-label'),
81
+ class: 'form-group form-check',
82
+ id: [object.class.to_s.parameterize, 'preference', key].join('-')
83
+ )
84
+ end
85
+
86
+ def add_common_preference(object, key, form)
87
+ content_tag(:div, class: 'form-group', 'data-hook' => "preferred_#{key}") do
88
+ form.label("preferred_#{key}", "#{Spree.t(key)}: ") + preference_field_for(
89
+ form,
90
+ "preferred_#{key}",
91
+ type: object.preference_type(key),
92
+ values: (object.__send__("preferred_#{key}_default").is_a?(Hash) ? object.__send__("preferred_#{key}_default")[:values] : nil), # rubocop:disable Layout/LineLength
93
+ selected: object.preferences[key]
94
+ )
95
+ end
96
+ end
97
+
98
+ def default_preference_support?(object, preference)
99
+ unsupported_default_preferences.include?(preference.to_s) &&
100
+ object.instance_of?(Spree::Gateway::EmerchantpayDirect)
101
+ end
102
+
103
+ # Not Supported preferences from Emerchantpay Gateway
104
+ def unsupported_default_preferences
105
+ %w(server)
106
+ end
107
+
108
+ end
109
+ end
110
+ end