abacatepay-ruby 0.1.0 → 1.1.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +136 -0
  3. data/CHANGELOG.md +171 -1
  4. data/README.md +491 -87
  5. data/Rakefile +1 -1
  6. data/abacatepay-ruby.gemspec +17 -10
  7. data/lib/abacate_pay/clients/billing_client.rb +90 -0
  8. data/lib/abacate_pay/clients/checkout_client.rb +91 -0
  9. data/lib/abacate_pay/clients/client.rb +202 -0
  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 +90 -0
  18. data/lib/abacate_pay/clients/transparent_client.rb +125 -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/collection.rb +98 -0
  22. data/lib/abacate_pay/configuration.rb +83 -0
  23. data/lib/{abacatepay/enums/billing → abacate_pay/enums/billings}/frequencies.rb +9 -3
  24. data/lib/{abacatepay/enums/billing → abacate_pay/enums/billings}/methods.rb +12 -3
  25. data/lib/{abacatepay/enums/billing → abacate_pay/enums/billings}/statuses.rb +3 -2
  26. data/lib/abacate_pay/enums/checkouts/statuses.rb +29 -0
  27. data/lib/abacate_pay/enums/coupons/discount_kinds.rb +26 -0
  28. data/lib/abacate_pay/enums/coupons/statuses.rb +27 -0
  29. data/lib/abacate_pay/enums/payouts/statuses.rb +29 -0
  30. data/lib/abacate_pay/enums/pix/key_types.rb +30 -0
  31. data/lib/abacate_pay/enums/products/cycles.rb +28 -0
  32. data/lib/abacate_pay/enums/transfers/statuses.rb +30 -0
  33. data/lib/abacate_pay/enums/webhooks/event_types.rb +50 -0
  34. data/lib/abacate_pay/enums.rb +20 -0
  35. data/lib/{abacatepay/resources/billing → abacate_pay/resources/billings}/metadata.rb +2 -8
  36. data/lib/{abacatepay/resources/billing → abacate_pay/resources/billings}/product.rb +2 -8
  37. data/lib/{abacatepay/resources/billing.rb → abacate_pay/resources/billings.rb} +16 -16
  38. data/lib/abacate_pay/resources/checkouts.rb +84 -0
  39. data/lib/abacate_pay/resources/coupons.rb +41 -0
  40. data/lib/{abacatepay/resources/customer → abacate_pay/resources/customers}/metadata.rb +2 -8
  41. data/lib/{abacatepay/resources/customer.rb → abacate_pay/resources/customers.rb} +5 -5
  42. data/lib/abacate_pay/resources/payouts.rb +40 -0
  43. data/lib/abacate_pay/resources/pix_transfers.rb +41 -0
  44. data/lib/abacate_pay/resources/products.rb +40 -0
  45. data/lib/{abacatepay → abacate_pay}/resources/resource.rb +33 -21
  46. data/lib/abacate_pay/resources/store/balance.rb +20 -0
  47. data/lib/abacate_pay/resources/store.rb +36 -0
  48. data/lib/abacate_pay/resources/subscriptions.rb +72 -0
  49. data/lib/abacate_pay/resources/transparents.rb +53 -0
  50. data/lib/abacate_pay/resources/webhook_endpoints.rb +49 -0
  51. data/lib/abacate_pay/resources.rb +27 -0
  52. data/lib/{abacatepay → abacate_pay}/version.rb +2 -2
  53. data/lib/abacate_pay/webhooks/event.rb +20 -0
  54. data/lib/abacate_pay/webhooks.rb +100 -0
  55. data/lib/abacate_pay.rb +112 -4
  56. metadata +75 -47
  57. data/lib/abacatepay/clients/billing_client.rb +0 -60
  58. data/lib/abacatepay/clients/client.rb +0 -68
  59. data/lib/abacatepay/clients/customer_client.rb +0 -39
  60. data/lib/abacatepay/clients.rb +0 -12
  61. data/lib/abacatepay/configuration.rb +0 -56
  62. data/lib/abacatepay/enums.rb +0 -12
  63. data/lib/abacatepay/resources.rb +0 -15
  64. data/sig/abacatepay/rails.rbs +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec476ed39de31a64c6de7517af308b971922f06a8a554de6764ea4992052b641
4
- data.tar.gz: cf82e3363dfa431419b29b120fcabddb671d91e3789ec90880dd131b46a08684
3
+ metadata.gz: 604226a94737477c55324bf08cb81736cce65fdf482453bd2667572c8986b010
4
+ data.tar.gz: 4e292b8d7db21a444cc4348bd59bc2cf396849ff8d99db4d4463ab1b651962fe
5
5
  SHA512:
6
- metadata.gz: 84f338ecbd8a318c44987d7417579f812b5002c66eb04929e59511b61a4de9a378df861449ce98d7d24753cf4ef8bd373886b6ce602fd53c500bcca5f5254274
7
- data.tar.gz: 7e87dd6972d35d967bb1cece1e825579f96a26b0da3f624ead50734195bcf15c193674a5c0e6cbffd1e167ea28a8ee2fb63f6d78ec4151633ed4037445b0825d
6
+ metadata.gz: 55eeee33fcf8a4b509c849f85d90ccb3f0fb4a2b4aad040325da4a6ed19bf2b60394190ce848ff0b03fa98ec56b0bddccf2fae677c19b1db1dfc7c744819286a
7
+ data.tar.gz: e8258c5ba01696d7d76fb85b4314a047052b04f13e0a90c4a826fe83f14ce967ea817cb445d3158a61be27a6ec4af99da9fc0c4dd477449c1bf1b60412f7f612
data/.rubocop.yml CHANGED
@@ -0,0 +1,136 @@
1
+ plugins:
2
+ - rubocop-rspec
3
+
4
+ AllCops:
5
+ TargetRubyVersion: 3.2
6
+ NewCops: enable
7
+ SuggestExtensions: false
8
+ Exclude:
9
+ - "vendor/**/*"
10
+ - "pkg/**/*"
11
+ - "tmp/**/*"
12
+ - "sig/**/*"
13
+
14
+ # The codebase uses double quotes consistently (Bundler gem template default).
15
+ Style/StringLiterals:
16
+ EnforcedStyle: double_quotes
17
+
18
+ Style/StringLiteralsInInterpolation:
19
+ EnforcedStyle: double_quotes
20
+
21
+ Layout/LineLength:
22
+ Max: 120
23
+
24
+ # Resources subclass Resource, which intentionally has no #initialize —
25
+ # it is a behaviour mixin, not a stateful base class. Calling super would
26
+ # only reach Object#initialize, which is a no-op.
27
+ Lint/MissingSuper:
28
+ Exclude:
29
+ - "lib/abacate_pay/resources/**/*.rb"
30
+ - "spec/**/*"
31
+
32
+ # Test doubles exist precisely to expose or stub inherited behaviour.
33
+ Lint/UselessMethodDefinition:
34
+ Exclude:
35
+ - "spec/**/*"
36
+
37
+ RSpec/MultipleMemoizedHelpers:
38
+ Max: 10
39
+
40
+ # Enums expose a `.values` class method returning an Array. The cop assumes a
41
+ # Hash and would rewrite it to `each_value`, which does not exist there.
42
+ Style/HashEachMethods:
43
+ AllowedReceivers:
44
+ - described_class
45
+
46
+ # Enum modules are self-documenting constant tables; a doc comment per
47
+ # module adds noise without adding information.
48
+ Style/Documentation:
49
+ Exclude:
50
+ - "lib/abacate_pay/enums/**/*.rb"
51
+
52
+ Naming/MethodParameterName:
53
+ AllowedNames:
54
+ - id
55
+
56
+ # `verify!` raises on failure and returns true on success — the bang, not a
57
+ # question mark, is the right signal. `valid?` is the predicate counterpart.
58
+ Naming/PredicateMethod:
59
+ AllowedMethods:
60
+ - verify!
61
+
62
+ # Faraday errors carry a response payload, not just a message; the compact
63
+ # form is how Faraday itself documents constructing them.
64
+ Style/RaiseArgs:
65
+ AllowedCompactTypes:
66
+ - Faraday::ConnectionFailed
67
+ - Faraday::TimeoutError
68
+ - Faraday::ClientError
69
+ - Faraday::ServerError
70
+
71
+ # Development dependencies live in the gemspec so a single file describes the
72
+ # gem completely.
73
+ Gemspec/DevelopmentDependencies:
74
+ EnforcedStyle: gemspec
75
+
76
+ Metrics/MethodLength:
77
+ Max: 25
78
+ Exclude:
79
+ - "spec/**/*"
80
+
81
+ Metrics/AbcSize:
82
+ Max: 25
83
+ Exclude:
84
+ - "spec/**/*"
85
+
86
+ Metrics/CyclomaticComplexity:
87
+ Max: 12
88
+
89
+ Metrics/PerceivedComplexity:
90
+ Max: 12
91
+
92
+ Metrics/ClassLength:
93
+ Max: 200
94
+ Exclude:
95
+ - "spec/**/*"
96
+
97
+ # RSpec describe/context blocks are naturally long; length is not a
98
+ # meaningful signal there.
99
+ Metrics/BlockLength:
100
+ Exclude:
101
+ - "spec/**/*"
102
+ - "*.gemspec"
103
+
104
+ Metrics/ParameterLists:
105
+ Exclude:
106
+ - "lib/abacate_pay/resources/**/*.rb"
107
+
108
+ RSpec/ExampleLength:
109
+ Max: 20
110
+
111
+ RSpec/MultipleExpectations:
112
+ Max: 10
113
+
114
+ RSpec/NestedGroups:
115
+ Max: 4
116
+
117
+ RSpec/SpecFilePathFormat:
118
+ Enabled: false
119
+
120
+ # These specs deliberately cut across the whole library rather than describing
121
+ # a single class.
122
+ RSpec/DescribeClass:
123
+ Exclude:
124
+ - "spec/clients/request_contract_spec.rb"
125
+ - "spec/clients/pagination_spec.rb"
126
+ - "spec/clients/resilience_spec.rb"
127
+ - "spec/clients/logging_spec.rb"
128
+ - "spec/clients/boleto_spec.rb"
129
+ - "spec/packaging_spec.rb"
130
+
131
+ # `has_more?` mirrors the API's own `hasMore` field and matches the convention
132
+ # other payment SDKs use for cursor pagination. `more?` would read as a
133
+ # different question.
134
+ Naming/PredicatePrefix:
135
+ AllowedMethods:
136
+ - has_more?
data/CHANGELOG.md CHANGED
@@ -1,5 +1,175 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
1
8
  ## [Unreleased]
2
9
 
3
- ## [0.1.0] - 2024-12-11
10
+ ## [1.1.0] - 2026-08-05
11
+
12
+ ### Added
13
+
14
+ - **BOLETO support.** The method was rejected outright by the `Billings::Methods`
15
+ enum even though it is a first-class payment method in the v2 API. Adds the
16
+ enum value, the boleto-only `due_date`/`interest`/`fine` fields on checkouts,
17
+ and a `method:` argument on `TransparentClient#create` (which previously
18
+ hard-coded PIX). Boleto responses now expose `bar_code`, `url`, `br_code`,
19
+ `br_code_base64` and `expires_at`.
20
+ - **Cursor pagination.** List endpoints cap at 100 items and report `hasMore`
21
+ plus a cursor; the SDK discarded that metadata, making record 101
22
+ unreachable. `list` now returns an `AbacatePay::Collection` — Enumerable and
23
+ Array-compatible, so existing code is unaffected — carrying `has_more?` and
24
+ `next_cursor`. `each_page` and `auto_paging_each` walk every page.
25
+ - **Retries with exponential backoff and jitter** on 429 and 5xx, configurable
26
+ via `config.max_retries` (default 2). Only idempotent methods are retried;
27
+ POST never is, because repeating `checkouts/create` after a timeout could
28
+ charge a customer twice and the API exposes no idempotency key.
29
+ - **Optional request logging** via `config.logger`, with the bearer token
30
+ redacted and bodies never logged.
31
+ - `SubscriptionClient#change_plan` and `#record_usage` — the last two of the 45
32
+ documented v2 endpoints. All 45 are now covered.
33
+ - `subscription.payment_failed` and `subscription.trial_started` webhook event
34
+ types. `payment_failed` is the dunning signal.
35
+ - Checkout fields the v2 API accepts but the SDK never sent: `max_installments`
36
+ (nested under `card`), `up_sell_product_id` and `custom_metadata`.
37
+ - A `User-Agent` identifying the SDK and Ruby version.
38
+
39
+ ### Fixed
40
+
41
+ - `Webhooks.parse` aside, malformed API responses raised `JSON::ParserError`
42
+ and a missing `data` field raised `KeyError`; both are now `ApiError`.
43
+
44
+ ### Changed
45
+
46
+ - `BillingClient`'s deprecation warning now states that its `/billings/*`
47
+ endpoints do not exist on either API version, so every call fails. It will be
48
+ removed in 2.0.0.
49
+
50
+ ### Corrected
51
+
52
+ - The 1.0.0 notes stated that the v1 API "has been retired and answers Not found
53
+ for every path". That is wrong: v1 is still served, under a different dialect
54
+ — singular paths (`/v1/billing/`, `/v1/customer/`) and different resource
55
+ names (`pixQrCode`). The original diagnosis tested v2-shaped paths against
56
+ `/v1`. The fix itself stands: this SDK only ever spoke v2's dialect, so 10 of
57
+ the 12 paths it calls do not exist on v1 and routing there produced 404s.
58
+
59
+ ## [1.0.0] - 2026-08-05
60
+
61
+ First stable release. The public surface is now covered by CI on four Ruby
62
+ versions and will not change without a major bump.
63
+
64
+ ### Added
65
+
66
+ - Full API coverage: checkouts, coupons, customers, payouts, PIX transfers,
67
+ products, store, subscriptions and transparent checkout.
68
+ - `AbacatePay::Webhooks.construct_event` — verifies the signature and parses the
69
+ body in a single call, so an unverified payload cannot be acted on.
70
+ - `AbacatePay::Webhooks::PayloadError` for malformed or non-object webhook bodies.
71
+ - `PaymentLinkClient` (`AbacatePay.payment_links`) — reusable multi-payment links.
72
+ - `WebhookClient` (`AbacatePay.webhook_endpoints`) — webhook endpoint registration,
73
+ with local HTTPS validation so a bad endpoint fails before the round trip.
74
+ - `CheckoutClient#refund`, `TransparentClient#refund`, `PaymentLinkClient#refund` —
75
+ refunds were previously impossible through the SDK.
76
+ - `SubscriptionClient#cancel` — a subscription created through the SDK could not
77
+ be cancelled through it.
78
+ - CI workflow running the suite on Ruby 3.2, 3.3, 3.4 and 4.0, plus RuboCop, a
79
+ dependency audit and a gem build check on every pull request.
80
+ - Request-contract specs that let the client build its own connection, asserting
81
+ the real endpoint path, bearer token, timeout and outgoing JSON body. The
82
+ existing client specs injected their own Faraday connection, so a wrong
83
+ endpoint path could not fail them.
84
+ - Coverage measurement: simplecov has been a declared dependency since 0.1.0
85
+ without ever being loaded. Line coverage is now 99.13% with a 90% floor.
86
+ - Dependabot for bundler and GitHub Actions.
87
+
88
+ ### Fixed
89
+
90
+ - **The SDK routed to a retired API.** `api_url` derived the version from the
91
+ token prefix and fell back to `/v1` for any format it did not recognise. The
92
+ v1 prefix has been retired and answers
93
+ `{"success":false,"data":null,"error":"Not found"}` on every path, so anyone
94
+ holding a token in an older format failed 100% of calls with a message that
95
+ did not point at the cause. There is one base URL now:
96
+ `https://api.abacatepay.com/v2`.
97
+ - **Three source files were missing from the built gem.** The gemspec builds its
98
+ file list from `git ls-files`, so a file nobody had staged was silently
99
+ dropped from the package; because the manifests require every component
100
+ explicitly, `require "abacate_pay"` then raised `LoadError` on the installed
101
+ gem while the whole suite stayed green against the working tree. A packaging
102
+ spec now asserts that every file under `lib/` ships.
103
+ - **Using the SDK before calling `configure` raised `NoMethodError`.** The most
104
+ common first-run mistake surfaced as `undefined method 'api_url' for nil` from
105
+ inside the client instead of naming the missing call. It is now a
106
+ `ConfigurationError` that says what to do.
107
+ - **Assigning `AbacatePay.configuration` directly left stale clients behind.**
108
+ `configure` discarded memoized clients but the raw writer did not, so a client
109
+ built against replaced credentials outlived them.
110
+ - **Webhook verification crashed on a missing signature header.** A request
111
+ without `X-Webhook-Signature` reached `secure_compare` as `nil` and raised
112
+ `NoMethodError`, so `valid?` returned neither `true` nor `false` and the
113
+ endpoint returned a server error instead of rejecting the request. Missing and
114
+ empty signatures — and a missing secret — are now `SignatureError`.
115
+ - **`Webhooks.parse` leaked parser internals.** Malformed JSON raised
116
+ `JSON::ParserError` and a non-object JSON body raised `TypeError`; both now
117
+ raise `PayloadError`.
118
+ - **`config.timeout` was dead configuration.** It was documented and settable but
119
+ never reached Faraday, so a hung gateway blocked the calling thread forever.
120
+ It now sets both the read and open timeout.
121
+ - **Changing the token at runtime had no effect.** Clients were memoized on first
122
+ use and never rebuilt, so `AbacatePay.configure` after a first API call kept
123
+ sending the previous bearer token. `configure` now discards memoized clients.
124
+ - `Configuration#api_url` was declared twice — as an `attr_reader` and as a
125
+ method. The dead reader has been removed.
126
+ - `CustomerClient#get` and `#delete` had no test coverage at all.
127
+ - The `customer` and `billing` specs stubbed singular endpoint paths while the
128
+ clients call the plural ones; the specs never noticed because they bypassed
129
+ connection building.
130
+ - Component loading no longer depends on a `Dir` glob whose correctness rested on
131
+ alphabetical ordering placing `client.rb` before its subclasses. The three
132
+ manifest files were also missing every class added after 0.1.0.
133
+
134
+ ### Security
135
+
136
+ - **faraday is now `~> 2.14, >= 2.14.3`**, excluding CVE-2026-54297 (High): an
137
+ uncontrolled recursion in `NestedParamsEncoder` allowing stack-exhaustion DoS
138
+ via deeply nested query parameters. The previous `~> 2.9` constraint let
139
+ consumers resolve to a vulnerable version.
140
+ - Webhook handling no longer crashes on unauthenticated input (see Fixed).
141
+
142
+ ### Changed
143
+
144
+ - **BREAKING: `required_ruby_version` is now `>= 3.2.0`** (was `>= 2.6.0`). The
145
+ old floor was never installable — faraday 2.x requires Ruby 3.0 or newer — so
146
+ no working installation is losing support, but a `bundle update` on Ruby 2.6
147
+ or 3.1 will now refuse to resolve instead of failing later.
148
+ - **`config.environment` is deprecated and now warns.** It was declared,
149
+ defaulted, and validated, but read by nothing. Per AbacatePay's own
150
+ documentation the environment is decided by the API key — Dev mode keys
151
+ simulate transactions — so the setting could never have worked. It is kept as
152
+ an accepted no-op so existing initializers keep loading.
153
+ - `validate!` no longer rejects unknown `environment` values, and now rejects an
154
+ empty or whitespace-only token.
155
+ - Releases publish from a `v*` tag instead of every push to `main`, and verify
156
+ that the tag matches `AbacatePay::VERSION` before publishing.
157
+ - Publishing uses RubyGems Trusted Publishing (OIDC) instead of a long-lived API
158
+ key written to `~/.gem/credentials`.
159
+ - `rubygems_mfa_required` is enabled on the gem.
160
+
161
+ ### Removed
162
+
163
+ - Publishing to GitHub Packages. The step never executed successfully in any run
164
+ and nothing consumed the gem from that registry.
165
+ - `sig/abacatepay/rails.rbs`. Leftover `bundle gem` scaffolding declaring an
166
+ `Abacatepay::Rails` module that does not exist in this codebase — and it was
167
+ shipping inside the published gem, where a type checker would read it.
168
+
169
+ ## [0.1.0] - 2024-12-13
4
170
 
5
171
  - Initial release
172
+
173
+ [1.1.0]: https://github.com/AbacatePay/abacatepay-ruby-sdk/releases/tag/v1.1.0
174
+ [1.0.0]: https://github.com/AbacatePay/abacatepay-ruby-sdk/releases/tag/v1.0.0
175
+ [0.1.0]: https://github.com/AbacatePay/abacatepay-ruby-sdk/releases/tag/v0.1.0