univapay-client-sdk 1.2.0 → 1.2.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 +4 -4
- data/README.md +3 -3
- data/lib/univapay_client_sdk/apis/base_api.rb +1 -1
- data/lib/univapay_client_sdk/apis/webhooks_api.rb +0 -68
- data/lib/univapay_client_sdk/extensions.rb +48 -0
- data/lib/univapay_client_sdk/models/merchant.rb +1 -1
- data/lib/univapay_client_sdk/models/merchant_webhook_configuration.rb +1 -1
- data/lib/univapay_client_sdk/models/store.rb +2 -2
- data/test/apis/test_webhooks_api.rb +0 -30
- data/test/test_client_get_charge.rb +154 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3dad9df2de9a66d2b1dd4fba909ae6bda94459bef6d270803e1411b0a98c71ab
|
|
4
|
+
data.tar.gz: eaf97de06378b70bb1f6a9f1a1e2748688350222a5f7c8566ca064b451492b42
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 06062d75f1be86e35e936c5a9745809ecb50e215fb2beeb5bfa5973885a577e12f113545f07399a52276d006c53a53cf124794c0dde000444b2cec20822632af
|
|
7
|
+
data.tar.gz: 19d93fc64e65334d9917cb2449a96c26d0e8be08d8b132cb90a309cf5aa096ecb51a49dbf1f36b8456f61033cf0165235096125e3793d778d0447461f8b6cbb2
|
data/README.md
CHANGED
|
@@ -36,16 +36,16 @@ We will assume that all requests are going to originate from a backend server th
|
|
|
36
36
|
Install the gem from the command line:
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
gem install univapay-client-sdk -v 1.2.
|
|
39
|
+
gem install univapay-client-sdk -v 1.2.2
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Or add the gem to your Gemfile and run `bundle`:
|
|
43
43
|
|
|
44
44
|
```ruby
|
|
45
|
-
gem 'univapay-client-sdk', '1.2.
|
|
45
|
+
gem 'univapay-client-sdk', '1.2.2'
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
For additional gem details, see the [RubyGems page for the univapay-client-sdk gem](https://rubygems.org/gems/univapay-client-sdk/versions/1.2.
|
|
48
|
+
For additional gem details, see the [RubyGems page for the univapay-client-sdk gem](https://rubygems.org/gems/univapay-client-sdk/versions/1.2.2).
|
|
49
49
|
|
|
50
50
|
## IRB Console Usage
|
|
51
51
|
|
|
@@ -10,7 +10,7 @@ module UnivapayClientSdk
|
|
|
10
10
|
attr_accessor :config, :http_call_back
|
|
11
11
|
|
|
12
12
|
def self.user_agent
|
|
13
|
-
'Ruby-SDK/1.2.
|
|
13
|
+
'Ruby-SDK/1.2.2 (OS: {os-info}, Engine: {engine}/{engine-version})'
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def self.user_agent_parameters
|
|
@@ -391,73 +391,5 @@ module UnivapayClientSdk
|
|
|
391
391
|
APIException))
|
|
392
392
|
.execute
|
|
393
393
|
end
|
|
394
|
-
|
|
395
|
-
# Re-sends the webhook payload for a previously delivered (or failed) event.
|
|
396
|
-
# Returns 202 Accepted immediately; delivery is asynchronous.
|
|
397
|
-
# @param [UUID | String] store_id Required parameter: The unique identifier
|
|
398
|
-
# of the store.
|
|
399
|
-
# @param [UUID | String] id Required parameter: The unique identifier of the
|
|
400
|
-
# resource.
|
|
401
|
-
# @param [UUID | String] event_id Required parameter: The unique identifier
|
|
402
|
-
# of the webhook event.
|
|
403
|
-
# @param [String] idempotency_key Optional parameter: An optional
|
|
404
|
-
# idempotency key to prevent double charges and duplicate operations. We
|
|
405
|
-
# recommend a randomly generated UUID (v4).
|
|
406
|
-
# @return [ApiResponse] Complete http response with raw body and status code.
|
|
407
|
-
def redeliver_webhook_event(store_id,
|
|
408
|
-
id,
|
|
409
|
-
event_id,
|
|
410
|
-
idempotency_key: nil)
|
|
411
|
-
@api_call
|
|
412
|
-
.request(new_request_builder(HttpMethodEnum::POST,
|
|
413
|
-
'/stores/{storeId}/webhooks/{id}/events/{eventId}/redeliver',
|
|
414
|
-
Server::DEFAULT)
|
|
415
|
-
.template_param(new_parameter(store_id, key: 'storeId')
|
|
416
|
-
.is_required(true)
|
|
417
|
-
.should_encode(true))
|
|
418
|
-
.template_param(new_parameter(id, key: 'id')
|
|
419
|
-
.is_required(true)
|
|
420
|
-
.should_encode(true))
|
|
421
|
-
.template_param(new_parameter(event_id, key: 'eventId')
|
|
422
|
-
.is_required(true)
|
|
423
|
-
.should_encode(true))
|
|
424
|
-
.header_param(new_parameter(idempotency_key, key: 'Idempotency-Key'))
|
|
425
|
-
.header_param(new_parameter('application/json', key: 'accept'))
|
|
426
|
-
.auth(Single.new('JWT_TOKEN')))
|
|
427
|
-
.response(new_response_handler
|
|
428
|
-
.deserializer(APIHelper.method(:json_deserialize))
|
|
429
|
-
.is_api_response(true)
|
|
430
|
-
.local_error_template('401',
|
|
431
|
-
'HTTP 401 Unauthorized: {$response.body#/code}',
|
|
432
|
-
ApiErrorException)
|
|
433
|
-
.local_error_template('403',
|
|
434
|
-
'HTTP 403 Forbidden: {$response.body#/code}',
|
|
435
|
-
ApiErrorException)
|
|
436
|
-
.local_error_template('404',
|
|
437
|
-
'HTTP 404 Not Found: {$response.body#/code}',
|
|
438
|
-
ApiErrorException)
|
|
439
|
-
.local_error_template('400',
|
|
440
|
-
'HTTP 400 Bad Request: {$response.body#/code}',
|
|
441
|
-
APIException)
|
|
442
|
-
.local_error_template('409',
|
|
443
|
-
'HTTP 409 Conflict: {$response.body#/code}',
|
|
444
|
-
APIException)
|
|
445
|
-
.local_error_template('429',
|
|
446
|
-
'HTTP 429 Rate Limited: {$response.body#/code}',
|
|
447
|
-
APIException)
|
|
448
|
-
.local_error_template('500',
|
|
449
|
-
'HTTP 500 Server Error: {$response.body#/code}',
|
|
450
|
-
APIException)
|
|
451
|
-
.local_error_template('503',
|
|
452
|
-
'HTTP 503 Unavailable: {$response.body#/code}',
|
|
453
|
-
APIException)
|
|
454
|
-
.local_error_template('504',
|
|
455
|
-
'HTTP 504 Timeout: {$response.body#/code}',
|
|
456
|
-
APIException)
|
|
457
|
-
.local_error_template('default',
|
|
458
|
-
'HTTP {$statusCode}: {$response.body#/code}',
|
|
459
|
-
APIException))
|
|
460
|
-
.execute
|
|
461
|
-
end
|
|
462
394
|
end
|
|
463
395
|
end
|
|
@@ -230,6 +230,30 @@ module UnivapayClientSdk
|
|
|
230
230
|
value = payload[claim]
|
|
231
231
|
value.is_a?(String) && UUID_PATTERN.match?(value) ? value : nil
|
|
232
232
|
end
|
|
233
|
+
|
|
234
|
+
# Asserts that a store id was resolvable from the configured app token.
|
|
235
|
+
#
|
|
236
|
+
# Named and shaped to match `AppJwt.requireStoreId` in the Java and PHP
|
|
237
|
+
# SDKs, `RequireStoreId` in the C# one and `requireStoreId` in the
|
|
238
|
+
# TypeScript and Python ones, so the guard reads the same in every language.
|
|
239
|
+
# Returning the id (rather than fetching on the caller's behalf) keeps the
|
|
240
|
+
# check ahead of every side effect.
|
|
241
|
+
#
|
|
242
|
+
# @param store_id [String, nil] The store id read from the token, or nil.
|
|
243
|
+
# @return [String] store_id, when it is present.
|
|
244
|
+
# @raise [RuntimeError] When it is not -- before any request is built, so a
|
|
245
|
+
# missing id can never reach the request path.
|
|
246
|
+
def self.require_store_id(store_id)
|
|
247
|
+
return store_id unless store_id.nil?
|
|
248
|
+
|
|
249
|
+
# Says nothing about the token itself: the credential and its claims must
|
|
250
|
+
# never reach an error message or a log. A merchant-level token reaching
|
|
251
|
+
# here is not a broken token -- it is simply not scoped to a store.
|
|
252
|
+
raise 'get_charge(charge_id) requires a store-level App Token: the ' \
|
|
253
|
+
'configured token carries no usable "store_id" claim. Use a ' \
|
|
254
|
+
'store-level App Token, or call get_charge(store_id, charge_id) ' \
|
|
255
|
+
'on charges with an explicit store id.'
|
|
256
|
+
end
|
|
233
257
|
end
|
|
234
258
|
|
|
235
259
|
# Reopened onto the generated client class to expose the app token's context.
|
|
@@ -259,6 +283,30 @@ module UnivapayClientSdk
|
|
|
259
283
|
AppJwt.read_uuid_claim(jwt_token_or_nil, 'store_id')
|
|
260
284
|
end
|
|
261
285
|
|
|
286
|
+
# Retrieves a charge without being given a store id.
|
|
287
|
+
#
|
|
288
|
+
# `/stores/{storeId}/charges/{id}` needs a store, which callers would
|
|
289
|
+
# otherwise have to persist alongside every charge id -- but a store-level
|
|
290
|
+
# app token already carries one, so this reads it from the configured token
|
|
291
|
+
# and then behaves exactly like `charges.get_charge`.
|
|
292
|
+
#
|
|
293
|
+
# Raises before any request is built when the configured token carries no
|
|
294
|
+
# `store_id` claim -- a merchant-level token, or none at all. With a
|
|
295
|
+
# merchant-level token, resolve the store yourself (see `stores`) and call
|
|
296
|
+
# `charges.get_charge` with it.
|
|
297
|
+
#
|
|
298
|
+
# @param charge_id [String] The unique identifier of the charge.
|
|
299
|
+
# @param polling [Boolean, nil] If true, instructs the API to internally
|
|
300
|
+
# poll the charge status until it leaves 'pending'.
|
|
301
|
+
# @return The controller's response, untouched.
|
|
302
|
+
# @raise [RuntimeError] When the token carries no usable `store_id` claim.
|
|
303
|
+
def get_charge(charge_id, polling: nil)
|
|
304
|
+
# Guard first, controller second: nothing is built and no request is made
|
|
305
|
+
# until a store id is known to exist.
|
|
306
|
+
store_id = AppJwt.require_store_id(get_current_store_id)
|
|
307
|
+
charges.get_charge(store_id, charge_id, polling: polling)
|
|
308
|
+
end
|
|
309
|
+
|
|
262
310
|
private
|
|
263
311
|
|
|
264
312
|
# The configured JWT, or nil when no credentials are set.
|
|
@@ -39,7 +39,7 @@ module UnivapayClientSdk
|
|
|
39
39
|
# @return [TrueClass | FalseClass]
|
|
40
40
|
attr_accessor :verified
|
|
41
41
|
|
|
42
|
-
# Merchant configuration snapshot serialized by
|
|
42
|
+
# Merchant configuration snapshot as serialized by the backend.
|
|
43
43
|
# @return [MerchantWebhookConfiguration]
|
|
44
44
|
attr_accessor :configuration
|
|
45
45
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# by APIMATIC v3.0 ( https://www.apimatic.io ).
|
|
5
5
|
|
|
6
6
|
module UnivapayClientSdk
|
|
7
|
-
# Merchant configuration object serialized by
|
|
7
|
+
# Merchant configuration object as serialized by the backend.
|
|
8
8
|
class MerchantWebhookConfiguration < BaseModel
|
|
9
9
|
SKIP = Object.new
|
|
10
10
|
private_constant :SKIP
|
|
@@ -24,8 +24,8 @@ module UnivapayClientSdk
|
|
|
24
24
|
# @return [DateTime]
|
|
25
25
|
attr_accessor :created_on
|
|
26
26
|
|
|
27
|
-
# Store-scoped configuration snapshot serialized by
|
|
28
|
-
#
|
|
27
|
+
# Store-scoped configuration snapshot as serialized by the backend. It uses
|
|
28
|
+
# the same flattened serializer as merchant configuration, but omits
|
|
29
29
|
# `transfer_schedule`.
|
|
30
30
|
# @return [MerchantWebhookConfiguration]
|
|
31
31
|
attr_accessor :configuration
|
|
@@ -212,34 +212,4 @@ class WebhooksApiTest < ApiTestBase
|
|
|
212
212
|
assert(ComparisonHelper.match_body(expected_body, received_body))
|
|
213
213
|
end
|
|
214
214
|
|
|
215
|
-
# Re-sends the webhook payload for a previously delivered (or failed) event. Returns 202 Accepted immediately; delivery is asynchronous.
|
|
216
|
-
def test_redeliver_webhook_event
|
|
217
|
-
# Parameters for the API call
|
|
218
|
-
store_id = '0cab399b-5621-425b-993b-f8507eba1e78'
|
|
219
|
-
id = 'c4e87129-cad4-47fb-8ded-b4c0a4ae0dd4'
|
|
220
|
-
event_id = 'e1f2a3b4-c5d6-7890-efab-123456789cde'
|
|
221
|
-
idempotency_key = 'f64be872-353d-4c3c-84cb-3dc617fe89f7'
|
|
222
|
-
|
|
223
|
-
# Perform the API call through the SDK function
|
|
224
|
-
result = @controller.redeliver_webhook_event(
|
|
225
|
-
store_id, id, event_id, idempotency_key: idempotency_key
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
# Test response code
|
|
229
|
-
assert_equal(202, @response_catcher.response.status_code)
|
|
230
|
-
# Test headers
|
|
231
|
-
expected_headers = {}
|
|
232
|
-
expected_headers['content-type'] = 'application/json; charset=utf-8'
|
|
233
|
-
|
|
234
|
-
assert(ComparisonHelper.match_headers(expected_headers, @response_catcher.response.headers))
|
|
235
|
-
|
|
236
|
-
# Test whether the captured response is as we expected
|
|
237
|
-
refute_nil(result)
|
|
238
|
-
expected_body = JSON.parse(
|
|
239
|
-
'{}'
|
|
240
|
-
)
|
|
241
|
-
received_body = JSON.parse(@response_catcher.response.raw_body)
|
|
242
|
-
assert(ComparisonHelper.match_body(expected_body, received_body))
|
|
243
|
-
end
|
|
244
|
-
|
|
245
215
|
end
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Custom test (not auto-generated): pins get_charge(charge_id) -- the
|
|
2
|
+
# store-scoped convenience call that reads the store id from the configured App
|
|
3
|
+
# Token instead of making the caller pass (and persist) one.
|
|
4
|
+
#
|
|
5
|
+
# Two things are guarded here.
|
|
6
|
+
#
|
|
7
|
+
# First the *guard*: when the configured token carries no usable store_id, the
|
|
8
|
+
# call must fail before a request is built. Interpolating a missing id would
|
|
9
|
+
# send GET /stores//charges/{id} -- a confusing 4xx instead of a clear
|
|
10
|
+
# client-side failure -- so the failure cases assert not just the raise but that
|
|
11
|
+
# the controller was never reached.
|
|
12
|
+
#
|
|
13
|
+
# Second the *delegation*: on the happy path this must behave exactly like
|
|
14
|
+
# charges.get_charge(store_id, charge_id, polling:), forwarding every argument
|
|
15
|
+
# and returning the response untouched. Anything else and the shortcut would be
|
|
16
|
+
# a second, subtly different way to fetch a charge.
|
|
17
|
+
#
|
|
18
|
+
# Everything here is synthetic and offline -- no network, no environment, no
|
|
19
|
+
# real credential. Note the SDK test harness injects JWT_TOKEN=test-jwt, which
|
|
20
|
+
# is not a JWT at all, so a test resting on an env-built client would silently
|
|
21
|
+
# exercise only the failure path. Hence the locally built clients.
|
|
22
|
+
#
|
|
23
|
+
# Seven SDKs are expected to share this contract; keep the case table aligned
|
|
24
|
+
# when porting.
|
|
25
|
+
|
|
26
|
+
require 'base64'
|
|
27
|
+
require 'json'
|
|
28
|
+
require 'minitest/autorun'
|
|
29
|
+
require 'univapay_client_sdk'
|
|
30
|
+
|
|
31
|
+
class ClientGetChargeTest < Minitest::Test
|
|
32
|
+
include UnivapayClientSdk
|
|
33
|
+
|
|
34
|
+
MERCHANT_ID = '11ec8e24-0ecf-2c5a-923c-331b915dc311'.freeze
|
|
35
|
+
STORE_ID = '11ec8e24-b133-6c68-b54d-971717202e9b'.freeze
|
|
36
|
+
CHARGE_ID = '11ec8e24-c5f5-6f2e-b9b0-1f4d3c6a9e10'.freeze
|
|
37
|
+
RESPONSE = Object.new
|
|
38
|
+
|
|
39
|
+
# Builds a JWT carrying claims. Header and signature are inert -- nothing
|
|
40
|
+
# verifies them.
|
|
41
|
+
def jwt(claims)
|
|
42
|
+
segment = lambda do |payload|
|
|
43
|
+
Base64.urlsafe_encode64(JSON.generate(payload), padding: false)
|
|
44
|
+
end
|
|
45
|
+
"#{segment.call({ 'alg' => 'HS256', 'typ' => 'JWT' })}.#{segment.call(claims)}.c2ln"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def store_token
|
|
49
|
+
jwt('merchant_id' => MERCHANT_ID, 'store_id' => STORE_ID)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def merchant_token
|
|
53
|
+
jwt('merchant_id' => MERCHANT_ID)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def client_with(jwt_token)
|
|
57
|
+
credentials =
|
|
58
|
+
if jwt_token.nil?
|
|
59
|
+
nil
|
|
60
|
+
else
|
|
61
|
+
BearerAuthCredentials.new(secret_key: 'not-a-real-secret', jwt_token: jwt_token)
|
|
62
|
+
end
|
|
63
|
+
Client.new(bearer_auth_credentials: credentials)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Replaces the controller call with a recorder, so no HTTP is issued.
|
|
67
|
+
def watching(client)
|
|
68
|
+
calls = []
|
|
69
|
+
controller = client.charges
|
|
70
|
+
controller.define_singleton_method(:get_charge) do |store_id, id, polling: nil|
|
|
71
|
+
calls << { store_id: store_id, id: id, polling: polling, receiver: self }
|
|
72
|
+
RESPONSE
|
|
73
|
+
end
|
|
74
|
+
calls
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# ── The guard: no usable store_id ─────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
def assert_guard_trips(jwt_token)
|
|
80
|
+
client = client_with(jwt_token)
|
|
81
|
+
calls = watching(client)
|
|
82
|
+
|
|
83
|
+
assert_nil client.get_current_store_id, 'precondition: no store id in this token'
|
|
84
|
+
|
|
85
|
+
error = assert_raises(RuntimeError) { client.get_charge(CHARGE_ID) }
|
|
86
|
+
assert_includes error.message, 'store-level App Token'
|
|
87
|
+
assert_empty calls, 'no request may be built when the store id is missing'
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def test_merchant_level_token_raises
|
|
91
|
+
assert_guard_trips(merchant_token)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def test_no_credentials_raises
|
|
95
|
+
assert_guard_trips(nil)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def test_malformed_token_raises
|
|
99
|
+
assert_guard_trips('not.a-jwt')
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_non_uuid_store_id_raises
|
|
103
|
+
assert_guard_trips(jwt('merchant_id' => MERCHANT_ID, 'store_id' => 'store-1'))
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def test_never_puts_the_credential_in_the_message
|
|
107
|
+
error = assert_raises(RuntimeError) { client_with(merchant_token).get_charge(CHARGE_ID) }
|
|
108
|
+
|
|
109
|
+
assert_includes error.message, 'get_charge(store_id, charge_id)'
|
|
110
|
+
refute_includes error.message, merchant_token
|
|
111
|
+
refute_includes error.message, MERCHANT_ID
|
|
112
|
+
refute_includes error.message, STORE_ID
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# ── The delegation: store id taken from the token ─────────────────────────
|
|
116
|
+
|
|
117
|
+
def test_delegates_with_the_store_id_from_the_token
|
|
118
|
+
client = client_with(store_token)
|
|
119
|
+
calls = watching(client)
|
|
120
|
+
|
|
121
|
+
client.get_charge(CHARGE_ID)
|
|
122
|
+
|
|
123
|
+
assert_equal 1, calls.length
|
|
124
|
+
assert_equal STORE_ID, calls[0][:store_id]
|
|
125
|
+
assert_equal CHARGE_ID, calls[0][:id]
|
|
126
|
+
assert_nil calls[0][:polling]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def test_forwards_the_polling_flag
|
|
130
|
+
client = client_with(store_token)
|
|
131
|
+
calls = watching(client)
|
|
132
|
+
|
|
133
|
+
client.get_charge(CHARGE_ID, polling: true)
|
|
134
|
+
|
|
135
|
+
assert_equal true, calls[0][:polling]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def test_returns_the_controller_response_untouched
|
|
139
|
+
client = client_with(store_token)
|
|
140
|
+
watching(client)
|
|
141
|
+
|
|
142
|
+
assert_same RESPONSE, client.get_charge(CHARGE_ID)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def test_reuses_one_controller_across_calls
|
|
146
|
+
client = client_with(store_token)
|
|
147
|
+
calls = watching(client)
|
|
148
|
+
|
|
149
|
+
client.get_charge(CHARGE_ID)
|
|
150
|
+
client.get_charge(CHARGE_ID)
|
|
151
|
+
|
|
152
|
+
assert_same calls[0][:receiver], calls[1][:receiver]
|
|
153
|
+
end
|
|
154
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: univapay-client-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Univapay Developers
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: apimatic_core_interfaces
|
|
@@ -433,6 +433,7 @@ files:
|
|
|
433
433
|
- test/framework_integrations/webhooks/test/test_sinatra_charge_app.rb
|
|
434
434
|
- test/http_response_catcher.rb
|
|
435
435
|
- test/test_app_jwt.rb
|
|
436
|
+
- test/test_client_get_charge.rb
|
|
436
437
|
homepage: https://univapay.com
|
|
437
438
|
licenses:
|
|
438
439
|
- MIT
|