abacatepay-ruby 0.1.0 → 1.0.0

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +125 -0
  3. data/CHANGELOG.md +121 -1
  4. data/README.md +408 -87
  5. data/Rakefile +1 -1
  6. data/abacatepay-ruby.gemspec +16 -10
  7. data/lib/abacate_pay/clients/billing_client.rb +80 -0
  8. data/lib/abacate_pay/clients/checkout_client.rb +70 -0
  9. data/lib/{abacatepay → abacate_pay}/clients/client.rb +20 -7
  10. data/lib/abacate_pay/clients/coupon_client.rb +57 -0
  11. data/lib/abacate_pay/clients/customer_client.rb +58 -0
  12. data/lib/abacate_pay/clients/payment_link_client.rb +71 -0
  13. data/lib/abacate_pay/clients/payout_client.rb +41 -0
  14. data/lib/abacate_pay/clients/pix_client.rb +47 -0
  15. data/lib/abacate_pay/clients/product_client.rb +54 -0
  16. data/lib/abacate_pay/clients/store_client.rb +40 -0
  17. data/lib/abacate_pay/clients/subscription_client.rb +55 -0
  18. data/lib/abacate_pay/clients/transparent_client.rb +73 -0
  19. data/lib/abacate_pay/clients/webhook_client.rb +82 -0
  20. data/lib/abacate_pay/clients.rb +24 -0
  21. data/lib/abacate_pay/configuration.rb +67 -0
  22. data/lib/{abacatepay/enums/billing → abacate_pay/enums/billings}/frequencies.rb +9 -3
  23. data/lib/{abacatepay/enums/billing → abacate_pay/enums/billings}/methods.rb +5 -3
  24. data/lib/{abacatepay/enums/billing → abacate_pay/enums/billings}/statuses.rb +3 -2
  25. data/lib/abacate_pay/enums/checkouts/statuses.rb +29 -0
  26. data/lib/abacate_pay/enums/coupons/discount_kinds.rb +26 -0
  27. data/lib/abacate_pay/enums/coupons/statuses.rb +27 -0
  28. data/lib/abacate_pay/enums/payouts/statuses.rb +29 -0
  29. data/lib/abacate_pay/enums/pix/key_types.rb +30 -0
  30. data/lib/abacate_pay/enums/products/cycles.rb +28 -0
  31. data/lib/abacate_pay/enums/transfers/statuses.rb +30 -0
  32. data/lib/abacate_pay/enums/webhooks/event_types.rb +43 -0
  33. data/lib/abacate_pay/enums.rb +20 -0
  34. data/lib/{abacatepay/resources/billing → abacate_pay/resources/billings}/metadata.rb +2 -8
  35. data/lib/{abacatepay/resources/billing → abacate_pay/resources/billings}/product.rb +2 -8
  36. data/lib/{abacatepay/resources/billing.rb → abacate_pay/resources/billings.rb} +16 -16
  37. data/lib/abacate_pay/resources/checkouts.rb +76 -0
  38. data/lib/abacate_pay/resources/coupons.rb +41 -0
  39. data/lib/{abacatepay/resources/customer → abacate_pay/resources/customers}/metadata.rb +2 -8
  40. data/lib/{abacatepay/resources/customer.rb → abacate_pay/resources/customers.rb} +5 -5
  41. data/lib/abacate_pay/resources/payouts.rb +40 -0
  42. data/lib/abacate_pay/resources/pix_transfers.rb +41 -0
  43. data/lib/abacate_pay/resources/products.rb +40 -0
  44. data/lib/{abacatepay → abacate_pay}/resources/resource.rb +33 -21
  45. data/lib/abacate_pay/resources/store/balance.rb +20 -0
  46. data/lib/abacate_pay/resources/store.rb +36 -0
  47. data/lib/abacate_pay/resources/subscriptions.rb +72 -0
  48. data/lib/abacate_pay/resources/transparents.rb +46 -0
  49. data/lib/abacate_pay/resources/webhook_endpoints.rb +49 -0
  50. data/lib/abacate_pay/resources.rb +27 -0
  51. data/lib/{abacatepay → abacate_pay}/version.rb +2 -2
  52. data/lib/abacate_pay/webhooks/event.rb +20 -0
  53. data/lib/abacate_pay/webhooks.rb +100 -0
  54. data/lib/abacate_pay.rb +111 -4
  55. metadata +69 -56
  56. data/lib/abacatepay/clients/billing_client.rb +0 -60
  57. data/lib/abacatepay/clients/customer_client.rb +0 -39
  58. data/lib/abacatepay/clients.rb +0 -12
  59. data/lib/abacatepay/configuration.rb +0 -56
  60. data/lib/abacatepay/enums.rb +0 -12
  61. data/lib/abacatepay/resources.rb +0 -15
  62. data/sig/abacatepay/rails.rbs +0 -6
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbacatePay
4
+ module Clients
5
+ # Client class for managing billing-related operations in the AbacatePay API.
6
+ class BillingClient < Client
7
+ # API endpoint for billing-related operations
8
+ URI = "billings"
9
+
10
+ # @param client [Faraday::Connection, nil] Optional Faraday client for custom configurations
11
+ # @deprecated Use {CheckoutClient} instead
12
+ def initialize(client = nil)
13
+ warn "[DEPRECATION] BillingClient is deprecated. Use CheckoutClient instead."
14
+ super(URI, client)
15
+ end
16
+
17
+ # Retrieves a list of billings
18
+ #
19
+ # @return [Array<Resources::Billing>] Array of Billing objects
20
+ def list
21
+ response = request("GET", "list")
22
+ Array(response).map { |data| Resources::Billings.new(data) }
23
+ end
24
+
25
+ # Creates a new billing
26
+ #
27
+ # @param data [Resources::Billing] The billing data to be sent for creation
28
+ # @return [Resources::Billing] The created Billing object
29
+ def create(data)
30
+ response = request("POST", "create", json: build_create_payload(data))
31
+ Resources::Billings.new(response)
32
+ end
33
+
34
+ private
35
+
36
+ # Builds the create-billing request payload
37
+ #
38
+ # @param data [Resources::Billings] The billing to serialize
39
+ # @return [Hash] The request payload
40
+ def build_create_payload(data)
41
+ {
42
+ frequency: data.frequency,
43
+ methods: data.methods,
44
+ returnUrl: data.metadata&.return_url,
45
+ completionUrl: data.metadata&.completion_url,
46
+ items: data.products&.map { |product| serialize_product(product) }
47
+ }.merge(serialize_customer(data.customer))
48
+ end
49
+
50
+ # @param product [Resources::Billings::Product] The product to serialize
51
+ # @return [Hash] The product payload
52
+ def serialize_product(product)
53
+ {
54
+ externalId: product.external_id,
55
+ name: product.name,
56
+ description: product.description,
57
+ quantity: product.quantity,
58
+ price: product.price
59
+ }
60
+ end
61
+
62
+ # An existing customer is referenced by id; a new one is sent inline.
63
+ #
64
+ # @param customer [Resources::Customers, nil] The customer to serialize
65
+ # @return [Hash] The customer payload fragment
66
+ def serialize_customer(customer)
67
+ return { customerId: customer.id } if customer&.id
68
+
69
+ {
70
+ customer: {
71
+ name: customer&.metadata&.name,
72
+ email: customer&.metadata&.email,
73
+ cellphone: customer&.metadata&.cellphone,
74
+ taxId: customer&.metadata&.tax_id
75
+ }
76
+ }
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbacatePay
4
+ module Clients
5
+ # Client for one-off checkout sessions in the AbacatePay API.
6
+ #
7
+ # Checkouts are single-payment links; use SubscriptionClient for
8
+ # recurring charges.
9
+ class CheckoutClient < Client
10
+ URI = "checkouts"
11
+
12
+ def initialize(client = nil)
13
+ super(URI, client)
14
+ end
15
+
16
+ # @param params [Hash] Optional filtering params (id, externalId, status, email, taxId, after, before, limit)
17
+ # @return [Array<Resources::Checkouts>]
18
+ def list(**params)
19
+ response = request("GET", "list", params: params.empty? ? nil : params)
20
+ Array(response).map { |data| Resources::Checkouts.new(data) }
21
+ end
22
+
23
+ # @param id [String] Checkout ID
24
+ # @return [Resources::Checkouts]
25
+ def get(id)
26
+ response = request("GET", "get", params: { id: id })
27
+ Resources::Checkouts.new(response)
28
+ end
29
+
30
+ # @param data [Resources::Checkouts]
31
+ # @return [Resources::Checkouts]
32
+ def create(data)
33
+ response = request("POST", "create", json: build_create_payload(data))
34
+ Resources::Checkouts.new(response)
35
+ end
36
+
37
+ # Refunds a paid checkout in full. AbacatePay does not support partial
38
+ # refunds — the original amount is always returned.
39
+ #
40
+ # @param id [String] Public checkout ID (`bill_...`) or charge ID
41
+ # (`char_...`, `pix_char_...`, `card_...`)
42
+ # @return [Resources::Checkouts] The refunded checkout
43
+ def refund(id)
44
+ response = request("POST", "refund", json: { id: id })
45
+ Resources::Checkouts.new(response)
46
+ end
47
+
48
+ private
49
+
50
+ # Builds the create-checkout request payload
51
+ #
52
+ # @param data [Resources::Checkouts] The checkout to serialize
53
+ # @return [Hash] The request payload, with nil entries removed
54
+ def build_create_payload(data)
55
+ customer_id = data.customer&.id
56
+
57
+ {
58
+ frequency: data.frequency,
59
+ methods: data.methods,
60
+ returnUrl: data.metadata&.return_url,
61
+ completionUrl: data.metadata&.completion_url,
62
+ items: data.products&.map { |product| { id: product.external_id, quantity: product.quantity } },
63
+ externalId: data.external_id,
64
+ coupons: data.coupons,
65
+ customerId: customer_id.to_s.empty? ? nil : customer_id
66
+ }.compact
67
+ end
68
+ end
69
+ end
70
+ end
@@ -27,10 +27,14 @@ module AbacatePay
27
27
  def request(method, uri, options = {})
28
28
  response = @client.public_send(method.downcase) do |req|
29
29
  req.url uri
30
+ req.params = options[:params] if options[:params]
30
31
  req.body = options[:json].to_json if options[:json]
31
32
  end
32
33
 
33
- JSON.parse(response.body).fetch("data")
34
+ parsed = JSON.parse(response.body)
35
+ raise ApiError, "API error: #{parsed["error"]}" if parsed["error"]
36
+
37
+ parsed.fetch("data")
34
38
  rescue Faraday::Error => e
35
39
  handle_request_error(e)
36
40
  rescue StandardError => e
@@ -42,11 +46,20 @@ module AbacatePay
42
46
  # @param uri [String] The endpoint URI
43
47
  # @return [Faraday::Connection] Configured Faraday client
44
48
  def build_client(uri)
49
+ configuration = AbacatePay.configuration!
50
+ base_url = uri.empty? ? "#{configuration.api_url}/" : "#{configuration.api_url}/#{uri}/"
51
+
45
52
  Faraday.new(
46
- url: "#{AbacatePay.configuration.api_url}/#{uri}/",
53
+ url: base_url,
47
54
  headers: {
48
55
  "Content-Type" => "application/json",
49
- "Authorization" => "Bearer #{AbacatePay.configuration.api_token}"
56
+ "Authorization" => "Bearer #{configuration.api_token}"
57
+ },
58
+ # Without an explicit timeout a hung gateway blocks the caller's
59
+ # thread indefinitely — inside a Rails request, that is an outage.
60
+ request: {
61
+ timeout: configuration.timeout,
62
+ open_timeout: configuration.timeout
50
63
  }
51
64
  )
52
65
  end
@@ -57,12 +70,12 @@ module AbacatePay
57
70
  # @raise [ApiError] With appropriate error message
58
71
  def handle_request_error(error)
59
72
  error_message = if error.response&.body
60
- response_body = JSON.parse(error.response.body)
61
- response_body["message"] || response_body["error"]
62
- end
73
+ response_body = JSON.parse(error.response.body)
74
+ response_body["message"] || response_body["error"]
75
+ end
63
76
 
64
77
  raise ApiError, "Request error: #{error_message || error.message}"
65
78
  end
66
79
  end
67
80
  end
68
- end
81
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbacatePay
4
+ module Clients
5
+ # Client for discount coupons in the AbacatePay API.
6
+ #
7
+ # Supports creating, listing, deleting and toggling coupons
8
+ # between active and inactive.
9
+ class CouponClient < Client
10
+ URI = "coupons"
11
+
12
+ def initialize(client = nil)
13
+ super(URI, client)
14
+ end
15
+
16
+ # @param params [Hash] Optional pagination params (after, before, limit)
17
+ # @return [Array<Resources::Coupons>]
18
+ def list(**params)
19
+ response = request("GET", "list", params: params.empty? ? nil : params)
20
+ Array(response).map { |data| Resources::Coupons.new(data) }
21
+ end
22
+
23
+ # @param id [String] Coupon ID
24
+ # @return [Resources::Coupons]
25
+ def get(id)
26
+ response = request("GET", "get", params: { id: id })
27
+ Resources::Coupons.new(response)
28
+ end
29
+
30
+ # @param data [Resources::Coupons]
31
+ # @return [Resources::Coupons]
32
+ def create(data)
33
+ response = request("POST", "create", json: {
34
+ code: data.code,
35
+ discount: data.discount,
36
+ discountKind: data.discount_kind,
37
+ maxRedeems: data.max_redeems
38
+ })
39
+ Resources::Coupons.new(response)
40
+ end
41
+
42
+ # @param id [String] Coupon ID
43
+ # @return [Resources::Coupons]
44
+ def delete(id)
45
+ response = request("POST", "delete", json: { id: id })
46
+ Resources::Coupons.new(response)
47
+ end
48
+
49
+ # @param id [String] Coupon ID
50
+ # @return [Resources::Coupons]
51
+ def toggle(id)
52
+ response = request("POST", "toggle", json: { id: id })
53
+ Resources::Coupons.new(response)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbacatePay
4
+ module Clients
5
+ # Client class for managing customer-related operations in the AbacatePay API.
6
+ class CustomerClient < Client
7
+ # API endpoint for customer-related operations
8
+ URI = "customers"
9
+
10
+ # @param client [Faraday::Connection, nil] Optional Faraday client for custom configurations
11
+ def initialize(client = nil)
12
+ super(URI, client)
13
+ end
14
+
15
+ # Retrieves a list of customers
16
+ #
17
+ # @param params [Hash] Optional pagination params (after, before, limit)
18
+ # @return [Array<Resources::Customers>] Array of Customer objects
19
+ def list(**params)
20
+ response = request("GET", "list", params: params.empty? ? nil : params)
21
+ Array(response).map { |data| Resources::Customers.new(data) }
22
+ end
23
+
24
+ # Retrieves a customer by ID
25
+ #
26
+ # @param id [String] The customer ID
27
+ # @return [Resources::Customers] The Customer object
28
+ def get(id)
29
+ response = request("GET", "get", params: { id: id })
30
+ Resources::Customers.new(response)
31
+ end
32
+
33
+ # Creates a new customer
34
+ #
35
+ # @param data [Resources::Customers] The customer data to be sent for creation
36
+ # @return [Resources::Customers] The created Customer object
37
+ def create(data)
38
+ response = request("POST", "create", json: {
39
+ name: data.metadata&.name,
40
+ email: data.metadata&.email,
41
+ cellphone: data.metadata&.cellphone,
42
+ taxId: data.metadata&.tax_id
43
+ })
44
+
45
+ Resources::Customers.new(response)
46
+ end
47
+
48
+ # Deletes a customer
49
+ #
50
+ # @param id [String] The customer ID
51
+ # @return [Resources::Customers] The deleted Customer object
52
+ def delete(id)
53
+ response = request("POST", "delete", json: { id: id })
54
+ Resources::Customers.new(response)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbacatePay
4
+ module Clients
5
+ # Client for reusable payment links in the AbacatePay API.
6
+ #
7
+ # A payment link can be paid by many customers independently — mass sales,
8
+ # raffles, sign-up forms — without creating one checkout per customer.
9
+ # Use CheckoutClient when each customer needs their own charge.
10
+ class PaymentLinkClient < Client
11
+ URI = "payment-links"
12
+
13
+ # Payment links are always multi-payment by definition; the API rejects
14
+ # any other frequency on this endpoint.
15
+ FREQUENCY = "MULTIPLE_PAYMENTS"
16
+
17
+ # @param client [Faraday::Connection, nil] Optional Faraday client
18
+ def initialize(client = nil)
19
+ super(URI, client)
20
+ end
21
+
22
+ # @param params [Hash] Optional pagination params (after, before, limit)
23
+ # @return [Array<Resources::Checkouts>]
24
+ def list(**params)
25
+ response = request("GET", "list", params: params.empty? ? nil : params)
26
+ Array(response).map { |data| Resources::Checkouts.new(data) }
27
+ end
28
+
29
+ # @param id [String] The payment link ID
30
+ # @return [Resources::Checkouts]
31
+ def get(id)
32
+ response = request("GET", "get", params: { id: id })
33
+ Resources::Checkouts.new(response)
34
+ end
35
+
36
+ # Creates a reusable payment link.
37
+ #
38
+ # @param data [Resources::Checkouts] The link definition
39
+ # @return [Resources::Checkouts] The created link, with `url` populated
40
+ def create(data)
41
+ response = request("POST", "create", json: build_create_payload(data))
42
+ Resources::Checkouts.new(response)
43
+ end
44
+
45
+ # Refunds a payment made through the link.
46
+ #
47
+ # @param id [String] Public charge ID
48
+ # @return [Resources::Checkouts] The refunded charge
49
+ def refund(id)
50
+ response = request("POST", "refund", json: { id: id })
51
+ Resources::Checkouts.new(response)
52
+ end
53
+
54
+ private
55
+
56
+ # @param data [Resources::Checkouts] The link to serialize
57
+ # @return [Hash] The request payload, with nil entries removed
58
+ def build_create_payload(data)
59
+ {
60
+ frequency: FREQUENCY,
61
+ methods: data.methods,
62
+ items: data.products&.map { |product| { id: product.external_id, quantity: product.quantity } },
63
+ externalId: data.external_id,
64
+ returnUrl: data.metadata&.return_url,
65
+ completionUrl: data.metadata&.completion_url,
66
+ coupons: data.coupons
67
+ }.compact
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbacatePay
4
+ module Clients
5
+ # Client for payouts (withdrawals) in the AbacatePay API.
6
+ #
7
+ # Payouts move settled balance out of the store account.
8
+ class PayoutClient < Client
9
+ URI = "payouts"
10
+
11
+ def initialize(client = nil)
12
+ super(URI, client)
13
+ end
14
+
15
+ # @param params [Hash] Optional pagination params (after, before, limit)
16
+ # @return [Array<Resources::Payouts>]
17
+ def list(**params)
18
+ response = request("GET", "list", params: params.empty? ? nil : params)
19
+ Array(response).map { |data| Resources::Payouts.new(data) }
20
+ end
21
+
22
+ # @param id [String] Payout ID
23
+ # @return [Resources::Payouts]
24
+ def get(id)
25
+ response = request("GET", "get", params: { id: id })
26
+ Resources::Payouts.new(response)
27
+ end
28
+
29
+ # @param data [Resources::Payouts]
30
+ # @return [Resources::Payouts]
31
+ def create(data)
32
+ response = request("POST", "create", json: {
33
+ amount: data.amount,
34
+ externalId: data.external_id,
35
+ description: data.description
36
+ })
37
+ Resources::Payouts.new(response)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbacatePay
4
+ module Clients
5
+ # Client for outbound PIX transfers in the AbacatePay API.
6
+ #
7
+ # Transfers are sent to a PIX key; see Enums::Pix::KeyTypes for the
8
+ # accepted key formats.
9
+ class PixClient < Client
10
+ URI = "pix"
11
+
12
+ def initialize(client = nil)
13
+ super(URI, client)
14
+ end
15
+
16
+ # @param params [Hash] Optional pagination params (after, before, limit)
17
+ # @return [Array<Resources::PixTransfers>]
18
+ def list(**params)
19
+ response = request("GET", "list", params: params.empty? ? nil : params)
20
+ Array(response).map { |data| Resources::PixTransfers.new(data) }
21
+ end
22
+
23
+ # @param id [String] PIX transfer ID
24
+ # @return [Resources::PixTransfers]
25
+ def get(id)
26
+ response = request("GET", "get", params: { id: id })
27
+ Resources::PixTransfers.new(response)
28
+ end
29
+
30
+ # Sends a PIX transfer to an external key.
31
+ # Named send_pix to avoid conflict with Ruby's Object#send.
32
+ #
33
+ # @param data [Resources::PixTransfers]
34
+ # @return [Resources::PixTransfers]
35
+ def send_pix(data)
36
+ response = request("POST", "send", json: {
37
+ amount: data.amount,
38
+ externalId: data.external_id,
39
+ description: data.description,
40
+ key: data.key,
41
+ keyType: data.key_type
42
+ })
43
+ Resources::PixTransfers.new(response)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbacatePay
4
+ module Clients
5
+ # Client for the product catalogue in the AbacatePay API.
6
+ #
7
+ # Products are the line items referenced by billings and checkouts.
8
+ class ProductClient < Client
9
+ URI = "products"
10
+
11
+ def initialize(client = nil)
12
+ super(URI, client)
13
+ end
14
+
15
+ # @param params [Hash] Optional pagination params (after, before, limit)
16
+ # @return [Array<Resources::Products>]
17
+ def list(**params)
18
+ response = request("GET", "list", params: params.empty? ? nil : params)
19
+ Array(response).map { |data| Resources::Products.new(data) }
20
+ end
21
+
22
+ # @param id [String] Product ID or externalId
23
+ # @return [Resources::Products]
24
+ def get(id)
25
+ response = request("GET", "get", params: { id: id })
26
+ Resources::Products.new(response)
27
+ end
28
+
29
+ # @param data [Resources::Products]
30
+ # @return [Resources::Products]
31
+ def create(data)
32
+ request_data = {
33
+ externalId: data.external_id,
34
+ name: data.name,
35
+ price: data.price,
36
+ currency: data.currency || "BRL",
37
+ description: data.description
38
+ }
39
+ request_data[:imageUrl] = data.image_url if data.image_url && !data.image_url.to_s.empty?
40
+ request_data[:cycle] = data.cycle if data.cycle && !data.cycle.to_s.empty?
41
+
42
+ response = request("POST", "create", json: request_data)
43
+ Resources::Products.new(response)
44
+ end
45
+
46
+ # @param id [String] Product ID
47
+ # @return [Resources::Products]
48
+ def delete(id)
49
+ response = request("POST", "delete", json: { id: id })
50
+ Resources::Products.new(response)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbacatePay
4
+ module Clients
5
+ # Client for store-level data in the AbacatePay API.
6
+ #
7
+ # Exposes the account balance and revenue reporting for a date range.
8
+ class StoreClient < Client
9
+ def initialize(client = nil)
10
+ super("", client)
11
+ end
12
+
13
+ # @return [Resources::Store]
14
+ def get
15
+ response = request("GET", "store/get")
16
+ Resources::Store.new(response)
17
+ end
18
+
19
+ # @return [Hash] Merchant info
20
+ def merchant_info
21
+ request("GET", "public-mrr/merchant-info")
22
+ end
23
+
24
+ # @return [Hash] MRR metrics
25
+ def mrr
26
+ request("GET", "public-mrr/mrr")
27
+ end
28
+
29
+ # @param start_date [String] Start date (YYYY-MM-DD)
30
+ # @param end_date [String] End date (YYYY-MM-DD)
31
+ # @return [Hash] Revenue data
32
+ def revenue(start_date:, end_date:)
33
+ request("GET", "public-mrr/revenue", params: {
34
+ startDate: start_date,
35
+ endDate: end_date
36
+ })
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AbacatePay
4
+ module Clients
5
+ # Client for recurring subscriptions in the AbacatePay API.
6
+ #
7
+ # See Enums::Products::Cycles for the supported billing cycles.
8
+ class SubscriptionClient < Client
9
+ URI = "subscriptions"
10
+
11
+ def initialize(client = nil)
12
+ super(URI, client)
13
+ end
14
+
15
+ # @param params [Hash] Optional pagination params (after, before, limit)
16
+ # @return [Array<Resources::Subscriptions>]
17
+ def list(**params)
18
+ response = request("GET", "list", params: params.empty? ? nil : params)
19
+ Array(response).map { |data| Resources::Subscriptions.new(data) }
20
+ end
21
+
22
+ # @param data [Resources::Subscriptions]
23
+ # @return [Resources::Subscriptions]
24
+ def create(data)
25
+ request_data = {
26
+ methods: data.methods,
27
+ externalId: data.external_id,
28
+ customerId: data.customer&.id,
29
+ items: data.products&.map do |product|
30
+ {
31
+ externalId: product.external_id,
32
+ name: product.name,
33
+ description: product.description,
34
+ quantity: product.quantity,
35
+ price: product.price
36
+ }
37
+ end
38
+ }
39
+
40
+ response = request("POST", "create", json: request_data)
41
+ Resources::Subscriptions.new(response)
42
+ end
43
+
44
+ # Cancels an active subscription immediately. Pending future instalments
45
+ # are cancelled along with it.
46
+ #
47
+ # @param id [String] The subscription ID (`subs_...`)
48
+ # @return [Resources::Subscriptions] The cancelled subscription
49
+ def cancel(id)
50
+ response = request("POST", "cancel", json: { id: id })
51
+ Resources::Subscriptions.new(response)
52
+ end
53
+ end
54
+ end
55
+ end