recurly 3.5.0 → 3.6.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: 8cb7459191e21c44fd4796091c9aba791fbc079c446aad569513bc3df26e5f10
4
- data.tar.gz: 632a9ff4168fbdeeff508dc643920bf2290d225eaed28d5c7d0d92897495a26a
3
+ metadata.gz: 1ac32fad822fa602a4eae953b306b78bac66aea6a7bc67f1161df49fe72f813f
4
+ data.tar.gz: c5ae0c45581474a6f1ff9d996e0af49140073b5282585fb0af8daf34cde549c8
5
5
  SHA512:
6
- metadata.gz: 7981c548326df5a65652e8184410a959927ad0b0630c1da6f286965389bb27a71a3da51e7e9804589b3564a94a4e266fc54f686eca3cd6d1b0aa6af779c73694
7
- data.tar.gz: 10b42ef5247e4ecad7112c8b9ca9f2e8cbee81bacc4c55a773e1e4d1bcdec42cf4f01847a83c6975f46f9d5c86bba9bdd97d4f0b10710f891be1be0f2c2ab437
6
+ metadata.gz: 4bb163f9b5db0561aa52875398b33c1751608321cddf49c4337205175fb08f40f448853f7c1815f147a2c0a433692b4065993c9fadb6f61c0f1ce528887c1e5d
7
+ data.tar.gz: 2d2449562b1e0fdea4dfcd5d5eac1885bcff3f3bc3d1333fa0bc855eddcf584b8527807d6954bde7cfc2b10c151216e5bbcc79851c42842870a2de1aa61d402b
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 3.5.0
2
+ current_version = 3.6.0
3
3
  parse = (?P<major>\d+)
4
4
  \.(?P<minor>\d+)
5
5
  \.(?P<patch>\d+)
@@ -10,3 +10,7 @@ serialize =
10
10
 
11
11
  [bumpversion:file:lib/recurly/version.rb]
12
12
 
13
+ [bumpversion:file:GETTING_STARTED.md]
14
+ parse = (?P<major>\d+)\.(?P<minor>\d+)
15
+ serialize = {major}.{minor}
16
+
@@ -1,8 +1,25 @@
1
1
  # Changelog
2
2
 
3
- ## [3.5.0](https://github.com/recurly/recurly-client-ruby/tree/HEAD)
3
+ ## [3.6.0](https://github.com/recurly/recurly-client-ruby/tree/HEAD)
4
4
 
5
- [Full Changelog](https://github.com/recurly/recurly-client-ruby/compare/3.4.1...HEAD)
5
+ [Full Changelog](https://github.com/recurly/recurly-client-ruby/compare/3.5.0...HEAD)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Latest Features [\#592](https://github.com/recurly/recurly-client-ruby/pull/592) ([bhelx](https://github.com/bhelx))
10
+ - Support the programmer passing their own logger [\#590](https://github.com/recurly/recurly-client-ruby/pull/590) ([bhelx](https://github.com/bhelx))
11
+
12
+ **Merged pull requests:**
13
+
14
+ - Release 3.6.0 [\#594](https://github.com/recurly/recurly-client-ruby/pull/594) ([bhelx](https://github.com/bhelx))
15
+ - Better format error message [\#593](https://github.com/recurly/recurly-client-ruby/pull/593) ([bhelx](https://github.com/bhelx))
16
+ - Let bump2version manage the getting started doc [\#591](https://github.com/recurly/recurly-client-ruby/pull/591) ([bhelx](https://github.com/bhelx))
17
+ - Document `Pager#first` and `Pager#count` [\#589](https://github.com/recurly/recurly-client-ruby/pull/589) ([bhelx](https://github.com/bhelx))
18
+ - Ensure that path parameters are not empty strings [\#587](https://github.com/recurly/recurly-client-ruby/pull/587) ([douglasmiller](https://github.com/douglasmiller))
19
+
20
+ ## [3.5.0](https://github.com/recurly/recurly-client-ruby/tree/3.5.0) (2020-04-20)
21
+
22
+ [Full Changelog](https://github.com/recurly/recurly-client-ruby/compare/3.4.1...3.5.0)
6
23
 
7
24
  **Implemented enhancements:**
8
25
 
@@ -5,7 +5,7 @@ This repository houses the official ruby client for Recurly's V3 API.
5
5
  In your Gemfile, add `recurly` as a dependency.
6
6
 
7
7
  ```ruby
8
- gem 'recurly', '~> 3.2'
8
+ gem 'recurly', '~> 3.6'
9
9
  ```
10
10
 
11
11
  > *Note*: We try to follow [semantic versioning](https://semver.org/) and will only apply breaking changes to major versions.
@@ -41,6 +41,27 @@ client = Recurly::Client.new(api_key: API_KEY2)
41
41
  sub = client.get_subscription(subscription_id: 'abcd7890')
42
42
  ```
43
43
 
44
+ ## Logging
45
+
46
+ The client constructor optionally accepts a logger provided by the programmer. The logger you pass should be an instance of ruby stdlib's [Logger](https://ruby-doc.org/stdlib/libdoc/logger/rdoc/Logger.html)
47
+ or follow the same interface. By default, the client creates a logger to `STDOUT` with level `WARN`.
48
+
49
+ ```ruby
50
+ require 'logger'
51
+
52
+ # Create a logger to STDOUT
53
+ logger = Logger.new(STDOUT)
54
+ logger.level = Logger::INFO
55
+
56
+ # You could also use an existing logger
57
+ # If you are using Rails you may want to use your application's logger
58
+ logger = Rails.logger
59
+
60
+ client = Recurly::Client.new(api_key: API_KEY, logger: logger)
61
+ ```
62
+
63
+ > *SECURITY WARNING*: The log level should never be set to DEBUG in production. This could potentially result in sensitive data in your logging system.
64
+
44
65
  # Operations
45
66
 
46
67
  The {Recurly::Client} contains every `operation` you can perform on the site as a list of methods. Each method is documented explaining
@@ -109,6 +130,45 @@ end
109
130
  `limit` defaults to 20 items per page and can be set from 1 to 200. Choosing a lower limit means more network requests but smaller payloads.
110
131
  We recommend keeping the default for most cases but increasing the limit if you are planning on iterating through many pages of items (e.g. all transactions in your site).
111
132
 
133
+ ## Efficiently Fetch the First or Last Resource
134
+
135
+ The Pager class implements a first method which allows you to fetch just the first or last resource from the server. On top of being a convenient abstraction, this is implemented efficiently by only asking the server for the 1 item you want.
136
+
137
+ ```ruby
138
+ accounts = client.list_accounts(
139
+ subscriber: true,
140
+ order: :desc
141
+ )
142
+
143
+ last_subscriber = accounts.first
144
+ ```
145
+
146
+ If you want to fetch the last account in this scenario, invert the order from descending `desc` to ascending `asc`:
147
+
148
+ ```ruby
149
+ accounts = client.list_accounts(
150
+ subscriber: true,
151
+ order: :asc
152
+ )
153
+
154
+ first_subscriber = accounts.first
155
+ ```
156
+
157
+ ## Counting Resources
158
+
159
+ The Pager class implements a `count` method which allows you to count the resources the pager would return. It does so by calling the endpoint with `HEAD` and parsing and returning the `Recurly-Total-Records` header. This method respects any filtering parameters you apply to the pager, but the sorting parameters will have no effect.
160
+
161
+ ```ruby
162
+ accounts = client.list_accounts(
163
+ subscriber: true,
164
+ begin_time: DateTime.new(2017,1,1)
165
+ )
166
+
167
+ # Calling count here will return an integer indicating
168
+ # the number of subscribers since 2017
169
+ count = accounts.count
170
+ # => 573
171
+ ```
112
172
 
113
173
  # Creating Resources
114
174
 
@@ -15,11 +15,11 @@ module Recurly
15
15
  CA_FILE = File.join(File.dirname(__FILE__), "../data/ca-certificates.crt")
16
16
  BINARY_TYPES = [
17
17
  "application/pdf",
18
- ]
18
+ ].freeze
19
19
  JSON_CONTENT_TYPE = "application/json"
20
20
  MAX_RETRIES = 3
21
-
22
- BASE36_ALPHABET = ("0".."9").to_a + ("a".."z").to_a
21
+ LOG_LEVELS = %i(debug info warn error fatal).freeze
22
+ BASE36_ALPHABET = (("0".."9").to_a + ("a".."z").to_a).freeze
23
23
 
24
24
  # Initialize a client. It requires an API key.
25
25
  #
@@ -45,12 +45,31 @@ module Recurly
45
45
  # sub = client.get_subscription(subscription_id: 'uuid-abcd7890')
46
46
  #
47
47
  # @param api_key [String] The private API key
48
- # @param site_id [String] The site you wish to be scoped to.
49
- # @param subdomain [String] Optional subdomain for the site you wish to be scoped to. Providing this makes all the `site_id` parameters optional.
50
- def initialize(api_key:, site_id: nil, subdomain: nil, **options)
48
+ # @param logger [Logger] A logger to use. Defaults to creating a new STDOUT logger with level WARN.
49
+ def initialize(api_key:, site_id: nil, subdomain: nil, logger: nil)
51
50
  set_site_id(site_id, subdomain)
52
51
  set_api_key(api_key)
53
- set_options(options)
52
+
53
+ if logger.nil?
54
+ @logger = Logger.new(STDOUT).tap do |l|
55
+ l.level = Logger::WARN
56
+ end
57
+ else
58
+ unless LOG_LEVELS.all? { |lev| logger.respond_to?(lev) }
59
+ raise ArgumentError, "You must pass in a logger implementation that responds to the following messages: #{LOG_LEVELS}"
60
+ end
61
+ @logger = logger
62
+ end
63
+
64
+ if @logger.level < Logger::INFO
65
+ msg = <<-MSG
66
+ The Recurly logger should not be initialized
67
+ beyond the level INFO. It is currently configured to emit
68
+ headers and request / response bodies. This has the potential to leak
69
+ PII and other sensitive information and should never be used in production.
70
+ MSG
71
+ log_warn("SECURITY_WARNING", message: msg)
72
+ end
54
73
 
55
74
  # execute block with this client if given
56
75
  yield(self) if block_given?
@@ -100,7 +119,6 @@ module Recurly
100
119
  if request_data
101
120
  request_class.new(request_data).validate!
102
121
  json_body = JSON.dump(request_data)
103
- logger.info("PUT BODY #{json_body}")
104
122
  request.body = json_body
105
123
  end
106
124
  http_response = run_request(request, options)
@@ -116,9 +134,6 @@ module Recurly
116
134
 
117
135
  private
118
136
 
119
- # @return [Logger]
120
- attr_reader :logger
121
-
122
137
  @connection_pool = Recurly::ConnectionPool.new
123
138
 
124
139
  class << self
@@ -134,14 +149,37 @@ module Recurly
134
149
 
135
150
  begin
136
151
  http.start unless http.started?
152
+ log_attrs = {
153
+ method: request.method,
154
+ path: request.path,
155
+ }
156
+ if @logger.level < Logger::INFO
157
+ log_attrs[:request_body] = request.body
158
+ # No need to log the authorization header
159
+ headers = request.to_hash.reject { |k, _| k&.downcase == "authorization" }
160
+ log_attrs[:request_headers] = headers
161
+ end
162
+
163
+ log_info("Request", **log_attrs)
164
+ start = Time.now
137
165
  response = http.request(request)
166
+ elapsed = Time.now - start
138
167
 
139
168
  # GETs are safe to retry after a server error, requests with an Idempotency-Key will return the prior response
140
169
  if response.kind_of?(Net::HTTPServerError) && request.is_a?(Net::HTTP::Get)
141
170
  retries += 1
171
+ log_info("Retrying", retries: retries, **log_attrs)
172
+ start = Time.now
142
173
  response = http.request(request) if retries < MAX_RETRIES
174
+ elapsed = Time.now - start
143
175
  end
144
176
 
177
+ if @logger.level < Logger::INFO
178
+ log_attrs[:response_body] = response.body
179
+ log_attrs[:response_headers] = response.to_hash
180
+ end
181
+ log_info("Response", time_ms: (elapsed * 1_000).floor, status: response.code, **log_attrs)
182
+
145
183
  response
146
184
  rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH, Errno::ECONNABORTED,
147
185
  Errno::EPIPE, Errno::ETIMEDOUT, Net::OpenTimeout, EOFError, SocketError => ex
@@ -190,8 +228,6 @@ module Recurly
190
228
  def set_http_options(http, options)
191
229
  http.open_timeout = options[:open_timeout] || 20
192
230
  http.read_timeout = options[:read_timeout] || 60
193
-
194
- http.set_debug_output(logger) if @log_level <= Logger::INFO && !http.started?
195
231
  end
196
232
 
197
233
  def handle_response!(request, http_response)
@@ -232,15 +268,15 @@ module Recurly
232
268
 
233
269
  def read_headers(response)
234
270
  if !@_ignore_deprecation_warning && response.headers["Recurly-Deprecated"]&.upcase == "TRUE"
235
- puts "[recurly-client-ruby] WARNING: Your current API version \"#{api_version}\" is deprecated and will be sunset on #{response.headers["Recurly-Sunset-Date"]}"
271
+ log_warn("DEPRECTATION WARNING", message: "Your current API version \"#{api_version}\" is deprecated and will be sunset on #{response.headers["Recurly-Sunset-Date"]}")
236
272
  end
237
273
  response
238
274
  end
239
275
 
240
- def interpolate_path(path, **options)
276
+ def validate_path_parameters!(**options)
277
+ # Check to see that we are passing the correct data types
278
+ # This prevents a confusing error if the user passes in a non-primitive by mistake
241
279
  options.each do |k, v|
242
- # Check to see that we are passing the correct data types
243
- # This prevents a confusing error if the user passes in a non-primitive by mistake
244
280
  unless [String, Symbol, Integer, Float].include?(v.class)
245
281
  message = "We cannot build the url with the given argument #{k}=#{v.inspect}."
246
282
  if k =~ /_id$/
@@ -248,6 +284,17 @@ module Recurly
248
284
  end
249
285
  raise ArgumentError, message
250
286
  end
287
+ end
288
+ # Check to make sure that parameters are not empty string values
289
+ empty_strings = options.select { |_, v| v.is_a?(String) && v.strip.empty? }
290
+ if empty_strings.any?
291
+ raise ArgumentError, "#{empty_strings.keys.join(", ")} cannot be an empty string"
292
+ end
293
+ end
294
+
295
+ def interpolate_path(path, **options)
296
+ validate_path_parameters!(options)
297
+ options.each do |k, v|
251
298
  # We need to encode the values for the url
252
299
  options[k] = ERB::Util.url_encode(v.to_s)
253
300
  end
@@ -286,10 +333,14 @@ module Recurly
286
333
  end
287
334
  end
288
335
 
289
- def set_options(options)
290
- @log_level = options[:log_level] || Logger::WARN
291
- @logger = Logger.new(STDOUT)
292
- @logger.level = @log_level
336
+ # Define a private `log_<level>` method for each log level
337
+ LOG_LEVELS.each do |level|
338
+ define_method "log_#{level}" do |tag, **attrs|
339
+ @logger.send(level, "Recurly") do
340
+ msg = attrs.each_pair.map { |k, v| "#{k}=#{v.inspect}" }.join(" ")
341
+ "[#{tag}] #{msg}"
342
+ end
343
+ end
293
344
  end
294
345
  end
295
346
  end
@@ -30,6 +30,7 @@ module Recurly
30
30
  # order. In descending order updated records will move behind the cursor and could
31
31
  # prevent some records from being returned.
32
32
  #
33
+ # @param state [String] Filter by state.
33
34
  # @return [Pager<Resources::Site>] A list of sites.
34
35
  # @example
35
36
  # sites = @client.list_sites(limit: 200)
@@ -48,6 +49,16 @@ module Recurly
48
49
  #
49
50
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
50
51
  # @return [Resources::Site] A site.
52
+ # @example
53
+ # begin
54
+ # site = @client.get_site(site_id: site_id)
55
+ # puts "Got Site #{site}"
56
+ # rescue Recurly::Errors::NotFoundError
57
+ # # If the resource was not found, you may want to alert the user or
58
+ # # just return nil
59
+ # puts "Resource Not Found"
60
+ # end
61
+ #
51
62
  def get_site(site_id:)
52
63
  path = interpolate_path("/sites/{site_id}", site_id: site_id)
53
64
  get(path)
@@ -251,6 +262,28 @@ module Recurly
251
262
  # @param body [Requests::AccountAcquisitionUpdatable] The Hash representing the JSON request to send to the server. It should conform to the schema of {Requests::AccountAcquisitionUpdatable}
252
263
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
253
264
  # @return [Resources::AccountAcquisition] An account's updated acquisition data.
265
+ # @example
266
+ # begin
267
+ # acquisition_update = {
268
+ # campaign: "podcast-marketing",
269
+ # channel: "social_media",
270
+ # subchannel: "twitter",
271
+ # cost: {
272
+ # currency: "USD",
273
+ # amount: 0.50
274
+ # }
275
+ # }
276
+ # acquisition = @client.update_account_acquisition(
277
+ # account_id: account_id,
278
+ # body: acquisition_update
279
+ # )
280
+ # puts "Updated AccountAcqusition #{acquisition}"
281
+ # rescue Recurly::Errors::ValidationError => e
282
+ # # If the request was invalid, you may want to tell your user
283
+ # # why. You can find the invalid params and reasons in e.recurly_error.params
284
+ # puts "ValidationError: #{e.recurly_error.params}"
285
+ # end
286
+ #
254
287
  def update_account_acquisition(account_id:, body:, **options)
255
288
  path = interpolate_path("/accounts/{account_id}/acquisition", account_id: account_id)
256
289
  put(path, body, Requests::AccountAcquisitionUpdatable, **options)
@@ -845,6 +878,26 @@ module Recurly
845
878
  # @param body [Requests::ShippingAddressCreate] The Hash representing the JSON request to send to the server. It should conform to the schema of {Requests::ShippingAddressCreate}
846
879
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
847
880
  # @return [Resources::ShippingAddress] Returns the new shipping address.
881
+ # @example
882
+ # begin
883
+ # shipping_address_create = {
884
+ # nickname: 'Work',
885
+ # street1: '900 Camp St',
886
+ # city: 'New Orleans',
887
+ # region: 'LA',
888
+ # country: 'US',
889
+ # postal_code: '70115',
890
+ # first_name: 'Joanna',
891
+ # last_name: 'Du Monde'
892
+ # }
893
+ # shipping_address = @client.create_shipping_address(account_id: account_id, body: shipping_address_create)
894
+ # puts "Created Shipping Address #{shipping_address}"
895
+ # rescue Recurly::Errors::NotFoundError
896
+ # # If the resource was not found, you may want to alert the user or
897
+ # # just return nil
898
+ # puts "Resource Not Found"
899
+ # end
900
+ #
848
901
  def create_shipping_address(account_id:, body:, **options)
849
902
  path = interpolate_path("/accounts/{account_id}/shipping_addresses", account_id: account_id)
850
903
  post(path, body, Requests::ShippingAddressCreate, **options)
@@ -1230,6 +1283,19 @@ module Recurly
1230
1283
  # @param body [Requests::CouponUpdate] The Hash representing the JSON request to send to the server. It should conform to the schema of {Requests::CouponUpdate}
1231
1284
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
1232
1285
  # @return [Resources::Coupon] The updated coupon.
1286
+ # @example
1287
+ # begin
1288
+ # coupon_update = {
1289
+ # name: "New Coupon Name"
1290
+ # }
1291
+ # coupon = @client.update_coupon(coupon_id: coupon_id, body: coupon_update)
1292
+ # puts "Updated Coupon #{coupon}"
1293
+ # rescue Recurly::Errors::NotFoundError
1294
+ # # If the resource was not found, you may want to alert the user or
1295
+ # # just return nil
1296
+ # puts "Resource Not Found"
1297
+ # end
1298
+ #
1233
1299
  def update_coupon(coupon_id:, body:, **options)
1234
1300
  path = interpolate_path("/coupons/{coupon_id}", coupon_id: coupon_id)
1235
1301
  put(path, body, Requests::CouponUpdate, **options)
@@ -1242,6 +1308,16 @@ module Recurly
1242
1308
  # @param coupon_id [String] Coupon ID or code. For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-10off+.
1243
1309
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
1244
1310
  # @return [Resources::Coupon] The expired Coupon
1311
+ # @example
1312
+ # begin
1313
+ # coupon = @client.deactivate_coupon(coupon_id: coupon_id)
1314
+ # puts "Deactivated Coupon #{coupon}"
1315
+ # rescue Recurly::Errors::NotFoundError
1316
+ # # If the resource was not found, you may want to alert the user or
1317
+ # # just return nil
1318
+ # puts "Resource Not Found"
1319
+ # end
1320
+ #
1245
1321
  def deactivate_coupon(coupon_id:, **options)
1246
1322
  path = interpolate_path("/coupons/{coupon_id}", coupon_id: coupon_id)
1247
1323
  delete(path, **options)
@@ -1373,6 +1449,18 @@ module Recurly
1373
1449
  # @param custom_field_definition_id [String] Custom Field Definition ID
1374
1450
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
1375
1451
  # @return [Resources::CustomFieldDefinition] An custom field definition.
1452
+ # @example
1453
+ # begin
1454
+ # custom_field_definition = @client.get_custom_field_definition(
1455
+ # custom_field_definition_id: custom_field_definition_id
1456
+ # )
1457
+ # puts "Got Custom Field Definition #{custom_field_definition}"
1458
+ # rescue Recurly::Errors::NotFoundError
1459
+ # # If the resource was not found, you may want to alert the user or
1460
+ # # just return nil
1461
+ # puts "Resource Not Found"
1462
+ # end
1463
+ #
1376
1464
  def get_custom_field_definition(custom_field_definition_id:, **options)
1377
1465
  path = interpolate_path("/custom_field_definitions/{custom_field_definition_id}", custom_field_definition_id: custom_field_definition_id)
1378
1466
  get(path, **options)
@@ -1627,6 +1715,20 @@ module Recurly
1627
1715
  # @param body [Requests::InvoiceUpdatable] The Hash representing the JSON request to send to the server. It should conform to the schema of {Requests::InvoiceUpdatable}
1628
1716
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
1629
1717
  # @return [Resources::Invoice] An invoice.
1718
+ # @example
1719
+ # begin
1720
+ # invoice_update = {
1721
+ # customer_notes: "New Notes",
1722
+ # terms_and_conditions: "New Terms and Conditions"
1723
+ # }
1724
+ # invoice = @client.put_invoice(invoice_id: invoice_id, body: invoice_update)
1725
+ # puts "Updated invoice #{invoice}"
1726
+ # rescue Recurly::Errors::NotFoundError
1727
+ # # If the resource was not found, you may want to alert the user or
1728
+ # # just return nil
1729
+ # puts "Resource Not Found"
1730
+ # end
1731
+ #
1630
1732
  def put_invoice(invoice_id:, body:, **options)
1631
1733
  path = interpolate_path("/invoices/{invoice_id}", invoice_id: invoice_id)
1632
1734
  put(path, body, Requests::InvoiceUpdatable, **options)
@@ -1753,11 +1855,34 @@ module Recurly
1753
1855
  # @param invoice_id [String] Invoice ID or number. For ID no prefix is used e.g. +e28zov4fw0v2+. For number use prefix +number-+, e.g. +number-1000+.
1754
1856
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
1755
1857
  # @return [Resources::Invoice] The updated invoice.
1858
+ # @example
1859
+ # begin
1860
+ # invoice = @client.void_invoice(invoice_id: invoice_id)
1861
+ # puts "Voided invoice #{invoice}"
1862
+ # rescue Recurly::Errors::NotFoundError
1863
+ # # If the resource was not found, you may want to alert the user or
1864
+ # # just return nil
1865
+ # puts "Resource Not Found"
1866
+ # end
1867
+ #
1756
1868
  def void_invoice(invoice_id:, **options)
1757
1869
  path = interpolate_path("/invoices/{invoice_id}/void", invoice_id: invoice_id)
1758
1870
  put(path, **options)
1759
1871
  end
1760
1872
 
1873
+ # Record an external payment for a manual invoices.
1874
+ #
1875
+ # {https://developers.recurly.com/api/v2019-10-10#operation/record_external_transaction record_external_transaction api documenation}
1876
+ #
1877
+ # @param invoice_id [String] Invoice ID or number. For ID no prefix is used e.g. +e28zov4fw0v2+. For number use prefix +number-+, e.g. +number-1000+.
1878
+ # @param body [Requests::ExternalTransaction] The Hash representing the JSON request to send to the server. It should conform to the schema of {Requests::ExternalTransaction}
1879
+ # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
1880
+ # @return [Resources::Transaction] The recorded transaction.
1881
+ def record_external_transaction(invoice_id:, body:, **options)
1882
+ path = interpolate_path("/invoices/{invoice_id}/transactions", invoice_id: invoice_id)
1883
+ post(path, body, Requests::ExternalTransaction, **options)
1884
+ end
1885
+
1761
1886
  # List an invoice's line items
1762
1887
  #
1763
1888
  # {https://developers.recurly.com/api/v2019-10-10#operation/list_invoice_line_items list_invoice_line_items api documenation}
@@ -1792,6 +1917,15 @@ module Recurly
1792
1917
  # @param type [String] Filter by type field.
1793
1918
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
1794
1919
  # @return [Pager<Resources::LineItem>] A list of the invoice's line items.
1920
+ # @example
1921
+ # line_items = @client.list_invoice_line_items(
1922
+ # invoice_id: invoice_id,
1923
+ # limit: 200
1924
+ # )
1925
+ # line_items.each do |line_item|
1926
+ # puts "Line Item: #{line_item.id}"
1927
+ # end
1928
+ #
1795
1929
  def list_invoice_line_items(invoice_id:, **options)
1796
1930
  path = interpolate_path("/invoices/{invoice_id}/line_items", invoice_id: invoice_id)
1797
1931
  pager(path, **options)
@@ -1847,6 +1981,15 @@ module Recurly
1847
1981
  # @param invoice_id [String] Invoice ID or number. For ID no prefix is used e.g. +e28zov4fw0v2+. For number use prefix +number-+, e.g. +number-1000+.
1848
1982
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
1849
1983
  # @return [Pager<Resources::Invoice>] A list of the credit or charge invoices associated with the invoice.
1984
+ # @example
1985
+ # invoices = @client.list_related_invoices(
1986
+ # invoice_id: invoice_id,
1987
+ # limit: 200
1988
+ # )
1989
+ # invoices.each do |invoice|
1990
+ # puts "Invoice: #{invoice.number}"
1991
+ # end
1992
+ #
1850
1993
  def list_related_invoices(invoice_id:, **options)
1851
1994
  path = interpolate_path("/invoices/{invoice_id}/related_invoices", invoice_id: invoice_id)
1852
1995
  pager(path, **options)
@@ -1915,6 +2058,14 @@ module Recurly
1915
2058
  # @param type [String] Filter by type field.
1916
2059
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
1917
2060
  # @return [Pager<Resources::LineItem>] A list of the site's line items.
2061
+ # @example
2062
+ # line_items = @client.list_line_items(
2063
+ # limit: 200
2064
+ # )
2065
+ # line_items.each do |line_item|
2066
+ # puts "LineItem: #{line_item.id}"
2067
+ # end
2068
+ #
1918
2069
  def list_line_items(**options)
1919
2070
  path = interpolate_path("/line_items")
1920
2071
  pager(path, **options)
@@ -2077,6 +2228,19 @@ module Recurly
2077
2228
  # @param body [Requests::PlanUpdate] The Hash representing the JSON request to send to the server. It should conform to the schema of {Requests::PlanUpdate}
2078
2229
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
2079
2230
  # @return [Resources::Plan] A plan.
2231
+ # @example
2232
+ # begin
2233
+ # plan_update = {
2234
+ # name: "Monthly Kombucha Subscription"
2235
+ # }
2236
+ # plan = @client.update_plan(plan_id: plan_id, body: plan_update)
2237
+ # puts "Updated plan #{plan}"
2238
+ # rescue Recurly::Errors::NotFoundError
2239
+ # # If the resource was not found, you may want to alert the user or
2240
+ # # just return nil
2241
+ # puts "Resource Not Found"
2242
+ # end
2243
+ #
2080
2244
  def update_plan(plan_id:, body:, **options)
2081
2245
  path = interpolate_path("/plans/{plan_id}", plan_id: plan_id)
2082
2246
  put(path, body, Requests::PlanUpdate, **options)
@@ -2089,6 +2253,16 @@ module Recurly
2089
2253
  # @param plan_id [String] Plan ID or code. For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-gold+.
2090
2254
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
2091
2255
  # @return [Resources::Plan] Plan deleted
2256
+ # @example
2257
+ # begin
2258
+ # plan = @client.remove_plan(plan_id: plan_id)
2259
+ # puts "Removed plan #{plan}"
2260
+ # rescue Recurly::Errors::NotFoundError
2261
+ # # If the resource was not found, you may want to alert the user or
2262
+ # # just return nil
2263
+ # puts "Resource Not Found"
2264
+ # end
2265
+ #
2092
2266
  def remove_plan(plan_id:, **options)
2093
2267
  path = interpolate_path("/plans/{plan_id}", plan_id: plan_id)
2094
2268
  delete(path, **options)
@@ -2148,6 +2322,27 @@ module Recurly
2148
2322
  # @param body [Requests::AddOnCreate] The Hash representing the JSON request to send to the server. It should conform to the schema of {Requests::AddOnCreate}
2149
2323
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
2150
2324
  # @return [Resources::AddOn] An add-on.
2325
+ # @example
2326
+ # begin
2327
+ # new_add_on = {
2328
+ # code: 'coffee_grinder',
2329
+ # name: 'A quality grinder for your beans',
2330
+ # default_quantity: 1,
2331
+ # currencies: [
2332
+ # {
2333
+ # currency: 'USD',
2334
+ # unit_amount: 10_000
2335
+ # }
2336
+ # ]
2337
+ # }
2338
+ # add_on = @client.create_plan_add_on(plan_id: plan_id, body: new_add_on)
2339
+ # puts "Created plan add-on #{add_on}"
2340
+ # rescue Recurly::Errors::NotFoundError
2341
+ # # If the resource was not found, you may want to alert the user or
2342
+ # # just return nil
2343
+ # puts "Resource Not Found"
2344
+ # end
2345
+ #
2151
2346
  def create_plan_add_on(plan_id:, body:, **options)
2152
2347
  path = interpolate_path("/plans/{plan_id}/add_ons", plan_id: plan_id)
2153
2348
  post(path, body, Requests::AddOnCreate, **options)
@@ -2187,6 +2382,23 @@ module Recurly
2187
2382
  # @param body [Requests::AddOnUpdate] The Hash representing the JSON request to send to the server. It should conform to the schema of {Requests::AddOnUpdate}
2188
2383
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
2189
2384
  # @return [Resources::AddOn] An add-on.
2385
+ # @example
2386
+ # begin
2387
+ # add_on_update = {
2388
+ # name: "A quality grinder for your finest beans"
2389
+ # }
2390
+ # add_on = @client.update_plan_add_on(
2391
+ # plan_id: plan_id,
2392
+ # add_on_id: add_on_id,
2393
+ # body: add_on_update
2394
+ # )
2395
+ # puts "Updated add-on #{add_on}"
2396
+ # rescue Recurly::Errors::NotFoundError
2397
+ # # If the resource was not found, you may want to alert the user or
2398
+ # # just return nil
2399
+ # puts "Resource Not Found"
2400
+ # end
2401
+ #
2190
2402
  def update_plan_add_on(plan_id:, add_on_id:, body:, **options)
2191
2403
  path = interpolate_path("/plans/{plan_id}/add_ons/{add_on_id}", plan_id: plan_id, add_on_id: add_on_id)
2192
2404
  put(path, body, Requests::AddOnUpdate, **options)
@@ -2200,6 +2412,19 @@ module Recurly
2200
2412
  # @param add_on_id [String] Add-on ID or code. For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-gold+.
2201
2413
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
2202
2414
  # @return [Resources::AddOn] Add-on deleted
2415
+ # @example
2416
+ # begin
2417
+ # add_on = @client.remove_plan_add_on(
2418
+ # plan_id: plan_id,
2419
+ # add_on_id: add_on_id
2420
+ # )
2421
+ # puts "Removed add-on #{add_on}"
2422
+ # rescue Recurly::Errors::NotFoundError
2423
+ # # If the resource was not found, you may want to alert the user or
2424
+ # # just return nil
2425
+ # puts "Resource Not Found"
2426
+ # end
2427
+ #
2203
2428
  def remove_plan_add_on(plan_id:, add_on_id:, **options)
2204
2429
  path = interpolate_path("/plans/{plan_id}/add_ons/{add_on_id}", plan_id: plan_id, add_on_id: add_on_id)
2205
2430
  delete(path, **options)
@@ -2236,6 +2461,14 @@ module Recurly
2236
2461
  # @param state [String] Filter by state.
2237
2462
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
2238
2463
  # @return [Pager<Resources::AddOn>] A list of add-ons.
2464
+ # @example
2465
+ # add_ons = @client.list_add_ons(
2466
+ # limit: 200
2467
+ # )
2468
+ # add_ons.each do |add_on|
2469
+ # puts "AddOn: #{add_on.code}"
2470
+ # end
2471
+ #
2239
2472
  def list_add_ons(**options)
2240
2473
  path = interpolate_path("/add_ons")
2241
2474
  pager(path, **options)
@@ -2248,6 +2481,16 @@ module Recurly
2248
2481
  # @param add_on_id [String] Add-on ID or code. For ID no prefix is used e.g. +e28zov4fw0v2+. For code use prefix +code-+, e.g. +code-gold+.
2249
2482
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
2250
2483
  # @return [Resources::AddOn] An add-on.
2484
+ # @example
2485
+ # begin
2486
+ # add_on = @client.get_add_on(add_on_id: add_on_id)
2487
+ # puts "Got add-on #{add_on}"
2488
+ # rescue Recurly::Errors::NotFoundError
2489
+ # # If the resource was not found, you may want to alert the user or
2490
+ # # just return nil
2491
+ # puts "Resource Not Found"
2492
+ # end
2493
+ #
2251
2494
  def get_add_on(add_on_id:, **options)
2252
2495
  path = interpolate_path("/add_ons/{add_on_id}", add_on_id: add_on_id)
2253
2496
  get(path, **options)
@@ -2283,6 +2526,14 @@ module Recurly
2283
2526
  #
2284
2527
  # @param site_id [String] Site ID or subdomain. For ID no prefix is used e.g. +e28zov4fw0v2+. For subdomain use prefix +subdomain-+, e.g. +subdomain-recurly+.
2285
2528
  # @return [Pager<Resources::ShippingMethod>] A list of the site's shipping methods.
2529
+ # @example
2530
+ # shipping_methods = @client.list_shipping_methods(
2531
+ # limit: 200
2532
+ # )
2533
+ # shipping_methods.each do |shipping_method|
2534
+ # puts "Shipping Method: #{shipping_method.code}"
2535
+ # end
2536
+ #
2286
2537
  def list_shipping_methods(**options)
2287
2538
  path = interpolate_path("/shipping_methods")
2288
2539
  pager(path, **options)