abacatepay-ruby 1.1.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/.rubocop.yml +8 -0
- data/CHANGELOG.md +137 -19
- data/README.md +43 -24
- data/abacatepay-ruby.gemspec +1 -1
- data/lib/abacate_pay/clients/billing_client.rb +2 -2
- data/lib/abacate_pay/clients/checkout_client.rb +1 -1
- data/lib/abacate_pay/clients/client.rb +66 -15
- 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/payment_link_client.rb +2 -2
- 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/store_client.rb +3 -1
- data/lib/abacate_pay/clients/subscription_client.rb +5 -10
- data/lib/abacate_pay/clients/transparent_client.rb +4 -2
- data/lib/abacate_pay/clients/webhook_client.rb +21 -4
- data/lib/abacate_pay/clients.rb +1 -1
- data/lib/abacate_pay/configuration.rb +3 -3
- data/lib/abacate_pay/enums/billings/methods.rb +1 -1
- data/lib/abacate_pay/enums/checkouts/statuses.rb +4 -1
- data/lib/abacate_pay/enums/coupons/statuses.rb +4 -1
- data/lib/abacate_pay/enums/webhooks/event_types.rb +1 -1
- data/lib/abacate_pay/resources/coupons.rb +29 -3
- data/lib/abacate_pay/resources/customers.rb +62 -21
- data/lib/abacate_pay/resources/payouts.rb +6 -0
- data/lib/abacate_pay/resources/resource.rb +21 -5
- data/lib/abacate_pay/resources/transparents.rb +21 -4
- data/lib/abacate_pay/resources/webhook_endpoints.rb +1 -1
- data/lib/abacate_pay/resources.rb +1 -1
- data/lib/abacate_pay/version.rb +1 -1
- data/lib/abacate_pay/webhooks.rb +53 -7
- data/lib/abacatepay-ruby.rb +14 -0
- metadata +4 -3
|
@@ -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
|
|
@@ -12,7 +12,9 @@ module AbacatePay
|
|
|
12
12
|
|
|
13
13
|
# @return [Resources::Store]
|
|
14
14
|
def get
|
|
15
|
-
|
|
15
|
+
# The API serves this as `stores/get`, plural, even though the reference
|
|
16
|
+
# documents `store/get`. The singular path answers HTTP 400.
|
|
17
|
+
response = request("GET", "stores/get")
|
|
16
18
|
Resources::Store.new(response)
|
|
17
19
|
end
|
|
18
20
|
|
|
@@ -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)
|
|
@@ -52,7 +47,7 @@ module AbacatePay
|
|
|
52
47
|
end
|
|
53
48
|
|
|
54
49
|
# Changes the main product of an active subscription. The new price takes
|
|
55
|
-
# effect on the next billing cycle
|
|
50
|
+
# effect on the next billing cycle, the current cycle is untouched.
|
|
56
51
|
#
|
|
57
52
|
# @param id [String] The subscription ID (`subs_...`)
|
|
58
53
|
# @param product_id [String] The new product ID (`prod_...`), which must have a cycle
|
|
@@ -45,12 +45,14 @@ module AbacatePay
|
|
|
45
45
|
# @param id [String] QR code ID (dev mode only)
|
|
46
46
|
# @return [Resources::Transparents]
|
|
47
47
|
def simulate_payment(id)
|
|
48
|
-
|
|
48
|
+
# The API reads the id from the query string here, like #check. Sending
|
|
49
|
+
# it only in the body fails with "Expected property 'id'".
|
|
50
|
+
response = request("POST", "simulate-payment", params: { id: id }, json: {})
|
|
49
51
|
Resources::Transparents.new(response)
|
|
50
52
|
end
|
|
51
53
|
|
|
52
54
|
# Refunds a transparent payment in full. AbacatePay does not support
|
|
53
|
-
# partial refunds
|
|
55
|
+
# partial refunds, the original amount is always returned.
|
|
54
56
|
#
|
|
55
57
|
# @param id [String] Public charge ID (`pix_char_...`, `card_...`, `char_...`)
|
|
56
58
|
# @return [Resources::Transparents] The refunded charge
|
|
@@ -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)
|
|
@@ -32,7 +35,7 @@ module AbacatePay
|
|
|
32
35
|
|
|
33
36
|
# Registers a new webhook endpoint.
|
|
34
37
|
#
|
|
35
|
-
# The secret is what AbacatePay signs deliveries with
|
|
38
|
+
# The secret is what AbacatePay signs deliveries with, pass the same
|
|
36
39
|
# value to {AbacatePay::Webhooks.construct_event} when handling them.
|
|
37
40
|
#
|
|
38
41
|
# @param name [String] Identifying name
|
|
@@ -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
|
|
data/lib/abacate_pay/clients.rb
CHANGED
|
@@ -9,8 +9,8 @@ module AbacatePay
|
|
|
9
9
|
# @api public
|
|
10
10
|
class Configuration
|
|
11
11
|
# The only base URL this SDK speaks. v1 still exists, but under a
|
|
12
|
-
# different dialect
|
|
13
|
-
# different resource names (`pixQrCode`)
|
|
12
|
+
# different dialect, singular paths (`/v1/billing/`, `/v1/customer/`) and
|
|
13
|
+
# different resource names (`pixQrCode`), which this SDK has never
|
|
14
14
|
# implemented. Deriving a base URL from the token prefix only produced 404s
|
|
15
15
|
# against v1 while sending v2-shaped paths.
|
|
16
16
|
API_BASE_URL = "https://api.abacatepay.com/v2"
|
|
@@ -43,7 +43,7 @@ module AbacatePay
|
|
|
43
43
|
@api_token = nil
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
# @deprecated The environment is determined by the API key itself
|
|
46
|
+
# @deprecated The environment is determined by the API key itself, keys
|
|
47
47
|
# created in Dev mode produce simulated transactions, production keys
|
|
48
48
|
# produce real ones. This setting has never had any effect and is kept
|
|
49
49
|
# only so existing initializers keep loading.
|
|
@@ -14,7 +14,7 @@ module AbacatePay
|
|
|
14
14
|
# @return [String] Credit card payment
|
|
15
15
|
CARD = "CARD"
|
|
16
16
|
|
|
17
|
-
# Boleto bancário. Supports a due date and late-payment interest/fine
|
|
17
|
+
# Boleto bancário. Supports a due date and late-payment interest/fine -
|
|
18
18
|
# see Resources::Checkouts#due_date, #interest and #fine.
|
|
19
19
|
# @return [String] Boleto payment method
|
|
20
20
|
BOLETO = "BOLETO"
|
|
@@ -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)
|
|
@@ -14,7 +14,7 @@ module AbacatePay
|
|
|
14
14
|
SUBSCRIPTION_RENEWED = "subscription.renewed"
|
|
15
15
|
SUBSCRIPTION_CANCELLED = "subscription.cancelled"
|
|
16
16
|
|
|
17
|
-
# Recurring charge failed
|
|
17
|
+
# Recurring charge failed, the dunning signal. Without handling this,
|
|
18
18
|
# a failing subscription looks identical to a healthy one.
|
|
19
19
|
SUBSCRIPTION_PAYMENT_FAILED = "subscription.payment_failed"
|
|
20
20
|
|
|
@@ -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
|
|
@@ -2,45 +2,86 @@
|
|
|
2
2
|
|
|
3
3
|
module AbacatePay
|
|
4
4
|
module Resources
|
|
5
|
-
# Represents a customer
|
|
5
|
+
# Represents a customer in the AbacatePay system.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
7
|
+
# The API returns the customer fields at the top level of the object:
|
|
8
|
+
#
|
|
9
|
+
# { "id": "cust_...", "name": "Ana", "email": "ana@example.com",
|
|
10
|
+
# "cellphone": "...", "taxId": "...", "metadata": {} }
|
|
11
|
+
#
|
|
12
|
+
# Earlier versions of this class only mapped `id` and a nested `metadata`,
|
|
13
|
+
# so every other field was silently dropped and `customer.metadata.name`
|
|
14
|
+
# came back nil for real API data.
|
|
15
|
+
#
|
|
16
|
+
# Both shapes work now: the fields are exposed directly, and `metadata`
|
|
17
|
+
# keeps answering for code written against the previous interface.
|
|
9
18
|
class Customers < Resource
|
|
10
|
-
# Maps property names to their corresponding resource classes
|
|
11
19
|
RESOURCE_PROPERTIES = {
|
|
12
20
|
metadata: "AbacatePay::Resources::Customers::Metadata"
|
|
13
21
|
}.freeze
|
|
14
22
|
|
|
15
|
-
#
|
|
16
|
-
|
|
23
|
+
# Fields the API sends inside the customer object.
|
|
24
|
+
IDENTITY_FIELDS = %i[name email cellphone tax_id].freeze
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
attr_reader :metadata
|
|
26
|
+
attr_reader :id, :country, :zip_code, :dev_mode
|
|
20
27
|
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# @
|
|
28
|
+
# @return [String, nil] Customer's name
|
|
29
|
+
attr_reader :name
|
|
30
|
+
|
|
31
|
+
# @return [String, nil] Customer's email address
|
|
32
|
+
attr_reader :email
|
|
33
|
+
|
|
34
|
+
# @return [String, nil] Customer's cellphone number
|
|
35
|
+
attr_reader :cellphone
|
|
36
|
+
|
|
37
|
+
# @return [String, nil] Customer's tax identification number
|
|
38
|
+
attr_reader :tax_id
|
|
39
|
+
|
|
40
|
+
# @param data [Hash] The customer properties
|
|
25
41
|
def initialize(data)
|
|
26
42
|
fill(data)
|
|
27
43
|
end
|
|
28
44
|
|
|
29
|
-
|
|
45
|
+
# @return [Boolean, nil] Whether this customer belongs to Dev mode
|
|
46
|
+
def dev_mode?
|
|
47
|
+
@dev_mode
|
|
48
|
+
end
|
|
30
49
|
|
|
31
|
-
#
|
|
50
|
+
# The identity fields, wrapped.
|
|
32
51
|
#
|
|
52
|
+
# Kept because `customer.metadata.name` is what the README documented and
|
|
53
|
+
# what existing integrations call. When the API sends the fields at the
|
|
54
|
+
# top level, this builds the wrapper from them rather than returning the
|
|
55
|
+
# empty object the API puts in `metadata`.
|
|
56
|
+
#
|
|
57
|
+
# @return [Customers::Metadata, nil]
|
|
58
|
+
def metadata
|
|
59
|
+
return @metadata if metadata_populated?(@metadata)
|
|
60
|
+
|
|
61
|
+
identity = IDENTITY_FIELDS.to_h { |field| [field, public_send(field)] }.compact
|
|
62
|
+
return @metadata if identity.empty?
|
|
63
|
+
|
|
64
|
+
Customers::Metadata.new(identity)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
# @param metadata [Customers::Metadata, nil]
|
|
70
|
+
# @return [Boolean] Whether the API actually filled the nested object
|
|
71
|
+
def metadata_populated?(metadata)
|
|
72
|
+
return false if metadata.nil?
|
|
73
|
+
|
|
74
|
+
IDENTITY_FIELDS.any? { |field| metadata.public_send(field) }
|
|
75
|
+
end
|
|
76
|
+
|
|
33
77
|
# @param property [String] The property name
|
|
34
|
-
# @param value [Object] The value
|
|
78
|
+
# @param value [Object] The raw value
|
|
35
79
|
# @return [Object] The processed value
|
|
36
80
|
def process_value(property, value)
|
|
37
81
|
return nil if value.nil?
|
|
38
82
|
|
|
39
83
|
if RESOURCE_PROPERTIES.key?(property.to_sym)
|
|
40
|
-
initialize_resource(
|
|
41
|
-
Object.const_get(RESOURCE_PROPERTIES[property.to_sym]),
|
|
42
|
-
value
|
|
43
|
-
)
|
|
84
|
+
initialize_resource(Object.const_get(RESOURCE_PROPERTIES[property.to_sym]), value)
|
|
44
85
|
else
|
|
45
86
|
value
|
|
46
87
|
end
|
|
@@ -48,8 +89,8 @@ module AbacatePay
|
|
|
48
89
|
|
|
49
90
|
protected
|
|
50
91
|
|
|
51
|
-
|
|
52
|
-
|
|
92
|
+
attr_writer :id, :metadata, :name, :email, :cellphone, :tax_id,
|
|
93
|
+
:country, :zip_code, :dev_mode
|
|
53
94
|
end
|
|
54
95
|
end
|
|
55
96
|
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
|
|
@@ -51,7 +63,7 @@ module AbacatePay
|
|
|
51
63
|
resource_class.new(value)
|
|
52
64
|
end
|
|
53
65
|
|
|
54
|
-
# Default value processor
|
|
66
|
+
# Default value processor, returns the value as-is.
|
|
55
67
|
# Subclasses override this to handle enums, datetimes, and nested resources.
|
|
56
68
|
#
|
|
57
69
|
# @param _property [String] The property name
|
|
@@ -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)
|
|
@@ -10,19 +10,36 @@ module AbacatePay
|
|
|
10
10
|
|
|
11
11
|
DATETIME_PROPERTIES = %w[created_at updated_at expires_at].freeze
|
|
12
12
|
|
|
13
|
+
# qr_code and qr_code_image are defined below rather than generated here:
|
|
14
|
+
# they fall back to the br_code fields the API actually sends.
|
|
13
15
|
attr_reader :id, :amount, :status, :method, :description,
|
|
14
|
-
:expires_in, :
|
|
16
|
+
:expires_in, :customer,
|
|
15
17
|
:metadata, :dev_mode, :created_at, :updated_at,
|
|
16
18
|
# Boleto: due date sent on create, plus the payment slip the
|
|
17
|
-
# API returns
|
|
19
|
+
# API returns: digitable line, viewing URL, and the PIX
|
|
18
20
|
# fallback issued for the same charge.
|
|
19
21
|
:due_date, :bar_code, :url, :br_code, :br_code_base64,
|
|
20
|
-
:expires_at
|
|
22
|
+
:expires_at, :platform_fee, :receipt_url
|
|
21
23
|
|
|
22
24
|
def initialize(data)
|
|
23
25
|
fill(data)
|
|
24
26
|
end
|
|
25
27
|
|
|
28
|
+
# The API returns the copy-and-paste payload as `brCode` and the image as
|
|
29
|
+
# `brCodeBase64`. Older documentation used `qrCode`/`qrCodeImage`, so both
|
|
30
|
+
# spellings are accepted: the reader prefers the `qr*` value when the API
|
|
31
|
+
# sends one and falls back to `br*`.
|
|
32
|
+
#
|
|
33
|
+
# @return [String, nil] PIX copy-and-paste payload
|
|
34
|
+
def qr_code
|
|
35
|
+
@qr_code || br_code
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [String, nil] PIX QR code image as a data URI
|
|
39
|
+
def qr_code_image
|
|
40
|
+
@qr_code_image || br_code_base64
|
|
41
|
+
end
|
|
42
|
+
|
|
26
43
|
def dev_mode?
|
|
27
44
|
@dev_mode
|
|
28
45
|
end
|
|
@@ -47,7 +64,7 @@ module AbacatePay
|
|
|
47
64
|
:expires_in, :qr_code, :qr_code_image, :customer,
|
|
48
65
|
:metadata, :dev_mode, :created_at, :updated_at,
|
|
49
66
|
:due_date, :bar_code, :url, :br_code, :br_code_base64,
|
|
50
|
-
:expires_at
|
|
67
|
+
:expires_at, :platform_fee, :receipt_url
|
|
51
68
|
end
|
|
52
69
|
end
|
|
53
70
|
end
|
|
@@ -4,7 +4,7 @@ module AbacatePay
|
|
|
4
4
|
module Resources
|
|
5
5
|
# Represents a registered webhook endpoint in the AbacatePay system.
|
|
6
6
|
#
|
|
7
|
-
# This is the endpoint *registration
|
|
7
|
+
# This is the endpoint *registration*, the URL AbacatePay delivers events
|
|
8
8
|
# to. For verifying and parsing an inbound delivery, see {AbacatePay::Webhooks}.
|
|
9
9
|
class WebhookEndpoints < Resource
|
|
10
10
|
# @return [String, nil] Webhook ID
|
data/lib/abacate_pay/version.rb
CHANGED