abacatepay-ruby 1.2.1 → 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/.rubocop.yml +1 -0
- data/CHANGELOG.md +48 -0
- data/lib/abacate_pay/clients/client.rb +23 -6
- data/lib/abacate_pay/clients/coupon_client.rb +4 -1
- data/lib/abacate_pay/clients/customer_client.rb +4 -1
- data/lib/abacate_pay/clients/payout_client.rb +7 -1
- data/lib/abacate_pay/clients/pix_client.rb +11 -3
- data/lib/abacate_pay/clients/product_client.rb +4 -1
- data/lib/abacate_pay/clients/subscription_client.rb +4 -9
- data/lib/abacate_pay/clients/webhook_client.rb +20 -3
- data/lib/abacate_pay/enums/checkouts/statuses.rb +4 -1
- data/lib/abacate_pay/enums/coupons/statuses.rb +4 -1
- data/lib/abacate_pay/resources/coupons.rb +29 -3
- data/lib/abacate_pay/resources/payouts.rb +6 -0
- data/lib/abacate_pay/resources/resource.rb +20 -4
- data/lib/abacate_pay/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6049a0d508a75825d5bd94835dd27ec3241a0040129299165cb122de1af4636c
|
|
4
|
+
data.tar.gz: 8d2e2a858ac296de096f69ebf9acbab31c220d127631bd7a9534f30aa5caf5a7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7733ebca9a9176869461cdbf726933ff32836f8953df5cd4a4a4c64c1021261c0ec6d5a75e753b382071f68b93928a928db8364c0dc280e050aed59a0b9a534e
|
|
7
|
+
data.tar.gz: f90e64c56b1681d41ee820ba3ba05e9e7fa0b9ff61afd2d9adf1daf8d6debf68d62f9865f49f5d956744843f1eb51fecc9cfbc49146a7717c7bbbf7a39a7d078
|
data/.rubocop.yml
CHANGED
|
@@ -134,6 +134,7 @@ RSpec/DescribeClass:
|
|
|
134
134
|
- "spec/clients/logging_spec.rb"
|
|
135
135
|
- "spec/clients/boleto_spec.rb"
|
|
136
136
|
- "spec/packaging_spec.rb"
|
|
137
|
+
- "spec/api_conformance_spec.rb"
|
|
137
138
|
|
|
138
139
|
# `has_more?` mirrors the API's own `hasMore` field and matches the convention
|
|
139
140
|
# other payment SDKs use for cursor pagination. `more?` would read as a
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.2] - 2026-08-06
|
|
11
|
+
|
|
12
|
+
Found by exercising all 39 public calls against the live sandbox.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **`customers.delete`, `products.delete` and `coupons.delete` always failed.**
|
|
17
|
+
Every delete endpoint reads the id from the query string; sending it only in
|
|
18
|
+
the body fails with "Expected property 'id' to be string but found:
|
|
19
|
+
undefined". Same defect as `webhook_endpoints.delete`.
|
|
20
|
+
- **`pix.send_pix` and `payouts.create` always failed.** The API nests the
|
|
21
|
+
destination under `pix` as `{ key, type }`. The SDK sent `key`/`keyType` at
|
|
22
|
+
the top level, which fails with "Property 'pix' is missing". `Payouts` gained
|
|
23
|
+
`pix_key` and `pix_key_type`, without which a payout has no destination at
|
|
24
|
+
all.
|
|
25
|
+
- **`subscriptions.create` always failed.** Line items take the product `id`,
|
|
26
|
+
the same shape checkouts use; the SDK sent `externalId`, which fails with
|
|
27
|
+
"Expected property 'items.0.id' to be string".
|
|
28
|
+
- **Parsing a response could crash on a value the SDK did not know.**
|
|
29
|
+
`initialize_enum` validated while reading API responses, so any status
|
|
30
|
+
AbacatePay introduced turned into an `ArgumentError` inside `list` and `get`
|
|
31
|
+
for every integration at once. It happened with the coupon status `DISABLED`
|
|
32
|
+
and the payment-link status `ACTIVE`, which made `coupons.toggle` and
|
|
33
|
+
`payment_links.create` fail outright. Unknown values now pass through with a
|
|
34
|
+
warning; use the enum's `validate!` when you want a hard failure on a value
|
|
35
|
+
you are about to send.
|
|
36
|
+
- **`Coupons#code` and `#current_redeems` were always nil.** The API uses the
|
|
37
|
+
coupon code as the object's `id` and sends the counter as `redeemsCount`.
|
|
38
|
+
Adds `redeems_count`, `notes` and `dev_mode?`; `code` falls back to the id
|
|
39
|
+
and `current_redeems` keeps answering.
|
|
40
|
+
- **`webhook_endpoints.delete` always failed.** The API reads the id from the
|
|
41
|
+
query string on that endpoint, and answers `{"success":true,"error":null}`
|
|
42
|
+
with no `data` key, which the SDK then rejected as malformed. A successful
|
|
43
|
+
response carrying no payload is no longer an error.
|
|
44
|
+
- Building a resource from an empty payload raised `NoMethodError`.
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- `webhook_endpoints.create` refuses a secret shorter than 32 characters
|
|
49
|
+
locally, which is what the API requires.
|
|
50
|
+
- `pix.list` documents that the API requires an `id`; despite the name it
|
|
51
|
+
filters rather than lists.
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- The coupon and checkout status enums gained `DISABLED` and `ACTIVE`.
|
|
56
|
+
|
|
10
57
|
## [1.2.1] - 2026-08-06
|
|
11
58
|
|
|
12
59
|
Inclui tudo o que estava previsto para a 1.2.0, que nunca chegou a ser
|
|
@@ -239,6 +286,7 @@ versions and will not change without a major bump.
|
|
|
239
286
|
|
|
240
287
|
- Initial release
|
|
241
288
|
|
|
289
|
+
[1.2.2]: https://github.com/AbacatePay/abacatepay-ruby-sdk/releases/tag/v1.2.2
|
|
242
290
|
[1.2.1]: https://github.com/AbacatePay/abacatepay-ruby-sdk/releases/tag/v1.2.1
|
|
243
291
|
[1.1.0]: https://github.com/AbacatePay/abacatepay-ruby-sdk/releases/tag/v1.1.0
|
|
244
292
|
[1.0.0]: https://github.com/AbacatePay/abacatepay-ruby-sdk/releases/tag/v1.0.0
|
|
@@ -83,16 +83,33 @@ module AbacatePay
|
|
|
83
83
|
parsed = JSON.parse(response.body)
|
|
84
84
|
raise ApiError, "API error: #{parsed["error"]}" if parsed["error"]
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
# Preserve the cursor when the API sends one; dropping it made paging
|
|
88
|
-
# past the first 100 records impossible.
|
|
89
|
-
parsed["pagination"] ? Collection.new(data, parsed["pagination"]) : data
|
|
86
|
+
extract_data(parsed)
|
|
90
87
|
rescue Faraday::Error => e
|
|
91
88
|
handle_request_error(e)
|
|
92
89
|
rescue JSON::ParserError => e
|
|
93
90
|
raise ApiError, "Malformed API response: #{e.message}"
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Pulls the payload out of the `{data, error, success}` envelope.
|
|
94
|
+
#
|
|
95
|
+
# Some endpoints answer a successful call with no `data` at all, for
|
|
96
|
+
# example `webhooks/delete` returning `{"success":true,"error":null}`.
|
|
97
|
+
# Treating that as malformed turned a working call into an ApiError.
|
|
98
|
+
#
|
|
99
|
+
# @param parsed [Hash] The decoded response body
|
|
100
|
+
# @return [Object, nil] The payload, or nil when the call carries none
|
|
101
|
+
# @raise [ApiError] if the envelope has neither data nor a success flag
|
|
102
|
+
def extract_data(parsed)
|
|
103
|
+
unless parsed.key?("data")
|
|
104
|
+
return nil if parsed["success"]
|
|
105
|
+
|
|
106
|
+
raise ApiError, "Unexpected API response: #{parsed.inspect[0, 120]}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
data = parsed["data"]
|
|
110
|
+
# Preserve the cursor when the API sends one; dropping it made paging
|
|
111
|
+
# past the first 100 records impossible.
|
|
112
|
+
parsed["pagination"] ? Collection.new(data, parsed["pagination"]) : data
|
|
96
113
|
end
|
|
97
114
|
|
|
98
115
|
# Issues the HTTP call.
|
|
@@ -42,7 +42,10 @@ module AbacatePay
|
|
|
42
42
|
# @param id [String] Coupon ID
|
|
43
43
|
# @return [Resources::Coupons]
|
|
44
44
|
def delete(id)
|
|
45
|
-
|
|
45
|
+
# The API reads the id from the query string on delete endpoints.
|
|
46
|
+
# Sending it only in the body fails with
|
|
47
|
+
# "Expected property 'id' to be string but found: undefined".
|
|
48
|
+
response = request("POST", "delete", params: { id: id }, json: {})
|
|
46
49
|
Resources::Coupons.new(response)
|
|
47
50
|
end
|
|
48
51
|
|
|
@@ -50,7 +50,10 @@ module AbacatePay
|
|
|
50
50
|
# @param id [String] The customer ID
|
|
51
51
|
# @return [Resources::Customers] The deleted Customer object
|
|
52
52
|
def delete(id)
|
|
53
|
-
|
|
53
|
+
# The API reads the id from the query string on delete endpoints.
|
|
54
|
+
# Sending it only in the body fails with
|
|
55
|
+
# "Expected property 'id' to be string but found: undefined".
|
|
56
|
+
response = request("POST", "delete", params: { id: id }, json: {})
|
|
54
57
|
Resources::Customers.new(response)
|
|
55
58
|
end
|
|
56
59
|
end
|
|
@@ -32,7 +32,13 @@ module AbacatePay
|
|
|
32
32
|
response = request("POST", "create", json: {
|
|
33
33
|
amount: data.amount,
|
|
34
34
|
externalId: data.external_id,
|
|
35
|
-
description: data.description
|
|
35
|
+
description: data.description,
|
|
36
|
+
# The API nests the destination under `pix`, with
|
|
37
|
+
# `key` and `type`. Sending pixKey/pixKeyType at the
|
|
38
|
+
# top level fails with "Property 'pix' is missing";
|
|
39
|
+
# nesting the wrong names fails with
|
|
40
|
+
# "Property 'pix.type' is missing".
|
|
41
|
+
pix: { key: data.pix_key, type: data.pix_key_type }
|
|
36
42
|
})
|
|
37
43
|
Resources::Payouts.new(response)
|
|
38
44
|
end
|
|
@@ -13,7 +13,11 @@ module AbacatePay
|
|
|
13
13
|
super(URI, client)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
#
|
|
16
|
+
# The API requires an `id` on this endpoint: a bare call fails with
|
|
17
|
+
# "Expected property 'id' to be string but found: undefined". Despite the
|
|
18
|
+
# name it filters rather than lists.
|
|
19
|
+
#
|
|
20
|
+
# @param params [Hash] Params forwarded to the API; `id` is required
|
|
17
21
|
# @return [Array<Resources::PixTransfers>]
|
|
18
22
|
def list(**params)
|
|
19
23
|
response = request("GET", "list", params: params.empty? ? nil : params)
|
|
@@ -37,8 +41,12 @@ module AbacatePay
|
|
|
37
41
|
amount: data.amount,
|
|
38
42
|
externalId: data.external_id,
|
|
39
43
|
description: data.description,
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
# The API nests the destination under `pix`, with
|
|
45
|
+
# `key` and `type`. Sending pixKey/pixKeyType at the
|
|
46
|
+
# top level fails with "Property 'pix' is missing";
|
|
47
|
+
# nesting the wrong names fails with
|
|
48
|
+
# "Property 'pix.type' is missing".
|
|
49
|
+
pix: { key: data.key, type: data.key_type }
|
|
42
50
|
})
|
|
43
51
|
Resources::PixTransfers.new(response)
|
|
44
52
|
end
|
|
@@ -46,7 +46,10 @@ module AbacatePay
|
|
|
46
46
|
# @param id [String] Product ID
|
|
47
47
|
# @return [Resources::Products]
|
|
48
48
|
def delete(id)
|
|
49
|
-
|
|
49
|
+
# The API reads the id from the query string on delete endpoints.
|
|
50
|
+
# Sending it only in the body fails with
|
|
51
|
+
# "Expected property 'id' to be string but found: undefined".
|
|
52
|
+
response = request("POST", "delete", params: { id: id }, json: {})
|
|
50
53
|
Resources::Products.new(response)
|
|
51
54
|
end
|
|
52
55
|
end
|
|
@@ -26,15 +26,10 @@ module AbacatePay
|
|
|
26
26
|
methods: data.methods,
|
|
27
27
|
externalId: data.external_id,
|
|
28
28
|
customerId: data.customer&.id,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
description: product.description,
|
|
34
|
-
quantity: product.quantity,
|
|
35
|
-
price: product.price
|
|
36
|
-
}
|
|
37
|
-
end
|
|
29
|
+
# The API expects the product id here, the same shape checkouts use.
|
|
30
|
+
# Sending externalId fails with
|
|
31
|
+
# "Expected property 'items.0.id' to be string".
|
|
32
|
+
items: data.products&.map { |product| { id: product.external_id, quantity: product.quantity } }
|
|
38
33
|
}
|
|
39
34
|
|
|
40
35
|
response = request("POST", "create", json: request_data)
|
|
@@ -11,6 +11,9 @@ module AbacatePay
|
|
|
11
11
|
class WebhookClient < Client
|
|
12
12
|
URI = "webhooks"
|
|
13
13
|
|
|
14
|
+
# Shortest signing secret the API accepts.
|
|
15
|
+
MINIMUM_SECRET_LENGTH = 32
|
|
16
|
+
|
|
14
17
|
# @param client [Faraday::Connection, nil] Optional Faraday client
|
|
15
18
|
def initialize(client = nil)
|
|
16
19
|
super(URI, client)
|
|
@@ -40,11 +43,19 @@ module AbacatePay
|
|
|
40
43
|
# @param secret [String] Signing secret
|
|
41
44
|
# @param events [Array<String>] Event types to subscribe to
|
|
42
45
|
# @return [Resources::WebhookEndpoints] The created webhook
|
|
43
|
-
# @raise [ArgumentError] if the endpoint is not
|
|
46
|
+
# @raise [ArgumentError] if the endpoint is not HTTPS, the event list is
|
|
47
|
+
# empty, or the secret is shorter than the API accepts
|
|
44
48
|
def create(name:, endpoint:, secret:, events:)
|
|
45
49
|
raise ArgumentError, "endpoint must be an HTTPS URL, got #{endpoint.inspect}" unless https_url?(endpoint)
|
|
46
50
|
raise ArgumentError, "events must not be empty" if Array(events).empty?
|
|
47
51
|
|
|
52
|
+
# The API rejects anything shorter with
|
|
53
|
+
# "Expected string length greater or equal to 32".
|
|
54
|
+
if secret.to_s.length < MINIMUM_SECRET_LENGTH
|
|
55
|
+
raise ArgumentError,
|
|
56
|
+
"secret must be at least #{MINIMUM_SECRET_LENGTH} characters, got #{secret.to_s.length}"
|
|
57
|
+
end
|
|
58
|
+
|
|
48
59
|
response = request("POST", "create", json: {
|
|
49
60
|
name: name,
|
|
50
61
|
endpoint: endpoint,
|
|
@@ -55,9 +66,15 @@ module AbacatePay
|
|
|
55
66
|
end
|
|
56
67
|
|
|
57
68
|
# @param id [String] The webhook ID
|
|
58
|
-
# @return [Resources::WebhookEndpoints] The deleted webhook
|
|
69
|
+
# @return [Resources::WebhookEndpoints, nil] The deleted webhook, or nil
|
|
70
|
+
# when the API confirms the deletion without echoing the object
|
|
59
71
|
def delete(id)
|
|
60
|
-
|
|
72
|
+
# The API reads the id from the query string here, like
|
|
73
|
+
# transparents/simulate-payment. Sending it in the body fails with
|
|
74
|
+
# "Expected property 'id' to be string but found: undefined".
|
|
75
|
+
response = request("POST", "delete", params: { id: id }, json: {})
|
|
76
|
+
return nil if response.nil?
|
|
77
|
+
|
|
61
78
|
Resources::WebhookEndpoints.new(response)
|
|
62
79
|
end
|
|
63
80
|
|
|
@@ -10,8 +10,11 @@ module AbacatePay
|
|
|
10
10
|
PAID = "PAID"
|
|
11
11
|
REFUNDED = "REFUNDED"
|
|
12
12
|
|
|
13
|
+
# Reusable payment links are created ACTIVE rather than PENDING.
|
|
14
|
+
ACTIVE = "ACTIVE"
|
|
15
|
+
|
|
13
16
|
def self.values
|
|
14
|
-
[PENDING, EXPIRED, CANCELLED, PAID, REFUNDED]
|
|
17
|
+
[PENDING, EXPIRED, CANCELLED, PAID, REFUNDED, ACTIVE]
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
def self.valid?(value)
|
|
@@ -8,8 +8,11 @@ module AbacatePay
|
|
|
8
8
|
INACTIVE = "INACTIVE"
|
|
9
9
|
EXPIRED = "EXPIRED"
|
|
10
10
|
|
|
11
|
+
# What the API returns for a coupon turned off through `toggle`.
|
|
12
|
+
DISABLED = "DISABLED"
|
|
13
|
+
|
|
11
14
|
def self.values
|
|
12
|
-
[ACTIVE, INACTIVE, EXPIRED]
|
|
15
|
+
[ACTIVE, INACTIVE, EXPIRED, DISABLED]
|
|
13
16
|
end
|
|
14
17
|
|
|
15
18
|
def self.valid?(value)
|
|
@@ -11,13 +11,38 @@ module AbacatePay
|
|
|
11
11
|
|
|
12
12
|
DATETIME_PROPERTIES = %w[created_at updated_at].freeze
|
|
13
13
|
|
|
14
|
-
attr_reader :id, :
|
|
15
|
-
:
|
|
14
|
+
attr_reader :id, :discount, :discount_kind, :max_redeems,
|
|
15
|
+
:status, :notes, :dev_mode, :created_at, :updated_at
|
|
16
|
+
|
|
17
|
+
# @return [Integer, nil] Times the coupon has been redeemed
|
|
18
|
+
attr_reader :redeems_count
|
|
16
19
|
|
|
17
20
|
def initialize(data)
|
|
18
21
|
fill(data)
|
|
19
22
|
end
|
|
20
23
|
|
|
24
|
+
# The coupon code.
|
|
25
|
+
#
|
|
26
|
+
# The API uses the code as the object's `id` and sends no separate
|
|
27
|
+
# `code` field, so this reader was always nil for API data.
|
|
28
|
+
#
|
|
29
|
+
# @return [String, nil]
|
|
30
|
+
def code
|
|
31
|
+
@code || @id
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [Integer, nil] Times the coupon has been redeemed
|
|
35
|
+
#
|
|
36
|
+
# @deprecated The API calls this `redeemsCount`; use {#redeems_count}.
|
|
37
|
+
def current_redeems
|
|
38
|
+
@current_redeems || @redeems_count
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [Boolean, nil] Whether the coupon belongs to Dev mode
|
|
42
|
+
def dev_mode?
|
|
43
|
+
@dev_mode
|
|
44
|
+
end
|
|
45
|
+
|
|
21
46
|
private
|
|
22
47
|
|
|
23
48
|
def process_value(property, value)
|
|
@@ -35,7 +60,8 @@ module AbacatePay
|
|
|
35
60
|
protected
|
|
36
61
|
|
|
37
62
|
attr_writer :id, :code, :discount, :discount_kind, :max_redeems,
|
|
38
|
-
:current_redeems, :
|
|
63
|
+
:current_redeems, :redeems_count, :status, :notes,
|
|
64
|
+
:dev_mode, :created_at, :updated_at
|
|
39
65
|
end
|
|
40
66
|
end
|
|
41
67
|
end
|
|
@@ -10,6 +10,12 @@ module AbacatePay
|
|
|
10
10
|
|
|
11
11
|
DATETIME_PROPERTIES = %w[created_at updated_at].freeze
|
|
12
12
|
|
|
13
|
+
# @return [String, nil] Destination PIX key
|
|
14
|
+
attr_accessor :pix_key
|
|
15
|
+
|
|
16
|
+
# @return [String, nil] Destination PIX key type
|
|
17
|
+
attr_accessor :pix_key_type
|
|
18
|
+
|
|
13
19
|
attr_reader :id, :amount, :external_id, :description,
|
|
14
20
|
:status, :created_at, :updated_at
|
|
15
21
|
|
|
@@ -21,16 +21,28 @@ module AbacatePay
|
|
|
21
21
|
raise ArgumentError, "Invalid datetime value: #{value}"
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
# Initialize an enum from a string or enum object
|
|
24
|
+
# Initialize an enum from a string or enum object.
|
|
25
|
+
#
|
|
26
|
+
# Unknown values pass through with a warning rather than raising. This
|
|
27
|
+
# method runs while parsing API responses, and AbacatePay adds statuses
|
|
28
|
+
# over time: rejecting an unrecognised one would turn every new value
|
|
29
|
+
# into a crash inside `list` and `get` for every integration at once.
|
|
30
|
+
# That happened with the coupon status `DISABLED` and the payment-link
|
|
31
|
+
# status `ACTIVE`, neither of which the SDK knew about.
|
|
32
|
+
#
|
|
33
|
+
# Use {AbacatePay::Enums} `validate!` directly when you want a hard
|
|
34
|
+
# failure on a value you are about to send.
|
|
25
35
|
#
|
|
26
36
|
# @param enum_class [Class] The enum class to initialize
|
|
27
37
|
# @param value [String, Object] The value to initialize
|
|
28
|
-
# @return [Object, nil] The
|
|
29
|
-
# @raise [ArgumentError] If the value is invalid
|
|
38
|
+
# @return [Object, nil] The value, validated when known
|
|
30
39
|
def initialize_enum(enum_class, value)
|
|
31
40
|
return nil if value.nil? || value.empty?
|
|
41
|
+
return value if enum_class.valid?(value)
|
|
32
42
|
|
|
33
|
-
enum_class.
|
|
43
|
+
warn "[AbacatePay] unknown #{enum_class.name.split("::").last} value #{value.inspect}. " \
|
|
44
|
+
"Passing it through; upgrade the gem if this is a new API value."
|
|
45
|
+
value
|
|
34
46
|
end
|
|
35
47
|
|
|
36
48
|
# Initialize a resource from a hash or resource object
|
|
@@ -66,6 +78,10 @@ module AbacatePay
|
|
|
66
78
|
# @param data [Hash] The data to fill with
|
|
67
79
|
# @return [void]
|
|
68
80
|
def fill(data)
|
|
81
|
+
# Some endpoints answer a successful call with no payload, so the
|
|
82
|
+
# resource is built from nil. An empty object beats a NoMethodError.
|
|
83
|
+
return if data.nil?
|
|
84
|
+
|
|
69
85
|
data.each do |key, value|
|
|
70
86
|
property = camel_to_snake(key)
|
|
71
87
|
next unless respond_to?("#{property}=", true)
|
data/lib/abacate_pay/version.rb
CHANGED