paddle 2.2.1 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6224fc2a3ec9423990985241a118780185a090617407f95e97bb542120eb9323
4
- data.tar.gz: 03403341d8dce73c881ed3fc2c722cf738b625803c6352dd2cca15e5d4cd65a7
3
+ metadata.gz: 8f5aba3cef837ddde090eb1759279427dcf213115c5950b18c167cbea59f4de3
4
+ data.tar.gz: 8bd53cd25b7d29113a5f5e942bac4457afd976cca2995ba232ce888051dc5367
5
5
  SHA512:
6
- metadata.gz: 6453da01350a80adf7f8723817b7d45edfac636d5ae72ebeb92b281ad93d85d59866631eb60a17c3a8225f5a8f51780b857e1aeab940c0264daaa53446a70637
7
- data.tar.gz: 716ef3061039df26a5cfc0dd0f8931cb1e9edcf964947047acf37ed8b95cc63a3cc36595288f6aaccc45ee2b1e3f8fe0b74ae39969123f2c99eeeeea9da41381
6
+ metadata.gz: e844d8f2c15d7c7d0ad0de2d6dadc566fee298b5a1e1930529b709f9532cf673efc7258e02fe9b636338ed9e7730f90a1a293e71faa7d71293e1c1996c479ac5
7
+ data.tar.gz: 895d3532f3a0ddf935427e186d5a06e28fd7e5af610594d88540b009887023683c0ae0ad4cf06bacbef7c7c7ab9f09e29d3931e65f898b779ccd0ee888623a1c
data/.rubocop.yml CHANGED
@@ -6,3 +6,6 @@ inherit_gem: { rubocop-rails-omakase: rubocop.yml }
6
6
  # # Use `[a, [b, c]]` not `[ a, [ b, c ] ]`
7
7
  # Layout/SpaceInsideArrayLiteralBrackets:
8
8
  # Enabled: false
9
+
10
+ Rails/RefuteMethods:
11
+ Enabled: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- paddle (2.2.1)
4
+ paddle (2.4.0)
5
5
  faraday (~> 2.0)
6
6
 
7
7
  GEM
@@ -40,9 +40,9 @@ GEM
40
40
  ast (~> 2.4.1)
41
41
  racc
42
42
  racc (1.8.0)
43
- rack (3.1.3)
43
+ rack (3.1.7)
44
44
  rainbow (3.1.1)
45
- rake (13.0.6)
45
+ rake (13.2.1)
46
46
  regexp_parser (2.9.2)
47
47
  rexml (3.3.0)
48
48
  strscan
data/README.md CHANGED
@@ -7,7 +7,7 @@ The easiest and most complete Ruby library for the Paddle APIs, both Classic and
7
7
  Add this line to your application's Gemfile:
8
8
 
9
9
  ```ruby
10
- gem "paddle", "~> 2.2"
10
+ gem "paddle", "~> 2.3"
11
11
  ```
12
12
 
13
13
  ## Billing API
@@ -77,6 +77,14 @@ Paddle::Product.list(per_page: 10, after: "abc123")
77
77
  >
78
78
  > The Paddle API doesn't take `nil` values for optional parameters. If you want to remove a value, you'll need to pass `"null"` instead.
79
79
 
80
+ ### Updating records
81
+
82
+ For API endpoints that support it, you can use the `update` method to update a record, like so:
83
+
84
+ ```ruby
85
+ Paddle::Product.retrieve(id: "pro_abc123").update(name: "My New Name")
86
+ ```
87
+
80
88
  ### Products
81
89
 
82
90
  ```ruby
@@ -93,10 +101,12 @@ Paddle::Product.create(name: "My SAAS Plan", tax_category: "saas")
93
101
  Paddle::Product.create(name: "My Standard Product", tax_category: "standard")
94
102
 
95
103
  # Retrieve a product
96
- Paddle::Product.retrieve(id: "pro_abc123")
104
+ product = Paddle::Product.retrieve(id: "pro_abc123")
97
105
 
98
106
  # Update a product
99
107
  # https://developer.paddle.com/api-reference/products/update-product
108
+ product.update(description: "This is a plan")
109
+ # or
100
110
  Paddle::Product.update(id: "pro_abc123", description: "This is a plan")
101
111
  ```
102
112
 
@@ -116,10 +126,12 @@ Paddle::Price.list(product_id: "pro_abc123")
116
126
  Paddle::Price.create(product_id: "pro_abc123", description: "A one off price", amount: "1000", currency: "GBP")
117
127
 
118
128
  # Retrieve a price
119
- Paddle::Price.retrieve(id: "pri_123abc")
129
+ price = Paddle::Price.retrieve(id: "pri_123abc")
120
130
 
121
131
  # Update a price
122
132
  # https://developer.paddle.com/api-reference/prices/update-price
133
+ price.update(description: "An updated description")
134
+ # or
123
135
  Paddle::Price.update(id: "pri_123abc", description: "An updated description")
124
136
  ```
125
137
 
@@ -149,10 +161,12 @@ Paddle::Discount.create(description: "$5 off", type: "flat", amount: "500", curr
149
161
  Paddle::Discount.create(description: "10% Off", type: "percentage", amount: "10", code: "10OFF")
150
162
 
151
163
  # Retrieve a discount
152
- Paddle::Discount.retrieve(id: "dsc_abc123")
164
+ discount = Paddle::Discount.retrieve(id: "dsc_abc123")
153
165
 
154
166
  # Update a discount
155
167
  # https://developer.paddle.com/api-reference/discounts/update-discount
168
+ discount.update(description: "An updated description")
169
+ # or
156
170
  Paddle::Discount.update(id: "dsc_abc123", description: "An updated description")
157
171
  ```
158
172
 
@@ -167,13 +181,16 @@ Paddle::Customer.list(email: "me@mydomain.com")
167
181
 
168
182
  # Create a customer
169
183
  # https://developer.paddle.com/api-reference/customers/create-customer
184
+ # Returns a Paddle::Errors::ConflictError if the email is already used on Paddle
170
185
  Paddle::Customer.create(email: "myemail@mydomain.com", name: "Customer Name")
171
186
 
172
187
  # Retrieve a customer
173
- Paddle::Customer.retrieve(id: "ctm_abc123")
188
+ customer = Paddle::Customer.retrieve(id: "ctm_abc123")
174
189
 
175
190
  # Update a customer
176
191
  # https://developer.paddle.com/api-reference/customers/update-customer
192
+ customer.update(status: "archived")
193
+ # or
177
194
  Paddle::Customer.update(id: "ctm_abc123", status: "archived")
178
195
 
179
196
  # Retrieve credit balance for a customer
@@ -193,10 +210,12 @@ Paddle::Address.list(customer: "ctm_abc123")
193
210
  Paddle::Address.create(customer: "ctm_abc123", country_code: "GB", postal_code: "SW1A 2AA")
194
211
 
195
212
  # Retrieve an address
196
- Paddle::Address.retrieve(customer: "ctm_abc123", id: "add_abc123")
213
+ address = Paddle::Address.retrieve(customer: "ctm_abc123", id: "add_abc123")
197
214
 
198
215
  # Update an address
199
216
  # https://developer.paddle.com/api-reference/addresses/update-address
217
+ address.update(status: "archived")
218
+ # or
200
219
  Paddle::Address.update(customer: "ctm_abc123", id: "add_abc123", status: "archived")
201
220
  ```
202
221
 
@@ -212,10 +231,12 @@ Paddle::Business.list(customer: "ctm_abc123")
212
231
  Paddle::Business.create(customer: "ctm_abc123", name: "My Ltd Company")
213
232
 
214
233
  # Retrieve a business
215
- Paddle::Business.retrieve(customer: "ctm_abc123", id: "biz_abc123")
234
+ business = Paddle::Business.retrieve(customer: "ctm_abc123", id: "biz_abc123")
216
235
 
217
236
  # Update a business
218
237
  # https://developer.paddle.com/api-reference/businesses/update-business
238
+ business.update(status: "archived")
239
+ # or
219
240
  Paddle::Business.update(customer: "ctm_abc123", id: "biz_abc123", status: "archived")
220
241
  ```
221
242
 
@@ -237,10 +258,12 @@ Paddle::Transaction.retrieve(id: "txn_abc123")
237
258
 
238
259
  # Retrieve a transaction with extra information
239
260
  # extra can be either "address", "adjustment", "adjustments_totals", "business", "customer", "discount"
240
- Paddle::Transaction.retrieve(id: "txn_abc123", extra: "customer")
261
+ transaction = Paddle::Transaction.retrieve(id: "txn_abc123", extra: "customer")
241
262
 
242
263
  # Update a transaction
243
264
  # https://developer.paddle.com/api-reference/transaction/update-transaction
265
+ transaction.update(items: [ { price_id: "pri_abc123", quantity: 2 } ])
266
+ # or
244
267
  Paddle::Transaction.update(id: "txn_abc123", items: [ { price_id: "pri_abc123", quantity: 2 } ])
245
268
 
246
269
  # Preview a transaction
@@ -271,7 +294,7 @@ Paddle::Subscription.retrieve(id: "sub_abc123")
271
294
 
272
295
  # Retrieve a subscription with extra information
273
296
  # extra can be either "next_transaction" or "recurring_transaction_details"
274
- Paddle::Subscription.retrieve(id: "sub_abc123", extra: "next_transaction")
297
+ subscription = Paddle::Subscription.retrieve(id: "sub_abc123", extra: "next_transaction")
275
298
 
276
299
  # Preview an update to a subscription
277
300
  # https://developer.paddle.com/api-reference/subscriptions/preview-subscription
@@ -279,6 +302,8 @@ Paddle::Subscription.preview(id: "sub_abc123", items: [ { price_id: "pri_123abc"
279
302
 
280
303
  # Update a subscription
281
304
  # https://developer.paddle.com/api-reference/subscriptions/update-subscription
305
+ subscription.update(billing_details: {purchase_order_number: "PO-1234"})
306
+ # or
282
307
  Paddle::Subscription.update(id: "sub_abc123", billing_details: {purchase_order_number: "PO-1234"})
283
308
 
284
309
  # Get a transaction to update payment method
@@ -358,7 +383,7 @@ Used for creating webhook and email notifications
358
383
  Paddle::NotificationSetting.list
359
384
 
360
385
  # Retrieve a notification setting
361
- Paddle::NotificationSetting.retrieve(id: "ntfset_abc123")
386
+ setting = Paddle::NotificationSetting.retrieve(id: "ntfset_abc123")
362
387
 
363
388
  # Create a notification setting
364
389
  # https://developer.paddle.com/api-reference/notification-settings/create-notification-setting
@@ -374,6 +399,8 @@ Paddle::NotificationSetting.create(
374
399
 
375
400
  # Update a notification setting
376
401
  # https://developer.paddle.com/api-reference/notification-settings/update-notification-setting
402
+ setting.update(subscribed_events: %w[subscription.activated transaction.completed transaction.billed])
403
+ # or
377
404
  Paddle::NotificationSetting.update(id: "ntfset_abc123",
378
405
  subscribed_events: [
379
406
  "subscription.activated",
@@ -0,0 +1,6 @@
1
+ module Paddle
2
+ module Classic
3
+ class Error < StandardError
4
+ end
5
+ end
6
+ end
data/lib/paddle/client.rb CHANGED
@@ -1,66 +1,61 @@
1
+ require "faraday"
2
+
1
3
  module Paddle
2
4
  class Client
3
5
  class << self
4
6
  def connection
5
- @connection ||= Faraday.new(Paddle.config.url) do |conn|
6
- conn.request :authorization, :Bearer, Paddle.config.api_key
7
-
8
- conn.headers = {
9
- "User-Agent" => "paddle/v#{VERSION} (github.com/deanpcmad/paddle)",
10
- "Paddle-Version" => Paddle.config.version.to_s
11
- }
12
-
13
- conn.request :json
14
- conn.response :json
15
- end
7
+ @connection ||= create_connection
16
8
  end
17
9
 
18
-
19
10
  def get_request(url, params: {}, headers: {})
20
- handle_response connection.get(url, params, headers)
11
+ handle_response(connection.get(url, params, headers))
21
12
  end
22
13
 
23
14
  def post_request(url, body: {}, headers: {})
24
- handle_response connection.post(url, body, headers)
15
+ handle_response(connection.post(url, body, headers))
25
16
  end
26
17
 
27
18
  def patch_request(url, body:, headers: {})
28
- handle_response connection.patch(url, body, headers)
19
+ handle_response(connection.patch(url, body, headers))
29
20
  end
30
21
 
31
22
  def delete_request(url, headers: {})
32
- handle_response connection.delete(url, headers)
23
+ handle_response(connection.delete(url, headers))
33
24
  end
34
25
 
35
- def handle_response(response)
36
- case response.status
37
- when 400
38
- raise Error, "Error 400: Your request was malformed. '#{response.body["error"]["code"]}'"
39
- when 401
40
- raise Error, "Error 401: You did not supply valid authentication credentials. '#{response.body["error"]}'"
41
- when 403
42
- raise Error, "Error 403: You are not allowed to perform that action. '#{response.body["error"]["code"]}'"
43
- when 404
44
- raise Error, "Error 404: No results were found for your request. '#{response.body["error"]["code"]}'"
45
- when 409
46
- raise Error, "Error 409: Your request was a conflict. '#{response.body["error"]["code"]}'"
47
- when 429
48
- raise Error, "Error 429: Your request exceeded the API rate limit. '#{response.body["error"]["code"]}'"
49
- when 500
50
- raise Error, "Error 500: We were unable to perform the request due to server-side problems. '#{response.body["error"]["code"]}'"
51
- when 503
52
- raise Error, "Error 503: You have been rate limited for sending more than 20 requests per second. '#{response.body["error"]["code"]}'"
53
- when 501
54
- raise Error, "Error 501: This resource has not been implemented. '#{response.body["error"]["code"]}'"
55
- when 204
56
- return true
57
- end
26
+ private
58
27
 
59
- if response.body && response.body["error"]
60
- raise Error, "Error #{response.body["error"]["code"]} - #{response.body["errors"]["message"]}"
28
+ def create_connection
29
+ Faraday.new(Paddle.config.url) do |conn|
30
+ conn.request :authorization, :Bearer, Paddle.config.api_key
31
+ conn.headers = default_headers
32
+ conn.request :json
33
+ conn.response :json
61
34
  end
35
+ end
36
+
37
+ def default_headers
38
+ {
39
+ "User-Agent" => "paddle/v#{VERSION} (github.com/deanpcmad/paddle)",
40
+ "Paddle-Version" => Paddle.config.version.to_s
41
+ }
42
+ end
43
+
44
+ def handle_response(response)
45
+ return true if response.status == 204
46
+ return response unless error?(response)
47
+
48
+ raise_error(response)
49
+ end
50
+
51
+ def error?(response)
52
+ [ 400, 401, 403, 404, 409, 429, 500, 501, 503 ].include?(response.status) ||
53
+ response.body&.key?("error")
54
+ end
62
55
 
63
- response
56
+ def raise_error(response)
57
+ error = Paddle::ErrorFactory.create(response.body, response.status)
58
+ raise error if error
64
59
  end
65
60
  end
66
61
  end
@@ -0,0 +1,128 @@
1
+ module Paddle
2
+ class ErrorGenerator < StandardError
3
+ attr_reader :http_status_code
4
+ attr_reader :paddle_error_code
5
+ attr_reader :paddle_error_message
6
+
7
+ def initialize(response_body, http_status_code)
8
+ @response_body = response_body
9
+ @http_status_code = http_status_code
10
+ set_paddle_error_values
11
+ super(build_message)
12
+ end
13
+
14
+ private
15
+
16
+ def set_paddle_error_values
17
+ @paddle_error_code = @response_body.dig("error", "code")
18
+ @paddle_error_message = @response_body.dig("error", "detail")
19
+ end
20
+
21
+ def error_message
22
+ @paddle_error_message || @response_body.dig("error", "code")
23
+ rescue NoMethodError
24
+ "An unknown error occurred."
25
+ end
26
+
27
+ def build_message
28
+ if paddle_error_code.nil?
29
+ return "Error #{@http_status_code}: #{error_message}"
30
+ end
31
+ "Error #{@http_status_code}: #{error_message} '#{paddle_error_code}'"
32
+ end
33
+ end
34
+
35
+ module Errors
36
+ class BadRequestError < ErrorGenerator
37
+ private
38
+
39
+ def error_message
40
+ "Your request was malformed."
41
+ end
42
+ end
43
+
44
+ class AuthenticationMissingError < ErrorGenerator
45
+ private
46
+
47
+ def error_message
48
+ "You did not supply valid authentication credentials."
49
+ end
50
+ end
51
+
52
+ class ForbiddenError < ErrorGenerator
53
+ private
54
+
55
+ def error_message
56
+ "You are not allowed to perform that action."
57
+ end
58
+ end
59
+
60
+ class EntityNotFoundError < ErrorGenerator
61
+ private
62
+
63
+ def error_message
64
+ "No results were found for your request."
65
+ end
66
+ end
67
+
68
+ class ConflictError < ErrorGenerator
69
+ private
70
+
71
+ def error_message
72
+ "Your request was a conflict."
73
+ end
74
+ end
75
+
76
+ class TooManyRequestsError < ErrorGenerator
77
+ private
78
+
79
+ def error_message
80
+ "Your request exceeded the API rate limit."
81
+ end
82
+ end
83
+
84
+ class InternalError < ErrorGenerator
85
+ private
86
+
87
+ def error_message
88
+ "We were unable to perform the request due to server-side problems."
89
+ end
90
+ end
91
+
92
+ class ServiceUnavailableError < ErrorGenerator
93
+ private
94
+
95
+ def error_message
96
+ "You have been rate limited for sending more than 20 requests per second."
97
+ end
98
+ end
99
+
100
+ class NotImplementedError < ErrorGenerator
101
+ private
102
+
103
+ def error_message
104
+ "This resource has not been implemented."
105
+ end
106
+ end
107
+ end
108
+
109
+ class ErrorFactory
110
+ HTTP_ERROR_MAP = {
111
+ 400 => Errors::BadRequestError,
112
+ 401 => Errors::AuthenticationMissingError,
113
+ 403 => Errors::ForbiddenError,
114
+ 404 => Errors::EntityNotFoundError,
115
+ 409 => Errors::ConflictError,
116
+ 429 => Errors::TooManyRequestsError,
117
+ 500 => Errors::InternalError,
118
+ 503 => Errors::ServiceUnavailableError,
119
+ 501 => Errors::NotImplementedError
120
+ }.freeze
121
+
122
+ def self.create(response_body, http_status_code)
123
+ status = http_status_code
124
+ error_class = HTTP_ERROR_MAP[status] || ErrorGenerator
125
+ error_class.new(response_body, http_status_code) if error_class
126
+ end
127
+ end
128
+ end
data/lib/paddle/object.rb CHANGED
@@ -15,5 +15,26 @@ module Paddle
15
15
  obj
16
16
  end
17
17
  end
18
+
19
+ def update(**params)
20
+ method_missing :update unless klass.respond_to? :update
21
+
22
+ primary_attributes = klass.method(:update).parameters.select { |(type, name)| type == :keyreq }.map(&:last) # Identified by whatever is a required named parameter.
23
+
24
+ primary_attributes.each do |attrib|
25
+ # ? Should we need to handle blank strings here?
26
+ params[attrib] = self[attrib] || self["#{attrib}_id"] # Handling for customer (customer_id) and id.
27
+ end
28
+
29
+ klass.public_send(:update, **params).each_pair { |key, val| self[key] = val } # Updating self
30
+
31
+ self
32
+ end
33
+
34
+ private
35
+
36
+ def klass
37
+ self.class
38
+ end
18
39
  end
19
40
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Paddle
4
- VERSION = "2.2.1"
4
+ VERSION = "2.4.0"
5
5
  end
data/lib/paddle.rb CHANGED
@@ -9,6 +9,9 @@ module Paddle
9
9
  autoload :Client, "paddle/client"
10
10
  autoload :Collection, "paddle/collection"
11
11
  autoload :Error, "paddle/error"
12
+ autoload :ErrorGenerator, "paddle/error_generator"
13
+ autoload :ErrorFactory, "paddle/error_generator"
14
+
12
15
  autoload :Object, "paddle/object"
13
16
 
14
17
  class << self
@@ -48,6 +51,7 @@ module Paddle
48
51
  autoload :Client, "paddle/classic/client"
49
52
  autoload :Collection, "paddle/classic/collection"
50
53
  autoload :Resource, "paddle/classic/resource"
54
+ autoload :Error, "paddle/classic/error"
51
55
 
52
56
  autoload :PlansResource, "paddle/classic/resources/plans"
53
57
  autoload :CouponsResource, "paddle/classic/resources/coupons"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paddle
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dean Perry
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-28 00:00:00.000000000 Z
11
+ date: 2024-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -42,6 +42,7 @@ files:
42
42
  - lib/paddle.rb
43
43
  - lib/paddle/classic/client.rb
44
44
  - lib/paddle/classic/collection.rb
45
+ - lib/paddle/classic/error.rb
45
46
  - lib/paddle/classic/objects/charge.rb
46
47
  - lib/paddle/classic/objects/coupon.rb
47
48
  - lib/paddle/classic/objects/license.rb
@@ -70,6 +71,7 @@ files:
70
71
  - lib/paddle/collection.rb
71
72
  - lib/paddle/configuration.rb
72
73
  - lib/paddle/error.rb
74
+ - lib/paddle/error_generator.rb
73
75
  - lib/paddle/models/address.rb
74
76
  - lib/paddle/models/adjustment.rb
75
77
  - lib/paddle/models/business.rb
@@ -110,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
112
  - !ruby/object:Gem::Version
111
113
  version: '0'
112
114
  requirements: []
113
- rubygems_version: 3.5.9
115
+ rubygems_version: 3.5.11
114
116
  signing_key:
115
117
  specification_version: 4
116
118
  summary: Ruby library for the Paddle Billing & Classic APIs