genesis_ruby 0.1.1 → 0.1.2

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 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