genesis_ruby 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7220a925cd5f4162c331e8f76df937b25d94c61ba2b00ffa2c0e658a9555a23
4
- data.tar.gz: b24aece0771f858eeed93a62ebb8a6ad2b1751d85f30b72c98ed647ae3b17f9d
3
+ metadata.gz: b8cc160e0cdb5cb22dd4726e2471a9d85ba199c4ee2bdd2e8cbd57b057657ce8
4
+ data.tar.gz: 824dc704419375ffd6276a45d757f1b45e40f6e5d542462d73b18c207392df9e
5
5
  SHA512:
6
- metadata.gz: d061c605183153c41dbde88215322a084b06f3ac293509c54ba7109e2e4342437a813f340ad11fa26a3a8352c7201a05ca667d5b0092a3ef22125128501a1b3e
7
- data.tar.gz: 108c00082a58b5addceb9f20f0d13eee410e86f4cfc38e2ad6ff47d2900f05876dc62cff5d498e56b1a10e575cb8446cce211a42d5b87e26b1be53d4578efcab
6
+ metadata.gz: 4cd6c2db4ec7da458a87d82ad550af947585fea23158117d5458f324aa966db882601fba8490357a349ae551c528825bf6d1e52381e3e3d06c24746a25af3ef3
7
+ data.tar.gz: 253fe2aaaec0df2023a6a2d37c74b04dd12eba99da1d376ff01f5566084449dbdbd3d4fe33560eb6d68e7ba98119baf33caa235879ffad9af743c2dc392ebd66
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ 0.1.2
2
+ -----
3
+ **Features**:
4
+
5
+ * Added 3D Secure Method Continue API request support
6
+ * Updated Gateway response handling upon error cases like 3D Secure Method Continue with invalid identifier a Network error will be raised
7
+ * Added `GenesisRuby::Api::Notification` handler that provides an easy way of handling Gateway instant payment notifications
8
+
1
9
  0.1.1
2
10
  -----
3
11
  **Features**:
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- genesis_ruby (0.1.1)
4
+ genesis_ruby (0.1.2)
5
5
  net-http (~> 0.3.2)
6
6
  nokogiri (~> 1.14)
7
7
 
@@ -11,14 +11,14 @@ GEM
11
11
  addressable (2.8.5)
12
12
  public_suffix (>= 2.0.2, < 6.0)
13
13
  ast (2.4.2)
14
- base64 (0.1.1)
14
+ base64 (0.2.0)
15
15
  concurrent-ruby (1.2.2)
16
16
  crack (0.4.5)
17
17
  rexml
18
18
  diff-lcs (1.5.0)
19
19
  faker (2.23.0)
20
20
  i18n (>= 1.8.11, < 2)
21
- faraday (2.7.11)
21
+ faraday (2.7.12)
22
22
  base64
23
23
  faraday-net_http (>= 2.0, < 3.1)
24
24
  ruby2_keywords (>= 0.0.4)
@@ -37,26 +37,26 @@ GEM
37
37
  json (2.6.3)
38
38
  language_server-protocol (3.17.0.3)
39
39
  mini_mime (1.1.5)
40
- mini_portile2 (2.8.4)
40
+ mini_portile2 (2.8.5)
41
41
  multi_xml (0.6.0)
42
42
  mustermann (3.0.0)
43
43
  ruby2_keywords (~> 0.0.1)
44
44
  net-http (0.3.2)
45
45
  uri
46
- nokogiri (1.15.4)
46
+ nokogiri (1.15.5)
47
47
  mini_portile2 (~> 2.8.2)
48
48
  racc (~> 1.4)
49
- octokit (6.1.1)
49
+ octokit (7.2.0)
50
50
  faraday (>= 1, < 3)
51
51
  sawyer (~> 0.9)
52
52
  parallel (1.23.0)
53
- parser (3.2.2.3)
53
+ parser (3.2.2.4)
54
54
  ast (~> 2.4.1)
55
55
  racc
56
- pronto (0.11.1)
56
+ pronto (0.11.2)
57
57
  gitlab (>= 4.4.0, < 5.0)
58
58
  httparty (>= 0.13.7, < 1.0)
59
- octokit (>= 4.7.0, < 7.0)
59
+ octokit (>= 4.7.0, < 8.0)
60
60
  rainbow (>= 2.2, < 4.0)
61
61
  rexml (>= 3.2.5, < 4.0)
62
62
  rugged (>= 0.23.0, < 2.0)
@@ -64,14 +64,14 @@ GEM
64
64
  pronto-rubocop (0.11.5)
65
65
  pronto (~> 0.11.0)
66
66
  rubocop (>= 0.63.1, < 2.0)
67
- public_suffix (5.0.3)
68
- racc (1.7.1)
67
+ public_suffix (5.0.4)
68
+ racc (1.7.3)
69
69
  rack (2.2.8)
70
70
  rack-protection (3.1.0)
71
71
  rack (~> 2.2, >= 2.2.4)
72
72
  rainbow (3.1.1)
73
- rake (13.0.6)
74
- regexp_parser (2.8.1)
73
+ rake (13.1.0)
74
+ regexp_parser (2.8.2)
75
75
  rexml (3.2.6)
76
76
  rspec (3.12.0)
77
77
  rspec-core (~> 3.12.0)
@@ -86,29 +86,28 @@ GEM
86
86
  diff-lcs (>= 1.2.0, < 2.0)
87
87
  rspec-support (~> 3.12.0)
88
88
  rspec-support (3.12.1)
89
- rubocop (1.56.3)
90
- base64 (~> 0.1.1)
89
+ rubocop (1.57.2)
91
90
  json (~> 2.3)
92
91
  language_server-protocol (>= 3.17.0)
93
92
  parallel (~> 1.10)
94
- parser (>= 3.2.2.3)
93
+ parser (>= 3.2.2.4)
95
94
  rainbow (>= 2.2.2, < 4.0)
96
95
  regexp_parser (>= 1.8, < 3.0)
97
96
  rexml (>= 3.2.5, < 4.0)
98
97
  rubocop-ast (>= 1.28.1, < 2.0)
99
98
  ruby-progressbar (~> 1.7)
100
99
  unicode-display_width (>= 2.4.0, < 3.0)
101
- rubocop-ast (1.29.0)
100
+ rubocop-ast (1.30.0)
102
101
  parser (>= 3.2.1.0)
103
- rubocop-capybara (2.18.0)
102
+ rubocop-capybara (2.19.0)
104
103
  rubocop (~> 1.41)
105
- rubocop-factory_bot (2.23.1)
104
+ rubocop-factory_bot (2.24.0)
106
105
  rubocop (~> 1.33)
107
106
  rubocop-faker (1.1.0)
108
107
  faker (>= 2.12.0)
109
108
  rubocop (>= 0.82.0)
110
- rubocop-rspec (2.24.0)
111
- rubocop (~> 1.33)
109
+ rubocop-rspec (2.25.0)
110
+ rubocop (~> 1.40)
112
111
  rubocop-capybara (~> 2.17)
113
112
  rubocop-factory_bot (~> 2.22)
114
113
  ruby-progressbar (1.13.0)
@@ -124,10 +123,10 @@ GEM
124
123
  tilt (~> 2.0)
125
124
  terminal-table (3.0.2)
126
125
  unicode-display_width (>= 1.1.1, < 3)
127
- thor (1.2.2)
126
+ thor (1.3.0)
128
127
  tilt (2.3.0)
129
- unicode-display_width (2.4.2)
130
- uri (0.12.2)
128
+ unicode-display_width (2.5.0)
129
+ uri (0.13.0)
131
130
  webmock (3.19.1)
132
131
  addressable (>= 2.8.0)
133
132
  crack (>= 0.3.2)
data/README.md CHANGED
@@ -105,6 +105,8 @@ rescue GenesisRuby::Error => error
105
105
  end
106
106
  ```
107
107
 
108
+ A full list of the Transaction Types and Custom Attributes can be found [here](https://emerchantpay.github.io/gateway-api-docs/?shell#wpf-transaction-types).
109
+
108
110
  ### Transactions
109
111
 
110
112
  ```ruby
@@ -144,7 +146,280 @@ rescue GenesisRuby::Error => error
144
146
  end
145
147
  ```
146
148
 
147
- A full list of the Transaction Types and Custom Attributes can be found [here](https://emerchantpay.github.io/gateway-api-docs/?shell#wpf-transaction-types).
149
+ ### Example 3DSv2 Request
150
+
151
+ Sample request including all the conditionally required/optional params for initiating a 3DS transaction with the 3DSv2-Method authentication protocol.
152
+
153
+ Also, an example is provided for the 3DS-Method-continue API call that will have to be submitted after the 3DS-Method is initiated.
154
+ <details>
155
+
156
+ ```ruby
157
+ require 'genesis_ruby'
158
+
159
+ begin
160
+ genesis_3ds_v2 = GenesisRuby::Genesis.for(config: configuration, request: GenesisRuby::Api::Requests::Financial::Cards::Sale3d) do |request|
161
+ # Common Attributes
162
+ request.transaction_id = '12345-67890'
163
+ request.remote_ip = '127.0.0.1'
164
+ request.amount = '0.99'
165
+ request.currency = 'EUR'
166
+ request.usage = 'Example usage'
167
+ request.customer_email = 'travis@example.com'
168
+ request.customer_phone = '+1987987987987'
169
+
170
+ # Credit Card Attributes
171
+ request.card_holder = 'Travis Pastrana'
172
+
173
+ # Test Cases
174
+ request.card_number = '4012000000060085' # Test Case: Synchronous 3DSv2 Request with Frictionless flow
175
+ # request.card_number = '4066330000000004' # Test Case: Asynchronous 3DSv2 Request with 3DS-Method and Frictionless flow
176
+ # request.card_number = '4918190000000002' # Test Case: Asynchronous 3DSv2 Request with Challenge flow
177
+ # request.card_number = '4938730000000001' # Test Case: Asynchronous 3DSv2 Request with 3DS-Method Challenge flow
178
+ # request.card_number = '4901170000000003' # Test Case: Asynchronous 3DSv2 Request with Fallback flow
179
+ # request.card_number = '4901164281364345' # Test Case: Asynchronous 3DSv2 Request with 3DS-Method Fallback flow
180
+
181
+ request.expiration_month = '12'
182
+ request.expiration_year = '2040'
183
+ request.cvv = '123'
184
+
185
+ # Async Attributes
186
+ request.notification_url = 'https://example.com/notification'
187
+ request.return_success_url = 'https://example.com/success'
188
+ request.return_failure_url = 'https://example.com/failure'
189
+
190
+ # Billing Attributes
191
+ request.billing_first_name = 'Travis'
192
+ request.billing_last_name = 'Pastrana'
193
+ request.billing_address1 = 'Kreisfreie Stadt Berlin'
194
+ request.billing_zip_code = '10115'
195
+ request.billing_city = 'Berlin'
196
+ request.billing_country = 'DE'
197
+
198
+ # Threeds V2 Attributes
199
+
200
+ ## Method Attributes
201
+ request.threeds_v2_method_callback_url = 'https://www.example.com/threeds/threeds_method/callback'
202
+
203
+ ## Control Attributes
204
+ request.threeds_v2_control_device_type =
205
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::Control::DeviceTypes::BROWSER
206
+ request.threeds_v2_control_challenge_window_size =
207
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::Control::ChallengeWindowSizes::FULLSCREEN
208
+ request.threeds_v2_control_challenge_indicator =
209
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::Control::ChallengeIndicators::MANDATE
210
+
211
+ ## Browser Attributes
212
+ ## When Control Device Type is Browser
213
+ request.threeds_v2_browser_accept_header = '*/*'
214
+ request.threeds_v2_browser_java_enabled = true
215
+ request.threeds_v2_browser_language = 'en-GB'
216
+ request.threeds_v2_browser_color_depth = 48
217
+ request.threeds_v2_browser_screen_height = 900
218
+ request.threeds_v2_browser_screen_width = 1440
219
+ request.threeds_v2_browser_time_zone_offset = '+0'
220
+ request.threeds_v2_browser_user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36'
221
+
222
+ ## SDK
223
+ ## When Control Device Type is SDK
224
+ request.threeds_v2_sdk_interface =
225
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::Sdk::Interfaces::BOTH
226
+ request.threeds_v2_sdk_ui_types =
227
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::Sdk::UiTypes::TEXT
228
+ request.threeds_v2_sdk_ui_types =
229
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::Sdk::UiTypes::SINGLE_SELECT
230
+ request.threeds_v2_sdk_ui_types =
231
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::Sdk::UiTypes::MULTI_SELECT
232
+ request.threeds_v2_sdk_application_id = 'fc1650c0-5778-0138-8205-2cbc32a32d65'
233
+ request.threeds_v2_sdk_encrypted_data = 'encrypted-data-here'
234
+ request.threeds_v2_sdk_ephemeral_public_key_pair = 'public-key-pair'
235
+ request.threeds_v2_sdk_max_timeout = 10
236
+ request.threeds_v2_sdk_reference_number = 'sdk-reference-number-her'
237
+
238
+
239
+ ## Purchase Attributes
240
+ request.threeds_v2_purchase_category =
241
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::Purchase::Categories::GOODS
242
+
243
+ ## Recurring
244
+ request.threeds_v2_recurring_expiration_date = '12-12-2024'
245
+ request.threeds_v2_recurring_frequency = 30
246
+
247
+ ## Merchant Risk Attributes
248
+ request.threeds_v2_merchant_risk_shipping_indicator =
249
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::MerchantRisk::ShippingIndicators::SAME_AS_BILLING
250
+ request.threeds_v2_merchant_risk_delivery_timeframe =
251
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::MerchantRisk::DeliveryTimeframes::ANOTHER_DAY
252
+ request.threeds_v2_merchant_risk_reorder_items_indicator =
253
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::MerchantRisk::ReorderItemIndicators::FIRST_TIME
254
+ request.threeds_v2_merchant_risk_pre_order_purchase_indicator =
255
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::MerchantRisk::PreOrderPurchaseIndicators::MERCHANDISE_AVAILABLE
256
+ request.threeds_v2_merchant_risk_pre_order_date = '31-12-2030'
257
+ request.threeds_v2_merchant_risk_gift_card = true
258
+ request.threeds_v2_merchant_risk_gift_card_count = 99
259
+
260
+ ## Card Holder Account Attributes
261
+ request.threeds_v2_card_holder_account_creation_date = '31-12-2022'
262
+ request.threeds_v2_card_holder_account_update_indicator =
263
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::CardHolderAccount::UpdateIndicators::MORE_THAN_60DAYS
264
+ request.threeds_v2_card_holder_account_last_change_date = '31-12-2022'
265
+ request.threeds_v2_card_holder_account_password_change_indicator =
266
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::CardHolderAccount::PasswordChangeIndicators::NO_CHANGE
267
+ request.threeds_v2_card_holder_account_password_change_date = '31-12-2022'
268
+ request.threeds_v2_card_holder_account_shipping_address_usage_indicator =
269
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::CardHolderAccount::ShippingAddressUsageIndicators::MORE_THAN_60DAYS
270
+ request.threeds_v2_card_holder_account_shipping_address_date_first_used = '31-12-2022'
271
+ request.threeds_v2_card_holder_account_transactions_activity_last24_hours = 2
272
+ request.threeds_v2_card_holder_account_transactions_activity_previous_year = 10
273
+ request.threeds_v2_card_holder_account_provision_attempts_last24_hours = 1
274
+ request.threeds_v2_card_holder_account_purchases_count_last6_months = 5
275
+ request.threeds_v2_card_holder_account_suspicious_activity_indicator =
276
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::CardHolderAccount::SuspiciousActivityIndicators::NO_SUSPICIOUS_OBSERVED
277
+ request.threeds_v2_card_holder_account_registration_indicator =
278
+ GenesisRuby::Api::Constants::Transactions::Parameters::Threeds::Version2::CardHolderAccount::RegistrationIndicators::MORE_THAN_60DAYS
279
+ request.threeds_v2_card_holder_account_registration_date = '31-12-2022'
280
+
281
+ end.execute
282
+
283
+ response_3ds_v2 = genesis_3ds_v2.response
284
+
285
+ if response_3ds_v2.approved?
286
+ # Transaction approved no customer action required
287
+ # Test Case: Synchronous 3DSv2 Request with Frictionless flow
288
+ puts response_3ds_v2.response_object
289
+ end
290
+
291
+ if response_3ds_v2.declined? || response_3ds_v2.error?
292
+ # Transaction declined no customer action required
293
+ # Synchronous 3DSv2 Request with Frictionless flow
294
+ puts response_3ds_v2.response_object
295
+ end
296
+
297
+ if response_3ds_v2.pending_async?
298
+ # Additional Actions Required
299
+ response_object_3ds_v2 = response_3ds_v2.response_object
300
+
301
+ if response_object_3ds_v2[:redirect_url]
302
+ # An interaction between consumer and issuer is required
303
+ # 3DSv2 Challenge required
304
+ # 3DSv1 payer authentication required - fallback from 3DSv2 to 3DSv1
305
+ # Test Case: Asynchronous 3DSv2 Request with Challenge flow
306
+ # Test Case: Asynchronous 3DSv2 Request with Fallback flow
307
+ puts response_object_3ds_v2[:redirect_url_type]
308
+ puts response_object_3ds_v2[:redirect_url]
309
+ end
310
+
311
+ if response_object_3ds_v2[:threeds_method_url]
312
+ # 3DS-Method submission is required
313
+ # Generate 3DSv2-Method Signature token used for Threeds Method Continue Request. It's not required when the 3DS-Method continue request is built by the initial request's response object.
314
+ puts GenesisRuby::Utils::Threeds::V2.generate_signature(
315
+ unique_id: response_object_3ds_v2[:unique_id],
316
+ amount: response_object_3ds_v2[:amount],
317
+ timestamp: response_object_3ds_v2[:timestamp].strftime(GenesisRuby::Api::Constants::DateTimeFormats::YYYY_MM_DD_H_I_S_ZULU),
318
+ merchant_password: configuration.password
319
+ )
320
+
321
+ # Execute 3DS-Method Continue Request after initiating the 3DS-Method submission
322
+ # The new request is loaded from the response object of the initial request
323
+ genesis_3ds_v2_continue = GenesisRuby::Api::Requests::Financial::Cards::Threeds::V2::MethodContinue.build_from_response_object(
324
+ configuration,
325
+ genesis_3ds_v2.response.response_object
326
+ )
327
+
328
+ genesis_3ds_v2_continue.execute
329
+
330
+ response_3ds_v2_continue = genesis_3ds_v2_continue.response
331
+
332
+ if response_3ds_v2_continue.approved?
333
+ # Transaction APPROVED no customer action required
334
+ # Test Case: Asynchronous 3DSv2 Request with 3DS-Method and Frictionless flow
335
+ puts response_3ds_v2_continue.response_object
336
+ end
337
+
338
+ if response_3ds_v2_continue.declined? || response_3ds_v2_continue.error?
339
+ # Transaction declined no customer action required
340
+ puts response_3ds_v2_continue.response_object
341
+ end
342
+
343
+ if response_3ds_v2_continue.pending_async?
344
+ # Customer action required
345
+ continue_response_object = response_3ds_v2_continue.response_object
346
+
347
+ if continue_response_object[:redirect_url]
348
+ # Test Case: Asynchronous 3DSv2 Request with 3DS-Method Challenge flow
349
+ # Test Case: Asynchronous 3DSv2 Request with 3DS-Method Fallback flow
350
+ puts continue_response_object[:redirect_url_type]
351
+ puts continue_response_object[:redirect_url]
352
+ end
353
+ end
354
+ end
355
+ end
356
+
357
+ rescue GenesisRuby::Error => error
358
+ puts error.message
359
+ end
360
+ ```
361
+
362
+ </details>
363
+
364
+ ### Standalone ThreedsV2 Method Continue Request.
365
+
366
+ <details>
367
+
368
+ ```ruby
369
+ require 'genesis_ruby'
370
+
371
+ begin
372
+ genesis = GenesisRuby::Genesis.for(config: configuration, request: GenesisRuby::Api::Requests::Financial::Cards::Threeds::V2::MethodContinue) do |request|
373
+ # Amount in minor currency unit
374
+ # If the AMOUNT is not in a minor currency unit then SET the CURRENCY. The AMOUNT will be converted into minor currency unit internally using the CURRENCY property.
375
+ # Ex. amount = 10.00
376
+ # currency = 'EUR'
377
+ # The AMOUNT in that case for signature generation will be 1000
378
+ # Amount is included in the response from the initial request in major currency unit genesis.response.response_object[:amount]
379
+ request.amount = 10.00
380
+
381
+ # If CURRENCY is set, AMOUNT value will be converted into MINOR currency unit
382
+ # If you SET the AMOUNT in MINOR currency unit DO NOT set CURRENCY
383
+ # Currency is included in the response from the initial request in major currency unit genesis.response.response_object[:currency]
384
+ request.currency = 'EUR'
385
+
386
+ # Set only one of the unique_id or url
387
+ # request.url = 'https://staging.gate.emerchantpay.net/threeds/threeds_method/d6a6aa96292e4856d4a352ce634a4335'
388
+ request.transaction_unique_id = 'd6a6aa96292e4856d4a352ce634a4335'
389
+
390
+ # String representation of the timestamp
391
+ # request.transaction_timestamp = genesis.response
392
+ # .response_object[:timestamp].strftime(GenesisRuby::Api::Constants::DateTimeFormats::YYYY_MM_DD_H_I_S_ZULU)
393
+ request.transaction_timestamp = '2020-12-31T23:59:59Z'
394
+ end.execute
395
+
396
+ response = genesis.response
397
+
398
+ if response.approved?
399
+ # Asynchronous 3DSv2 Request with 3DS-Method and Frictionless flow
400
+ # Transaction approved no customer action required
401
+ puts response.response_object
402
+ end
403
+
404
+ if response.pending_async?
405
+ # Customer action required
406
+ response_object = response.response_object
407
+
408
+ if response_object[:redirect_url]
409
+ # Asynchronous 3DSv2 Request with 3DS-Method Challenge flow
410
+ # Asynchronous 3DSv2 Request with 3DS-Method Fallback flow
411
+ puts response_object[:redirect_url_type]
412
+ puts response_object[:redirect_url]
413
+ end
414
+ end
415
+
416
+ rescue GenesisRuby::Error => error
417
+ puts error.message
418
+ end
419
+ ```
420
+
421
+ </details>
422
+
148
423
 
149
424
  ### Recurring
150
425
 
@@ -277,6 +552,88 @@ rescue GenesisRuby::Error => error
277
552
  end
278
553
  ```
279
554
 
555
+ ### Gateway Notification
556
+
557
+ With the asynchronous payment flows like Web Payment Form the Gateway sends the transaction events upon status change on the defined `notification_url`.
558
+ The library contains a Notification module that helps handle the received gateway notification and can provide easy reconciliation execution.
559
+
560
+ #### Initialization
561
+ The notification module requires notification data or any object that responds to `to_h` with a Hash return value.
562
+ For example, with Ruby on Rails you can permit the params and to_h method can be executed without errors:
563
+
564
+ ```ruby
565
+ permitted_params = params.permit(:transaction_id, :terminal_token, :unique_id, :transaction_type, :status, :signature, :amount)
566
+ ```
567
+ A full list of the available params that can be received upon notification can be found [here](https://emerchantpay.github.io/gateway-api-docs/?shell#asynchronous-transactions).
568
+
569
+ ```ruby
570
+ begin
571
+ notification = GenesisRuby::Api::Notification.new configuration, permitted_params
572
+
573
+ # Helper methods
574
+ notification.api_notification?
575
+ notification.wpf_notification?
576
+ notification.unique_id
577
+
578
+ # Executes Gateway Transaction Reconciliation
579
+ # This provides the latest information on the transaction from the Gateway
580
+ notification.reconcile
581
+
582
+ # Provides information if the given reconcile response contains transaction information
583
+ notification.transaction_reconciliation?
584
+
585
+ # Get the Reconcile Response Object in Hash data structure
586
+ notification.reconciliation.response_object
587
+
588
+ # Generate response document expected from the Gateway
589
+ notification.generate_response
590
+ rescue GenesisRuby::ParameterError => error
591
+ puts error.message
592
+ end
593
+ ```
594
+
595
+ #### Reconcile
596
+ Minimum required data for execution of `reconcile`:
597
+
598
+ ```ruby
599
+ {
600
+ unique_id: 'unique_id received from the gateway in the notification params',
601
+ signature: 'the signature received in the notification'
602
+ }
603
+ ```
604
+
605
+ If the signature can't be verified Genesis::Ruby::ParameterError will be raised.
606
+
607
+ #### Helpers
608
+ `notification.reconcile` returns GenesisRuby::Api::Response. The response object can be accessed via `notification.reconciliation`.
609
+ The reconciliation object has every helper that [Response](#response-helpers) contains like checking the status with `error?`, `approved?`, etc.
610
+
611
+ For checking if the `reconciliation.response_object` is a successful transaction response you can use `notification.transaction_reconciliation?`
612
+
613
+ #### Errors
614
+ Upon wrong data like configuration terminal token, `reconciliation.response_object` can be similar:
615
+
616
+ ```ruby
617
+ {
618
+ status: 'error',
619
+ code: '220',
620
+ message: 'Reconcile request failed, please contact support!',
621
+ technical_message: 'Invalid Terminal'
622
+ }
623
+ ```
624
+
625
+ #### Respond to the Gateway
626
+ When receiving the notification, you are required to render an xml page containing the transaction’s unique id so that the gateway knows that you have accepted the notification.
627
+ If the XML is not delivered, the notification is sent periodically until the XML is received.
628
+
629
+ `GenesisRuby::Api::Notification` provides helper method for generation of the expected xml content. The Gateway expects a response with:
630
+ * Status 200
631
+ * Content Type `text/xml`
632
+
633
+ ```ruby
634
+ notification.generate_response
635
+ ```
636
+
280
637
  ### Response Helpers
281
638
 
282
639
  #### Sates
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.1.2
@@ -16,7 +16,7 @@ module GenesisRuby
16
16
  DD_MM_YYYY_L_SLASHES = '%d/%m/%Y'.freeze
17
17
 
18
18
  # Zulu timestamp
19
- YYYY_MM_DD_H_I_S_ZULU = '%Y-%m-%dT%H:%M:%S%z'.freeze
19
+ YYYY_MM_DD_H_I_S_ZULU = '%Y-%m-%dT%H:%M:%SZ'.freeze
20
20
 
21
21
  # Modified Zulu timestamp
22
22
  YYYY_MM_DD_H_I_S = '%Y-%m-%d %H:%M:%S'.freeze
@@ -0,0 +1,156 @@
1
+ require 'cgi'
2
+ require 'digest'
3
+
4
+ module GenesisRuby
5
+ module Api
6
+ # Gateway Notification handler
7
+ class Notification
8
+
9
+ attr_reader :unique_id, :notification, :reconciliation
10
+
11
+ # Signature algorithms
12
+ SHA1_SIGNATURE_TYPE = 'SHA1'.freeze
13
+ SHA256_SIGNATURE_TYPE = 'SHA256'.freeze
14
+ SHA512_SIGNATURE_TYPE = 'SHA512'.freeze
15
+
16
+ # Possible request/response identifier fields
17
+ API_UNIQUE_FIELD = 'unique_id'.freeze
18
+ WPF_UNIQUE_FIELD = 'wpf_unique_id'.freeze
19
+ KYC_UNIQUE_FIELD = 'reference_id'.freeze
20
+
21
+ # Class constructor
22
+ def initialize(configuration, data)
23
+ @configuration = configuration
24
+
25
+ parse_notification data
26
+ end
27
+
28
+ # Check if the given data is API notification
29
+ def api_notification?
30
+ notification.key? :unique_id
31
+ end
32
+
33
+ # Check if the given data is Web Payment Form notification
34
+ def wpf_notification?
35
+ notification.key? :wpf_unique_id
36
+ end
37
+
38
+ # Check if the given data is Know Your Customer notification
39
+ def kyc_notification?
40
+ notification.key? :reference_id
41
+ end
42
+
43
+ # Generates XML document expected from the Gateway
44
+ def generate_response
45
+ response = {
46
+ notification_echo: [[fetch_response_unique_field, unique_id]].to_h
47
+ }
48
+
49
+ builder = GenesisRuby::Builder.new Builder::XML
50
+ builder.parse_structure response
51
+
52
+ builder.document
53
+ end
54
+
55
+ # Execute Reconcile API Request
56
+ def reconcile
57
+ request_object = fetch_reconciliation_request
58
+
59
+ begin
60
+ genesis = Genesis.for config: @configuration, request: request_object do |req|
61
+ req.unique_id = unique_id
62
+ end.execute
63
+
64
+ @reconciliation = genesis.response
65
+ rescue Error
66
+ @reconciliation = nil
67
+ end
68
+ end
69
+
70
+ # Determinate if the executed reconciliation response contains transaction data
71
+ def transaction_reconciliation?
72
+ response_object = reconciliation&.response_object
73
+
74
+ return false if response_object.nil?
75
+
76
+ response_object.key?(:unique_id) && response_object.key?(:transaction_id) && response_object.key?(:status)
77
+ end
78
+
79
+ private
80
+
81
+ # Parse the given notification data
82
+ def parse_notification(data, authenticate: true)
83
+ @notification = parse_raw_data data
84
+ @unique_id = fetch_unique_id
85
+
86
+ raise ParameterError, 'Invalid Genesis Notification!' if authenticate && !authentic?
87
+ end
88
+
89
+ # Parse the given raw data
90
+ def parse_raw_data(data)
91
+ normalize_data data.to_h
92
+ rescue StandardError => e
93
+ raise ParameterError, "Given notification data doesn't respond to_h! #{e.message}"
94
+ end
95
+
96
+ # Normalize the given notification data
97
+ def normalize_data(data)
98
+ data.map { |key, value| [CGI.unescape(key.to_s).to_sym, CGI.unescape(value.strip)] }.to_h
99
+ end
100
+
101
+ # Assign the unique_id property based on the given notification data
102
+ def fetch_unique_id
103
+ return @notification[API_UNIQUE_FIELD.to_sym] if api_notification?
104
+ return @notification[WPF_UNIQUE_FIELD.to_sym] if wpf_notification?
105
+ return @notification[KYC_UNIQUE_FIELD.to_sym] if kyc_notification?
106
+
107
+ nil
108
+ end
109
+
110
+ # Validate the Notification signature
111
+ def authentic?
112
+ if unique_id.nil? || notification[:signature].nil?
113
+ raise ParameterError, 'Missing Notification attributes: unique_id or signature'
114
+ end
115
+
116
+ generated_signature = fetch_signature_generator.hexdigest "#{unique_id}#{@configuration.password}"
117
+
118
+ notification[:signature] == generated_signature
119
+ end
120
+
121
+ # Fetch the hash generator based on the hash type
122
+ def fetch_signature_generator
123
+ Digest.const_get fetch_hash_type
124
+ end
125
+
126
+ # Fetch the hash algorithm by the given signature length
127
+ def fetch_hash_type
128
+ case notification[:signature].length
129
+ when 40 then SHA1_SIGNATURE_TYPE
130
+ when 64 then SHA256_SIGNATURE_TYPE
131
+ when 128 then SHA512_SIGNATURE_TYPE
132
+ else
133
+ SHA1_SIGNATURE_TYPE
134
+ end
135
+ end
136
+
137
+ # Fetches the response field identifier name witch is expected from the Gateway
138
+ def fetch_response_unique_field
139
+ return API_UNIQUE_FIELD if api_notification?
140
+ return WPF_UNIQUE_FIELD if wpf_notification?
141
+ return KYC_UNIQUE_FIELD if kyc_notification?
142
+
143
+ raise ParameterError, 'Unknown notification type!'
144
+ end
145
+
146
+ # Fetch the Reconcile object
147
+ def fetch_reconciliation_request
148
+ return Requests::NonFinancial::Reconcile::Transaction if api_notification?
149
+ return Requests::Wpf::Reconcile if wpf_notification?
150
+
151
+ raise ParameterError, 'Unsupported notification type for Reconciliation'
152
+ end
153
+
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,145 @@
1
+ require 'genesis_ruby/utils/threeds/v2'
2
+
3
+ module GenesisRuby
4
+ module Api
5
+ module Requests
6
+ module Financial
7
+ module Cards
8
+ module Threeds
9
+ module V2
10
+ # Method Continue API request
11
+ class MethodContinue < Request
12
+
13
+ include Mixins::Requests::Financial::PaymentAttributes
14
+ include Mixins::Requests::RestrictedSetter
15
+
16
+ attr_writer :url, :transaction_unique_id, :signature
17
+
18
+ class << self
19
+
20
+ def build_from_response_object(configuration, response_object)
21
+ if response_object[:threeds_method_continue_url].nil? ||
22
+ response_object[:unique_id].nil? ||
23
+ response_object[:amount].nil? || response_object[:currency].nil? ||
24
+ response_object[:timestamp].nil? || !response_object[:timestamp].is_a?(DateTime)
25
+
26
+ raise ParameterError, 'Response object is incomplete or required attributes are missing!'
27
+ end
28
+
29
+ build_method_continue_request configuration, response_object
30
+ end
31
+
32
+ private
33
+
34
+ # Build Method Continue Request
35
+ def build_method_continue_request(configuration, response_object)
36
+ request = new configuration
37
+
38
+ request.url = response_object[:threeds_method_continue_url]
39
+ request.transaction_unique_id = response_object[:unique_id]
40
+ request.amount = response_object[:amount]
41
+ request.currency = response_object[:currency]
42
+ request.transaction_timestamp = response_object[:timestamp].strftime(
43
+ Constants::DateTimeFormats::YYYY_MM_DD_H_I_S_ZULU
44
+ )
45
+
46
+ GenesisRuby::Genesis.new configuration, request
47
+ end
48
+
49
+ end
50
+
51
+ # Override default constructor with FORM Builder Interface
52
+ def initialize(configuration, _builder_interface = nil)
53
+ super(configuration, Builder::FORM)
54
+ end
55
+
56
+ # A link between the customer's browser and the card issuer must be opened with a hidden iframe
57
+ def url
58
+ return @url = generate_endpoint_url if @url.nil?
59
+
60
+ @url
61
+ end
62
+
63
+ # Equivalent to the value of the unique_id,
64
+ # received from the response of the initial transaction request
65
+ def transaction_unique_id
66
+ return extract_unique_id_from_url @url if @transaction_unique_id.nil?
67
+
68
+ @transaction_unique_id
69
+ end
70
+
71
+ # SHA512 of а concatenated string (unique_id, amount, timestamp, merchant_api_password)
72
+ def signature
73
+ return @signature unless @signature.nil?
74
+
75
+ payment_amount = @currency.nil? ? amount : transform_amount(amount, currency)
76
+
77
+ Utils::Threeds::V2.generate_signature(
78
+ unique_id: transaction_unique_id,
79
+ amount: payment_amount,
80
+ timestamp: transaction_timestamp,
81
+ merchant_password: @configuration.password
82
+ )
83
+ end
84
+
85
+ # The timestamp from the initial transaction response
86
+ def transaction_timestamp
87
+ @transaction_timestamp&.strftime(
88
+ GenesisRuby::Api::Constants::DateTimeFormats::YYYY_MM_DD_H_I_S_ZULU
89
+ )
90
+ end
91
+
92
+ # The timestamp from the initial transaction response
93
+ def transaction_timestamp=(value)
94
+ parse_date attribute: __method__, value: value, allow_empty: true
95
+ end
96
+
97
+ protected
98
+
99
+ # Init Method Continue Request configuration
100
+ def init_configuration
101
+ init_form_configuration
102
+
103
+ @api_config.type = Request::METHOD_PUT
104
+
105
+ init_api_gateway_configuration request_path: 'threeds/threeds_method/:unique_id', include_token: false
106
+ end
107
+
108
+ # Build correct endpoint url during runtime
109
+ def process_request_parameters
110
+ @api_config.url = url
111
+
112
+ super
113
+ end
114
+
115
+ # Method Continue Request structure
116
+ def populate_structure
117
+ @tree_structure = {
118
+ unique_id: transaction_unique_id,
119
+ signature: signature
120
+ }
121
+ end
122
+
123
+ private
124
+
125
+ # Fills the Unique Id in the endpoint URL
126
+ def generate_endpoint_url
127
+ @api_config.url&.sub! ':unique_id', transaction_unique_id.to_s
128
+ end
129
+
130
+ # Extract the Unique Id
131
+ def extract_unique_id_from_url(url)
132
+ uri = URI.parse url || ''
133
+ exploded_path = uri.path&.split('/')
134
+
135
+ exploded_path.last
136
+ end
137
+
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -57,6 +57,8 @@ module GenesisRuby
57
57
  @parser = GenesisRuby::Parsers.new(GenesisRuby::Parser::JSON) if network.json?
58
58
  @parser = GenesisRuby::Parser.new(GenesisRuby::Parser::XML) if network.xml?
59
59
 
60
+ raise NetworkError, network.server_message if @parser.nil? || @response_raw.empty?
61
+
60
62
  @parser.skip_root_node if @request_api_config[:parser_skip_root_node]
61
63
 
62
64
  @parser
@@ -1,4 +1,5 @@
1
1
  require 'genesis_ruby/builders/xml'
2
+ require 'genesis_ruby/builders/form'
2
3
  require 'genesis_ruby/errors/builder_error'
3
4
 
4
5
  module GenesisRuby
@@ -17,8 +18,8 @@ module GenesisRuby
17
18
  # Initialize the Builder Interface based on the Request requirements
18
19
  def initialize(request_interface)
19
20
  case request_interface
20
- when XML
21
- @builder_context = GenesisRuby::Builders::Xml.new
21
+ when XML then @builder_context = Builders::Xml.new
22
+ when FORM then @builder_context = Builders::Form.new
22
23
  else
23
24
  raise GenesisRuby::BuilderError, 'Invalid Builder interface!'
24
25
  end
@@ -0,0 +1,44 @@
1
+ require 'genesis_ruby/builders/base'
2
+ require 'uri'
3
+
4
+ module GenesisRuby
5
+ module Builders
6
+ # XML, Nokogiri Builder Implementation
7
+ class Form < Base
8
+
9
+ # Initialize Nokogiri XML Builder
10
+ def initialize
11
+ @builder = URI
12
+ super
13
+ end
14
+
15
+ # Generated HTTP Query Document
16
+ def output
17
+ document
18
+ end
19
+
20
+ def populate_nodes(structure)
21
+ self.document = @builder.encode_www_form(transform_structure(structure))
22
+ end
23
+
24
+ private
25
+
26
+ attr_accessor :buffer, :document
27
+
28
+ # Transform structure from hash to array
29
+ # { key: 'value' } -> [[key, value]]
30
+ def transform_structure(structure)
31
+ self.buffer = []
32
+ structure.each { |key, value| add_buffer [key, value] }
33
+
34
+ buffer
35
+ end
36
+
37
+ # Fill up parameters
38
+ def add_buffer(value)
39
+ @buffer.push(value)
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -18,6 +18,7 @@ Dir["#{File.dirname(__FILE__)}/api/mixins/**/*_attributes.rb"].sort.each { |file
18
18
  require 'genesis_ruby/api/requests/base/financial'
19
19
  require 'genesis_ruby/api/requests/base/financials/credit_card'
20
20
  require 'genesis_ruby/api/requests/base/reference'
21
+ require 'genesis_ruby/api/notification'
21
22
 
22
23
  # Load Financial and Non Financial API Requests
23
24
  Dir["#{File.dirname(__FILE__)}/api/requests/*financial/**/*.rb"].sort.each { |file| require file }
@@ -25,6 +25,16 @@ module GenesisRuby
25
25
  raise NotImplementedError, 'Execute method must be implemented'
26
26
  end
27
27
 
28
+ # Whether the response is an error (HTTP Code != 200)
29
+ def error?
30
+ raise NotImplementedError, 'Error? method must be implemented'
31
+ end
32
+
33
+ # Response server message
34
+ def server_message
35
+ raise NotImplementedError, 'Server Message method must be implemented'
36
+ end
37
+
28
38
  end
29
39
  end
30
40
  end
@@ -25,14 +25,13 @@ module GenesisRuby
25
25
  def execute
26
26
  raise NetworkError, 'Request is not initialized' unless @request
27
27
 
28
- begin
29
- # TODO: Use GenesisRuby::Api::Request::METHOD_XXX constants for sending GET and PUT request_data.type
30
- @response = @request.post(path, request_data.body, headers)
31
- rescue StandardError => e
32
- raise NetworkError, "Network error raised by #{e.class.name}: #{e.message}"
33
- ensure
34
- # Close the request
35
- @request.finish
28
+ safe_execute do
29
+ case request_data.type
30
+ when Api::Request::METHOD_POST then @response = @request.post path, request_data.body, headers
31
+ when Api::Request::METHOD_PUT then @response = @request.put path, request_data.body, headers
32
+ else
33
+ raise 'Invalid Request Type!'
34
+ end
36
35
  end
37
36
  end
38
37
 
@@ -46,6 +45,19 @@ module GenesisRuby
46
45
  @response_headers ||= @response ? @response.each_header.to_h : {}
47
46
  end
48
47
 
48
+ # Whether the response is an error (HTTP Code != 200)
49
+ def error?
50
+ !@response.is_a? Net::HTTPSuccess
51
+ end
52
+
53
+ # Response server message
54
+ def server_message
55
+ message = status
56
+ message += " #{@response.message}" if @response.message
57
+
58
+ message
59
+ end
60
+
49
61
  private
50
62
 
51
63
  attr_reader :uri, :request, :response, :request_data
@@ -86,6 +98,16 @@ module GenesisRuby
86
98
  }
87
99
  end
88
100
 
101
+ # Safe Request execution
102
+ def safe_execute(&block)
103
+ block.call
104
+ rescue StandardError => e
105
+ raise NetworkError, "Network error raised by #{e.class.name}: #{e.message}"
106
+ ensure
107
+ # Close the request
108
+ @request.finish
109
+ end
110
+
89
111
  end
90
112
  end
91
113
  end
@@ -9,6 +9,7 @@ module GenesisRuby
9
9
 
10
10
  XML_HEADER = 'application/xml'.freeze
11
11
  JSON_HEADER = 'application/json'.freeze
12
+ HTML_HEADER = 'text/html'.freeze
12
13
 
13
14
  # Base constructor
14
15
  def initialize(configuration)
@@ -27,6 +28,16 @@ module GenesisRuby
27
28
  @context.response_headers
28
29
  end
29
30
 
31
+ # Whether returned response is an error response
32
+ def error?
33
+ @context.error?
34
+ end
35
+
36
+ # Returns the server message
37
+ def server_message
38
+ @context.server_message
39
+ end
40
+
30
41
  # Send the request
31
42
  def send_request
32
43
  @context.execute
@@ -47,6 +58,11 @@ module GenesisRuby
47
58
  raise NotImplementedError, 'Is JSON method must be implemented'
48
59
  end
49
60
 
61
+ # Every child defines is HTML Response Type
62
+ def html?
63
+ raise NotImplementedError, 'Is HTML method must be implemented'
64
+ end
65
+
50
66
  protected
51
67
 
52
68
  # GenesisRuby::Configuration, Adapter Context, Network Adapter Config mapper
@@ -16,6 +16,11 @@ module GenesisRuby
16
16
  response_headers['content-type'].downcase.include?(BaseNetwork::JSON_HEADER)
17
17
  end
18
18
 
19
+ # HTML Response Type
20
+ def html?
21
+ response_headers['content-type'].downcase.include?(BaseNetwork::HTML_HEADER)
22
+ end
23
+
19
24
  protected
20
25
 
21
26
  # Adapter Initialization
@@ -22,13 +22,12 @@ module GenesisRuby
22
22
 
23
23
  # Retrieve the Request data format that must be used as Content-Type header
24
24
  def fetch_content_type(data_format)
25
- # TODO: Builder Constants
26
25
  case data_format
27
- when 'xml'
26
+ when Builder::XML
28
27
  'text/xml'
29
- when 'json'
28
+ when Builder::JSON
30
29
  'application/json'
31
- when 'form'
30
+ when Builder::FORM
32
31
  'application/x-www-form-urlencoded'
33
32
  else
34
33
  raise InvalidArgumentError, 'Invalid request format type. Allowed are XML, JSON and FORM'
@@ -0,0 +1,21 @@
1
+ require 'digest'
2
+
3
+ module GenesisRuby
4
+ module Utils
5
+ module Threeds
6
+ # Threeds V2 Utils
7
+ class V2
8
+
9
+ class << self
10
+
11
+ # Generate 3DSV2 signature
12
+ def generate_signature(unique_id:, amount:, timestamp:, merchant_password:)
13
+ Digest::SHA512.hexdigest "#{unique_id}#{amount}#{timestamp}#{merchant_password}"
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,5 +1,5 @@
1
1
  module GenesisRuby
2
2
 
3
- VERSION = '0.1.1'.freeze
3
+ VERSION = '0.1.2'.freeze
4
4
 
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: genesis_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - emerchantpay Ltd.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-10-09 00:00:00.000000000 Z
11
+ date: 2023-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-http
@@ -292,6 +292,7 @@ files:
292
292
  - lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/sdk.rb
293
293
  - lib/genesis_ruby/api/mixins/requests/financial/threeds/version2/wpf_attributes.rb
294
294
  - lib/genesis_ruby/api/mixins/requests/restricted_setter.rb
295
+ - lib/genesis_ruby/api/notification.rb
295
296
  - lib/genesis_ruby/api/request.rb
296
297
  - lib/genesis_ruby/api/requests/base/financial.rb
297
298
  - lib/genesis_ruby/api/requests/base/financials/credit_card.rb
@@ -301,6 +302,7 @@ files:
301
302
  - lib/genesis_ruby/api/requests/financial/cards/authorize3d.rb
302
303
  - lib/genesis_ruby/api/requests/financial/cards/sale.rb
303
304
  - lib/genesis_ruby/api/requests/financial/cards/sale3d.rb
305
+ - lib/genesis_ruby/api/requests/financial/cards/threeds/v2/method_continue.rb
304
306
  - lib/genesis_ruby/api/requests/financial/refund.rb
305
307
  - lib/genesis_ruby/api/requests/financial/void.rb
306
308
  - lib/genesis_ruby/api/requests/non_financial/reconcile/date_range.rb
@@ -310,6 +312,7 @@ files:
310
312
  - lib/genesis_ruby/api/response.rb
311
313
  - lib/genesis_ruby/builder.rb
312
314
  - lib/genesis_ruby/builders/base.rb
315
+ - lib/genesis_ruby/builders/form.rb
313
316
  - lib/genesis_ruby/builders/xml.rb
314
317
  - lib/genesis_ruby/configuration.rb
315
318
  - lib/genesis_ruby/connection.rb
@@ -345,6 +348,7 @@ files:
345
348
  - lib/genesis_ruby/utils/options/api_config.rb
346
349
  - lib/genesis_ruby/utils/options/base.rb
347
350
  - lib/genesis_ruby/utils/options/network_adapter_config.rb
351
+ - lib/genesis_ruby/utils/threeds/v2.rb
348
352
  - lib/genesis_ruby/utils/transactions/financial_types.rb
349
353
  - lib/genesis_ruby/utils/transactions/references/capturable_types.rb
350
354
  - lib/genesis_ruby/utils/transactions/references/refundable_types.rb
@@ -374,7 +378,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
374
378
  - !ruby/object:Gem::Version
375
379
  version: '0'
376
380
  requirements: []
377
- rubygems_version: 3.4.1
381
+ rubygems_version: 3.1.2
378
382
  signing_key:
379
383
  specification_version: 4
380
384
  summary: Ruby Client for Genesis Payment Processing Gateway