spree_emerchantpay_genesis 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +109 -10
  4. data/app/assets/javascripts/spree/frontend/card.min.js +3 -3
  5. data/app/controllers/spree/api/v2/storefront/checkout_controller_decorator.rb +45 -16
  6. data/app/controllers/spree/api/v2/storefront/emerchantpay_notification_controller.rb +5 -3
  7. data/app/helpers/spree/admin/payment_methods_helper.rb +20 -13
  8. data/app/helpers/spree_emerchantpay_genesis/mappers/genesis.rb +51 -163
  9. data/app/helpers/spree_emerchantpay_genesis/mappers/threeds_attributes.rb +227 -0
  10. data/app/helpers/spree_emerchantpay_genesis/payment_method_helper.rb +34 -0
  11. data/app/helpers/spree_emerchantpay_genesis/threeds_helper.rb +2 -1
  12. data/app/helpers/spree_emerchantpay_genesis/transaction_helper.rb +9 -5
  13. data/app/models/spree/emerchantpay_checkout_source.rb +37 -0
  14. data/app/models/spree/gateway/emerchantpay_checkout.rb +52 -0
  15. data/app/models/spree/gateway/emerchantpay_direct.rb +4 -19
  16. data/app/models/spree/payment_decorator.rb +31 -0
  17. data/app/models/spree_emerchantpay_genesis/base/gateway.rb +18 -1
  18. data/app/models/spree_emerchantpay_genesis/genesis_provider.rb +74 -18
  19. data/app/repositories/spree_emerchantpay_genesis/emerchantpay_payments_repository.rb +6 -5
  20. data/app/repositories/spree_emerchantpay_genesis/spree_payments_repository.rb +4 -8
  21. data/app/serializers/spree/api/v2/platform/emerchantpay_checkout_source_serializer.rb +17 -0
  22. data/app/services/spree/payments/create_decorator.rb +13 -1
  23. data/app/services/spree_emerchantpay_genesis/base/payment_service.rb +28 -2
  24. data/app/services/spree_emerchantpay_genesis/sources/create_checkout.rb +40 -0
  25. data/app/views/partials/_gateway_messages.html.erb +21 -0
  26. data/app/views/partials/_transaction_table.html.erb +34 -0
  27. data/app/views/spree/admin/payments/source_views/_emerchantpay_checkout.html.erb +23 -0
  28. data/app/views/spree/admin/payments/source_views/_emerchantpay_direct.html.erb +2 -55
  29. data/app/views/spree/checkout/payment/_emerchantpay_checkout.html.erb +3 -0
  30. data/config/initializers/extend_spree_permitted_checkout_attributes.rb +13 -0
  31. data/config/locales/en.yml +67 -0
  32. data/db/migrate/20240306152438_add_checkout_source.rb +22 -0
  33. data/lib/spree_emerchantpay_genesis/engine.rb +3 -1
  34. data/lib/spree_emerchantpay_genesis/version.rb +1 -1
  35. metadata +18 -6
@@ -0,0 +1,227 @@
1
+ module SpreeEmerchantpayGenesis
2
+ module Mappers
3
+ # Threeds V2 attributes Data Mapper
4
+ class ThreedsAttributes # rubocop:disable Metrics/ClassLength
5
+
6
+ attr_reader :context
7
+
8
+ include Spree::Core::Engine.routes.url_helpers
9
+
10
+ class << self
11
+
12
+ # Assign 3DSv2 attributes used for processing payments
13
+ def for_payment(genesis, order, source, options)
14
+ this = new genesis
15
+
16
+ this.map_processing_payment order, source, options
17
+ end
18
+
19
+ # Assign 3DSv2 attributes used for wpf payments
20
+ def for_wpf(genesis, order, options)
21
+ this = new genesis
22
+
23
+ this.map_wpf_payment order, options
24
+ end
25
+
26
+ end
27
+
28
+ # Initialize the mapper
29
+ def initialize(context)
30
+ @context = context
31
+ end
32
+
33
+ # Map processing 3DSv2 attributes
34
+ def map_processing_payment(order, source, options)
35
+ user_orders = fetch_user_orders order
36
+
37
+ method_attributes options
38
+ control_attributes options
39
+ purchase_attributes order
40
+ merchant_risk_attributes order, user_orders
41
+ card_holder_attributes order, user_orders
42
+ browser_attributes source
43
+
44
+ self
45
+ end
46
+
47
+ # Map WPF 3DSv2 attributes
48
+ def map_wpf_payment(order, options)
49
+ user_orders = fetch_user_orders order
50
+
51
+ control_attributes options, is_processing: false
52
+ purchase_attributes order
53
+ merchant_risk_attributes order, user_orders
54
+ card_holder_attributes order, user_orders
55
+
56
+ self
57
+ end
58
+
59
+ # Map Threeds Method attributes
60
+ def method_attributes(options)
61
+ @context.threeds_v2_method_callback_url =
62
+ "#{options[:hostname]}#{api_v2_storefront_emerchantpay_threeds_callback_handler_path}"
63
+
64
+ nil
65
+ end
66
+
67
+ def control_attributes(options, is_processing: true)
68
+ if is_processing
69
+ @context.threeds_v2_control_device_type = fetch_threeds_constant 'Control::DeviceTypes::BROWSER'
70
+ end
71
+
72
+ @context.threeds_v2_control_challenge_window_size =
73
+ fetch_threeds_constant('Control::ChallengeWindowSizes::FULLSCREEN')
74
+ @context.threeds_v2_control_challenge_indicator = options[:challenge_indicator]
75
+
76
+ nil
77
+ end
78
+
79
+ # Map 3DSv2 Purchase Category
80
+ def purchase_attributes(order)
81
+ @context.threeds_v2_purchase_category = if order.digital
82
+ fetch_threeds_constant('Purchase::Categories::SERVICE')
83
+ else
84
+ fetch_threeds_constant('Purchase::Categories::GOODS')
85
+ end
86
+
87
+ nil
88
+ end
89
+
90
+ # Map 3DSv2 Merchant Risk
91
+ def merchant_risk_attributes(order, user_orders)
92
+ @context.threeds_v2_merchant_risk_shipping_indicator = fetch_shipping_indicator order
93
+ @context.threeds_v2_merchant_risk_delivery_timeframe = fetch_delivery_timeframe order
94
+ @context.threeds_v2_merchant_risk_reorder_items_indicator = fetch_reorder_items_indicator order, user_orders
95
+
96
+ nil
97
+ end
98
+
99
+ # 3DSv2 Card Holder Account attributes
100
+ def card_holder_attributes(order, user_orders) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
101
+ if order.user.empty?
102
+ @context.threeds_v2_card_holder_account_registration_indicator =
103
+ fetch_threeds_constant('CardHolderAccount::RegistrationIndicators::GUEST_CHECKOUT')
104
+
105
+ return nil
106
+ end
107
+
108
+ @context.threeds_v2_card_holder_account_creation_date = order.user.created_at_formatted
109
+
110
+ # Card Holder last changes with the 3DS requester
111
+ card_holder_updated_at = ThreedsHelper.fetch_last_change_date(
112
+ order.user.billing_address, order.user.shipping_address
113
+ )
114
+ @context.threeds_v2_card_holder_account_update_indicator = ThreedsHelper.fetch_class_indicator(
115
+ 'UpdateIndicators', card_holder_updated_at
116
+ )
117
+ @context.threeds_v2_card_holder_account_last_change_date = card_holder_updated_at
118
+
119
+ # Card Holder Password changes
120
+ @context.threeds_v2_card_holder_account_password_change_indicator = ThreedsHelper.fetch_class_indicator(
121
+ 'PasswordChangeIndicators', order.user.updated_at_formatted
122
+ )
123
+ @context.threeds_v2_card_holder_account_password_change_date = order.user.updated_at_formatted
124
+
125
+ # Card Holder Shipping Address first time used
126
+ shipping_address_first_used = ThreedsHelper.fetch_shipping_address_first_used order.user.shipping_address
127
+ @context.threeds_v2_card_holder_account_shipping_address_usage_indicator = ThreedsHelper.fetch_class_indicator(
128
+ 'ShippingAddressUsageIndicators', shipping_address_first_used
129
+ )
130
+ @context.threeds_v2_card_holder_account_shipping_address_date_first_used = shipping_address_first_used
131
+
132
+ # Card Holder Payments count
133
+ activity_last24_hours = ThreedsHelper.filter_transaction_activity_24_hours user_orders
134
+ if activity_last24_hours.positive?
135
+ @context.threeds_v2_card_holder_account_transactions_activity_last24_hours = activity_last24_hours
136
+ end
137
+
138
+ previous_year_count = ThreedsHelper.filter_transaction_activity_previous_year user_orders
139
+ if previous_year_count.positive?
140
+ @context.threeds_v2_card_holder_account_transactions_activity_previous_year = previous_year_count
141
+ end
142
+
143
+ purchases_last6_months = ThreedsHelper.filter_purchases_count_last6_months user_orders
144
+ if purchases_last6_months.positive?
145
+ @context.threeds_v2_card_holder_account_purchases_count_last6_months = purchases_last6_months
146
+ end
147
+
148
+ # Card Holder registration with the 3D requester
149
+ first_completed_payment = ThreedsHelper.filter_first_completed_payment user_orders
150
+ unless first_completed_payment.empty?
151
+ string_date = DateTime.parse(first_completed_payment['payment_created_at'].to_s)
152
+ .strftime(ThreedsHelper::DATE_ISO_FORMAT)
153
+
154
+ @context.threeds_v2_card_holder_account_registration_indicator = ThreedsHelper.fetch_class_indicator(
155
+ 'RegistrationIndicators', string_date
156
+ )
157
+ @context.threeds_v2_card_holder_account_registration_date = string_date
158
+ end
159
+
160
+ nil
161
+ end
162
+
163
+ # 3DSv2 Browser attributes
164
+ def browser_attributes(source) # rubocop:disable Metrics/AbcSize
165
+ @context.threeds_v2_browser_accept_header = source.public_metadata[:accept_header]
166
+ @context.threeds_v2_browser_java_enabled = ActiveModel::Type::Boolean.new.cast(
167
+ source.public_metadata[:java_enabled]
168
+ )
169
+ @context.threeds_v2_browser_language = source.public_metadata[:language]
170
+ @context.threeds_v2_browser_color_depth = source.public_metadata[:color_depth]
171
+ @context.threeds_v2_browser_screen_height = source.public_metadata[:screen_height]
172
+ @context.threeds_v2_browser_screen_width = source.public_metadata[:screen_width]
173
+ @context.threeds_v2_browser_time_zone_offset = source.public_metadata[:time_zone_offset]
174
+ @context.threeds_v2_browser_user_agent = source.public_metadata[:user_agent]
175
+ end
176
+
177
+ private
178
+
179
+ # Helper method for 3DSv2 constants
180
+ def fetch_threeds_constant(constant)
181
+ "#{ThreedsHelper::CONSTANT_PATH}#{constant}".constantize
182
+ end
183
+
184
+ # Fetch User Orders
185
+ def fetch_user_orders(order)
186
+ return SpreeOrderRepository.orders_by_user order.user_id if order.user_id
187
+
188
+ []
189
+ end
190
+
191
+ # Fetch 3DSv2 Merchant Risk Shipping Indicator
192
+ def fetch_shipping_indicator(order)
193
+ return fetch_threeds_constant('MerchantRisk::ShippingIndicators::DIGITAL_GOODS') if order.digital
194
+
195
+ if order.shipping_address&.to_s == order.billing_address&.to_s
196
+ return fetch_threeds_constant 'MerchantRisk::ShippingIndicators::SAME_AS_BILLING'
197
+ end
198
+
199
+ return fetch_threeds_constant 'MerchantRisk::ShippingIndicators::STORED_ADDRESS' if order.user_id
200
+
201
+ fetch_threeds_constant 'MerchantRisk::ShippingIndicators::OTHER'
202
+ end
203
+
204
+ # Fetch 3DSv2 Merchant Risk Delivery Timeframe
205
+ def fetch_delivery_timeframe(order)
206
+ return fetch_threeds_constant 'MerchantRisk::DeliveryTimeframes::ELECTRONICS' if order.digital
207
+
208
+ fetch_threeds_constant 'MerchantRisk::DeliveryTimeframes::ANOTHER_DAY'
209
+ end
210
+
211
+ # Fetch 3DSv2 Merchant Risk Reorder Items Indicator
212
+ def fetch_reorder_items_indicator(order, user_orders)
213
+ return fetch_threeds_constant 'MerchantRisk::ReorderItemIndicators::FIRST_TIME' unless order.user_id
214
+
215
+ variants = []
216
+ order.line_items.each { |line_item| variants.push line_item[:variant_id] }
217
+
218
+ reordered = SpreeOrderRepository.reordered_variant? order.id, variants, user_orders
219
+
220
+ return fetch_threeds_constant 'MerchantRisk::ReorderItemIndicators::REORDERED' if reordered
221
+
222
+ fetch_threeds_constant 'MerchantRisk::ReorderItemIndicators::FIRST_TIME'
223
+ end
224
+
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,34 @@
1
+ module SpreeEmerchantpayGenesis
2
+ # Helper methods used in the transaction processing
3
+ class PaymentMethodHelper
4
+
5
+ DIRECT_PAYMENT = 'emerchantpay_direct'.freeze
6
+ CHECKOUT_PAYMENT = 'emerchantpay_checkout'.freeze
7
+
8
+ class << self
9
+
10
+ # Fetch the Payment type from the given Payment Method
11
+ def fetch_method_type(payment_method)
12
+ case payment_method
13
+ when Spree::Gateway::EmerchantpayDirect.name
14
+ DIRECT_PAYMENT
15
+ when Spree::Gateway::EmerchantpayCheckout.name
16
+ CHECKOUT_PAYMENT
17
+ else
18
+ ''
19
+ end
20
+ end
21
+
22
+ # Build Checkout source attribute if source_attributes are not present?
23
+ def default_checkout_source_attributes(order)
24
+ default_attr = { name: CHECKOUT_PAYMENT }
25
+
26
+ default_attr.merge!({ consumer_email: order.email }) if order&.email
27
+
28
+ default_attr
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
@@ -8,6 +8,7 @@ module SpreeEmerchantpayGenesis
8
8
  CURRENT_TRANSACTION_INDICATOR = 'current_transaction'.freeze
9
9
  DATE_ISO_FORMAT = GenesisRuby::Api::Constants::DateTimeFormats::YYYY_MM_DD_ISO_8601
10
10
  COMPLETED_SPREE_PAYMENT_STATE = 'completed'.freeze
11
+ CONSTANT_PATH = 'GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::'.freeze
11
12
 
12
13
  class << self
13
14
 
@@ -70,7 +71,7 @@ module SpreeEmerchantpayGenesis
70
71
 
71
72
  # Fetch the given 3DSv2 parameter indicator
72
73
  def fetch_class_indicator(indicator_class, updated_at) # rubocop:disable Metrics/MethodLength
73
- constant_path = 'GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::CardHolderAccount::'
74
+ constant_path = "#{CONSTANT_PATH}CardHolderAccount::"
74
75
 
75
76
  case fetch_indicator updated_at
76
77
  when LESS_THAN_30_DAYS_INDICATOR
@@ -6,6 +6,7 @@ module SpreeEmerchantpayGenesis
6
6
  CAPTURE_ACTION = 'capture'.freeze
7
7
  REFUND_ACTION = 'refund'.freeze
8
8
  VOID_ACTION = 'void'.freeze
9
+ WPF_TRANSACTION_TYPE = 'checkout'.freeze
9
10
 
10
11
  class << self
11
12
 
@@ -29,7 +30,7 @@ module SpreeEmerchantpayGenesis
29
30
 
30
31
  # Check given response for asynchronous execution
31
32
  def async_result?(response)
32
- response.pending? || response.pending_async? || response.in_progress? || response.pending_hold?
33
+ response.pending? || response.pending_async? || response.in_progress? || response.pending_hold? || response.new?
33
34
  end
34
35
 
35
36
  # Check given response for Method Continue parameters
@@ -39,7 +40,7 @@ module SpreeEmerchantpayGenesis
39
40
 
40
41
  # Check given response for failure
41
42
  def failure_result?(response)
42
- response.error? || response.declined?
43
+ response.error? || response.declined? || response.timeout?
43
44
  end
44
45
 
45
46
  # Generate Spree Response from Gateway action
@@ -71,9 +72,7 @@ module SpreeEmerchantpayGenesis
71
72
  end
72
73
 
73
74
  # Check if the given Genesis Response can be stored
74
- def can_save_genesis_response?(response)
75
- response_object = response.response_object
76
-
75
+ def can_save_genesis_response?(response_object)
77
76
  !(
78
77
  response_object[:transaction_id].nil? ||
79
78
  response_object[:transaction_type].nil? ||
@@ -109,6 +108,11 @@ module SpreeEmerchantpayGenesis
109
108
  GenesisRuby::Api::Requests::Financial::Cards::Threeds::V2::MethodContinue.new configuration
110
109
  end
111
110
 
111
+ # Initialize WPF API Request
112
+ def init_wpf_req(configuration)
113
+ GenesisRuby::Api::Requests::Wpf::Create.new configuration
114
+ end
115
+
112
116
  # Fetch Redirect Url from Genesis Response
113
117
  def fetch_redirect_url(options, response)
114
118
  url = ''
@@ -0,0 +1,37 @@
1
+ module Spree
2
+ # Emerchantpay Checkout Source
3
+ class EmerchantpayCheckoutSource < Spree::Base
4
+
5
+ acts_as_paranoid
6
+
7
+ self.table_name = 'emerchantpay_checkout_sources'
8
+
9
+ belongs_to :payment_method
10
+ belongs_to :user, class_name: Spree.user_class.to_s, foreign_key: 'user_id',
11
+ optional: true
12
+ has_many :payments, as: :source
13
+
14
+ include Spree::Metadata
15
+
16
+ def actions
17
+ %w(capture void credit)
18
+ end
19
+
20
+ # Indicates whether its possible to capture the payment
21
+ def can_capture?(payment)
22
+ payment.pending? || payment.checkout?
23
+ end
24
+
25
+ # Indicates whether its possible to void the payment.
26
+ def can_void?(payment)
27
+ !payment.failed? && !payment.void?
28
+ end
29
+
30
+ # Indicates whether its possible to credit the payment. Note that most gateways require that the
31
+ # payment be settled first which generally happens within 12-24 hours of the transaction.
32
+ def can_credit?(payment)
33
+ payment.completed? && payment.credit_allowed.positive?
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,52 @@
1
+ module Spree
2
+ # Emerchantpay Checkout Payment Method
3
+ class Gateway::EmerchantpayCheckout < SpreeEmerchantpayGenesis::Base::Gateway # rubocop:disable Style/ClassAndModuleChildren
4
+
5
+ preference :transaction_types, :multi_select, default: lambda {
6
+ { values: GenesisRuby::Utils::Transactions::WpfTypes.all }
7
+ }
8
+ preference :return_cancel_url, :string, default: 'http://localhost:4000/checkout/payment'
9
+ preference :return_pending_url, :string, default: 'http://localhost:4000/orders/|:ORDER:|'
10
+
11
+ delegate :load_data, :load_source, :load_payment, to: :provider
12
+
13
+ def method_type
14
+ SpreeEmerchantpayGenesis::PaymentMethodHelper::CHECKOUT_PAYMENT
15
+ end
16
+
17
+ def purchase(_money_in_cents, source, gateway_options)
18
+ order, payment = order_data_from_options gateway_options
19
+ user = order.user
20
+
21
+ prepare_provider(
22
+ SpreeEmerchantpayGenesis::Mappers::Order.prepare_data(order, user, gateway_options),
23
+ source,
24
+ payment
25
+ )
26
+
27
+ provider.purchase
28
+ end
29
+
30
+ def source_required?
31
+ true
32
+ end
33
+
34
+ def payment_source_class
35
+ EmerchantpayCheckoutSource
36
+ end
37
+
38
+ def auto_capture?
39
+ false
40
+ end
41
+
42
+ private
43
+
44
+ # Prepare provider
45
+ def prepare_provider(data, source, payment)
46
+ load_data SpreeEmerchantpayGenesis::Mappers::Order.for data
47
+ load_source source
48
+ load_payment payment
49
+ end
50
+
51
+ end
52
+ end
@@ -3,21 +3,14 @@ module Spree
3
3
  class Gateway::EmerchantpayDirect < SpreeEmerchantpayGenesis::Base::Gateway # rubocop:disable Style/ClassAndModuleChildren
4
4
 
5
5
  preference :token, :string
6
+ preference :transaction_types, :select, default: lambda {
7
+ { values: [:authorize, :authorize3d, :sale, :sale3d] }
8
+ }
6
9
 
7
10
  delegate :load_data, :load_source, :load_payment, to: :provider
8
11
 
9
12
  def method_type
10
- 'emerchantpay_direct'
11
- end
12
-
13
- def provider_class
14
- SpreeEmerchantpayGenesis::GenesisProvider
15
- end
16
-
17
- def provider
18
- @provider = provider_class.new options if @provider.nil?
19
-
20
- @provider
13
+ SpreeEmerchantpayGenesis::PaymentMethodHelper::DIRECT_PAYMENT
21
14
  end
22
15
 
23
16
  def purchase(_money_in_cents, source, gateway_options)
@@ -33,14 +26,6 @@ module Spree
33
26
  provider.purchase
34
27
  end
35
28
 
36
- def authorize(money_in_cents, source, gateway_options)
37
- purchase money_in_cents, source, gateway_options
38
- end
39
-
40
- def supports?(_source)
41
- true
42
- end
43
-
44
29
  def source_required?
45
30
  true
46
31
  end
@@ -7,6 +7,37 @@ module Spree
7
7
  SpreeEmerchantpayGenesis::EmerchantpayPaymentsRepository.find_all_by_order_and_payment order.number, number
8
8
  end
9
9
 
10
+ # Monkey patch for Emerchantpay Checkout Source
11
+ # see https://github.com/spree/spree/issues/981
12
+ def build_source
13
+ return unless new_record?
14
+
15
+ if default_emerchantpay_checkout_source?
16
+ self.source_attributes = SpreeEmerchantpayGenesis::PaymentMethodHelper.default_checkout_source_attributes order
17
+ end
18
+
19
+ build_default_source if can_build_source?
20
+ end
21
+
22
+ private
23
+
24
+ # Check if emerchantpay checkout source should be build with the default source_attributes
25
+ def default_emerchantpay_checkout_source?
26
+ payment_method.is_a?(Spree::Gateway::EmerchantpayCheckout) && !source_attributes.present?
27
+ end
28
+
29
+ # Check if can build source
30
+ def can_build_source?
31
+ source_attributes.present? && source.blank? && payment_method.try(:payment_source_class)
32
+ end
33
+
34
+ # Default Spree build source logic
35
+ def build_default_source
36
+ self.source = payment_method.payment_source_class.new(source_attributes)
37
+ source.payment_method_id = payment_method.id
38
+ source.user_id = order.user_id if order
39
+ end
40
+
10
41
  end
11
42
  end
12
43
 
@@ -5,7 +5,6 @@ module SpreeEmerchantpayGenesis
5
5
 
6
6
  preference :username, :string
7
7
  preference :password, :string
8
- preference :transaction_types, :select, default: -> { { values: [:authorize, :authorize3d, :sale, :sale3d] } }
9
8
  preference :return_success_url, :string, default: 'http://localhost:4000/orders/|:ORDER:|'
10
9
  preference :return_failure_url, :string, default: 'http://localhost:4000/checkout/payment?order_number=|:ORDER:|'
11
10
  preference :threeds_allowed, :boolean_select, default: true
@@ -15,6 +14,20 @@ module SpreeEmerchantpayGenesis
15
14
  preference :hostname, :string, default: 'http://127.0.0.1:4000'
16
15
  preference :test_mode, :boolean_select, default: true
17
16
 
17
+ def provider_class
18
+ SpreeEmerchantpayGenesis::GenesisProvider
19
+ end
20
+
21
+ def provider
22
+ @provider = provider_class.new method_type, options if @provider.nil?
23
+
24
+ @provider
25
+ end
26
+
27
+ def authorize(money_in_cents, source, gateway_options)
28
+ purchase money_in_cents, source, gateway_options
29
+ end
30
+
18
31
  # Capture authorized payment
19
32
  def capture(amount, transaction_id, gateway_options)
20
33
  order, payment = order_data_from_options gateway_options
@@ -53,6 +66,10 @@ module SpreeEmerchantpayGenesis
53
66
  false
54
67
  end
55
68
 
69
+ def supports?(_source)
70
+ true
71
+ end
72
+
56
73
  def order_data_from_options(options)
57
74
  order_number, payment_number = options[:order_id].split('-')
58
75
  order = Spree::Order.find_by(number: order_number)
@@ -5,9 +5,10 @@ module SpreeEmerchantpayGenesis
5
5
  attr_reader :provider_data
6
6
 
7
7
  # Constructor
8
- def initialize(options)
9
- @options = options
10
- @configuration = Mappers::Genesis.for_config(@options).context
8
+ def initialize(method_type, options)
9
+ @options = options
10
+ @configuration = Mappers::Genesis.for_config(@options).context
11
+ @method_type = method_type
11
12
  end
12
13
 
13
14
  # Load Order data
@@ -32,16 +33,11 @@ module SpreeEmerchantpayGenesis
32
33
  # Create a payment
33
34
  def purchase
34
35
  safe_execute do
35
- genesis_request = TransactionHelper.init_genesis_req @configuration, @options[:transaction_types]
36
-
37
- genesis = GenesisRuby::Genesis.new(
38
- @configuration,
39
- Mappers::Genesis.for_payment(genesis_request, @order, @source, @options).context
40
- )
36
+ genesis = init_gateway_req
41
37
 
42
38
  response = genesis.execute.response
43
39
 
44
- handle_response genesis_request, response
40
+ handle_response genesis.request, response
45
41
 
46
42
  response
47
43
  end
@@ -140,12 +136,14 @@ module SpreeEmerchantpayGenesis
140
136
 
141
137
  # Handle Genesis Response
142
138
  def handle_response(request, response, is_payment: true)
143
- if TransactionHelper.can_save_genesis_response? response
144
- EmerchantpayPaymentsRepository.save_from_response_data request, response, @order, @payment
139
+ response_object = fetch_response_object response
140
+
141
+ if TransactionHelper.can_save_genesis_response? response_object
142
+ EmerchantpayPaymentsRepository.save_from_response_data request, response_object, @order, @payment
145
143
  end
146
144
 
147
- SpreePaymentsRepository.update_payment @payment, response if is_payment
148
- SpreePaymentsRepository.add_payment_metadata @payment, @options, response
145
+ SpreePaymentsRepository.update_payment @payment, response_object if is_payment
146
+ SpreePaymentsRepository.add_payment_metadata @payment, @options, response, response_object
149
147
 
150
148
  # Missing shipping amount fix
151
149
  @payment.amount = response.response_object[:amount] if response.response_object&.key?(:amount) && is_payment
@@ -158,11 +156,13 @@ module SpreeEmerchantpayGenesis
158
156
 
159
157
  # Handle Genesis Notification Response
160
158
  def handle_reconciliation_response(emerchantpay_payment, reconciliation)
161
- EmerchantpayPaymentsRepository.update_from_response_data(emerchantpay_payment, reconciliation, @payment)
159
+ response_object = fetch_reconciliation_object reconciliation, emerchantpay_payment.unique_id
160
+
161
+ EmerchantpayPaymentsRepository.update_from_response_data emerchantpay_payment, response_object, @payment
162
162
 
163
- SpreePaymentsRepository.update_payment @payment, reconciliation
164
- SpreePaymentsRepository.add_payment_metadata @payment, @options, reconciliation
165
- SpreePaymentsRepository.update_payment_status @payment, reconciliation
163
+ SpreePaymentsRepository.update_payment @payment, response_object
164
+ SpreePaymentsRepository.add_payment_metadata @payment, @options, reconciliation, response_object
165
+ SpreePaymentsRepository.update_payment_status @payment, reconciliation, response_object[:transaction_type]
166
166
 
167
167
  @payment.save
168
168
 
@@ -199,5 +199,61 @@ module SpreeEmerchantpayGenesis
199
199
  @options
200
200
  end
201
201
 
202
+ # Initialize Gateway API Request
203
+ def init_gateway_req
204
+ case @method_type
205
+ when PaymentMethodHelper::CHECKOUT_PAYMENT
206
+ init_wpf_api
207
+ when PaymentMethodHelper::DIRECT_PAYMENT
208
+ init_processing_api
209
+ else
210
+ raise GenesisRuby::Error, 'Invalid Payment Method Type given!'
211
+ end
212
+ end
213
+
214
+ # Init Processing API Request
215
+ def init_processing_api
216
+ genesis_request = TransactionHelper.init_genesis_req @configuration, @options[:transaction_types]
217
+
218
+ GenesisRuby::Genesis.new(
219
+ @configuration,
220
+ Mappers::Genesis.for_payment(genesis_request, @order, @source, @options).context
221
+ )
222
+ end
223
+
224
+ # Init WPF API Request
225
+ def init_wpf_api
226
+ genesis_request = TransactionHelper.init_wpf_req @configuration
227
+
228
+ GenesisRuby::Genesis.new(
229
+ @configuration,
230
+ Mappers::Genesis.for_wpf(genesis_request, @order, @source, @options).context
231
+ )
232
+ end
233
+
234
+ # Prepare the response object
235
+ def fetch_response_object(genesis_response)
236
+ response_object = genesis_response.response_object
237
+ test_mode = ActiveModel::Type::Boolean.new.cast(@options[:test_mode]) ? 'test' : 'live'
238
+
239
+ if @method_type == PaymentMethodHelper::CHECKOUT_PAYMENT && genesis_response.new?
240
+ response_object.merge!(
241
+ { transaction_type: TransactionHelper::WPF_TRANSACTION_TYPE, mode: test_mode, terminal_token: '' }
242
+ )
243
+ end
244
+
245
+ response_object
246
+ end
247
+
248
+ # Fetch the payment_transaction object from the given reconciliation response
249
+ def fetch_reconciliation_object(reconciliation, unique_id)
250
+ response_object = reconciliation.response_object
251
+
252
+ return response_object unless response_object.key? :payment_transaction
253
+ return response_object[:payment_transaction] if response_object[:payment_transaction].is_a? Hash
254
+
255
+ response_object[:payment_transaction].select { |payment| payment[:unique_id] == unique_id }.first
256
+ end
257
+
202
258
  end
203
259
  end