recurly 2.9.3 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of recurly might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: '0810dfb4e91991de5ea68776f877db4961e0fe4e5b27897edc1fd2a13a1e32b5'
4
- data.tar.gz: d7da1593f6ebacbea835751fe68cd639fac7918b4888727e73041a46ea8e6927
2
+ SHA1:
3
+ metadata.gz: 2b2f5ee6a8f39d65f8f8b7bf0e7e28dc8fbfe5f7
4
+ data.tar.gz: 87bb6b057dd79daa33251094d2be8b4cf9030c80
5
5
  SHA512:
6
- metadata.gz: 2d28a5665bae1cb32d4193769f72c6f08d57674002bdbeba4a279e8ef6931f974c00f9b15a77ac1cf444120e6a770b9247c9d63e3ab20b038515557e0732bb92
7
- data.tar.gz: 77d7119a631d9c6e0091354852fdd61dfe71b72983776ffe3c46616713db89276dd4b0a98948484489ab439bdc8f3a385250fbcc79e57d938f178b7c7bdebcf5
6
+ metadata.gz: 44074d5b0f6e35dadb1a0d442be7f071b1f5a8466e838163afcb73cb9901f0b5ba5821916e2e1cd26c806f618940bbd1f2a32ced62610016cb86ba50dd76e875
7
+ data.tar.gz: '049c7dd6465aba53105a3d5d279d8a5cb9298dabb52be3c682020c0db665fce2e964b37a8fd21036d98226a9bd3b642b2902a294f29914b29a82ecf316dff170'
data/README.md CHANGED
@@ -14,7 +14,7 @@ Recurly is packaged as a Ruby gem. We recommend you install it with
14
14
  [Bundler](http://gembundler.com/) by adding the following line to your Gemfile:
15
15
 
16
16
  ``` ruby
17
- gem 'recurly', '~> 2.9.2'
17
+ gem 'recurly', '~> 2.10.0'
18
18
  ```
19
19
 
20
20
  Recurly will automatically use [Nokogiri](http://nokogiri.org/) (for a nice
data/lib/recurly.rb CHANGED
@@ -30,6 +30,7 @@ module Recurly
30
30
  require 'recurly/xml'
31
31
  require 'recurly/delivery'
32
32
  require 'recurly/gift_card'
33
+ require 'recurly/purchase'
33
34
  require 'recurly/webhook'
34
35
 
35
36
  @subdomain = nil
@@ -86,8 +87,6 @@ module Recurly
86
87
  end
87
88
 
88
89
  # Assigns a logger to log requests/responses and more.
89
- # The logger can only be set if the environment variable
90
- # `RECURLY_INSECURE_DEBUG` equals `true`.
91
90
  #
92
91
  # @return [Logger, nil]
93
92
  # @example
@@ -99,22 +98,6 @@ module Recurly
99
98
  # Recurly.logger = nil # Or Recurly.logger = Logger.new nil
100
99
  attr_accessor :logger
101
100
 
102
- def logger=(logger)
103
- if ENV['RECURLY_INSECURE_DEBUG'].to_s.downcase == 'true'
104
- @logger = logger
105
- puts <<-MSG
106
- [WARNING] Recurly logger enabled. The logger has the potential to leak
107
- PII and should never be used in production environments.
108
- MSG
109
- else
110
- puts <<-MSG
111
- [WARNING] Recurly logger has been disabled. If you wish to use it,
112
- only do so in a non-production environment and make sure
113
- the `RECURLY_INSECURE_DEBUG` environment variable is set to `true`.
114
- MSG
115
- end
116
- end
117
-
118
101
  # Convenience logging method includes a Logger#progname dynamically.
119
102
  # @return [true, nil]
120
103
  def log level, message
@@ -17,7 +17,7 @@ module Recurly
17
17
  has_many :subscriptions
18
18
  has_many :transactions
19
19
  has_many :redemptions
20
- has_many :shipping_addresses, resource_class: :ShippingAddress, readonly: false
20
+ has_many :shipping_addresses, readonly: false
21
21
 
22
22
  # @return [BillingInfo, nil]
23
23
  has_one :billing_info, :readonly => false
data/lib/recurly/api.rb CHANGED
@@ -15,9 +15,8 @@ module Recurly
15
15
  require 'recurly/api/errors'
16
16
 
17
17
  @@base_uri = "https://api.recurly.com/v2/"
18
- @@valid_domains = [".recurly.com"]
19
18
 
20
- RECURLY_API_VERSION = '2.5'
19
+ RECURLY_API_VERSION = '2.6'
21
20
 
22
21
  FORMATS = Helper.hash_with_indifferent_read_access(
23
22
  'pdf' => 'application/pdf',
@@ -76,13 +75,6 @@ module Recurly
76
75
  URI.parse @@base_uri.sub('api', Recurly.subdomain)
77
76
  end
78
77
 
79
- def validate_uri!(uri)
80
- domain = @@valid_domains.detect { |d| uri.host.end_with?(d) }
81
- unless domain
82
- raise ArgumentError, "URI #{uri} is invalid. You may only make requests to a Recurly domain."
83
- end
84
- end
85
-
86
78
  # @return [String]
87
79
  def user_agent
88
80
  "Recurly/#{Version}; #{RUBY_DESCRIPTION}"
@@ -43,7 +43,6 @@ module Recurly
43
43
  }
44
44
  uri += "?#{pairs.join '&'}"
45
45
  end
46
- self.validate_uri!(uri)
47
46
  request = METHODS[method].new uri.request_uri, head
48
47
  request.basic_auth(*[Recurly.api_key, nil].flatten[0, 2])
49
48
  if options[:body]
@@ -18,7 +18,7 @@ module Recurly
18
18
  # @return [Subscription]
19
19
  belongs_to :subscription
20
20
  # @return [Invoice]
21
- belongs_to :original_invoice, class_name: 'Invoice'
21
+ belongs_to :original_invoice, class_name: :Invoice
22
22
 
23
23
  # This will only be present if the invoice has > 500 line items
24
24
  # @return [Adjustment]
data/lib/recurly/plan.rb CHANGED
@@ -29,9 +29,21 @@ module Recurly
29
29
  setup_fee_revenue_schedule_type
30
30
  tax_exempt
31
31
  tax_code
32
+ trial_requires_billing_info
32
33
  created_at
33
34
  updated_at
34
35
  )
35
36
  alias to_param plan_code
37
+
38
+ #TODO this can be removed after the server update
39
+ def trial_requires_billing_info
40
+ val = read_attribute(:trial_requires_billing_info)
41
+ case val
42
+ when String
43
+ val == 'true'
44
+ else
45
+ val
46
+ end
47
+ end
36
48
  end
37
49
  end
@@ -0,0 +1,131 @@
1
+ module Recurly
2
+ # The Purchase object works a slightly differently than the rest of the models.
3
+ # You build up the purchase data into an object then pass to either:
4
+ # {Purchase.invoice!} or {Purchase.preview!} and it will
5
+ # return an {Invoice}.
6
+ #
7
+ # You can build your purchase object with a new account or an existing account.
8
+ # For an existing account, you just need an account_code:
9
+ # Recurly::Purchase.new({account: {account_code: 'myexistingaccount'}})
10
+ # or
11
+ # account = Recurly::Account.find('existing_account')
12
+ # Recurly::Purchase.new({account: account})
13
+ # or
14
+ # account = Recurly::Account.find('existing_account')
15
+ # purchase = Recurly::Purchase.new
16
+ # purchase.account = account
17
+ #
18
+ # For a new account, you can pass in {Account} data, {BillingInfo} data, etc
19
+ # in the same way you would when creating a {Subscription} with a new account.
20
+ #
21
+ # You can also pass in adjustments and invoicing data to be passed to the invoice.
22
+ # Here is an example of a Purchase with a new account:
23
+ # require 'securerandom'
24
+ #
25
+ # purchase = Recurly::Purchase.new({
26
+ # currency: 'USD',
27
+ # collection_method: :automatic,
28
+ # account: {
29
+ # account_code: SecureRandom.uuid,
30
+ # billing_info: {
31
+ # first_name: 'Benjamin',
32
+ # last_name: 'Du Monde',
33
+ # address1: '400 Alabama St',
34
+ # city: 'San Francisco',
35
+ # state: 'CA',
36
+ # zip: '94110',
37
+ # country: 'US',
38
+ # number: '4111-1111-1111-1111',
39
+ # month: 12,
40
+ # year: 2019,
41
+ # }
42
+ # },
43
+ # adjustments: [
44
+ # {
45
+ # product_code: 'product_1',
46
+ # unit_amount_in_cents: 1000,
47
+ # quantity: 1,
48
+ # revenue_schedule_type: :at_invoice
49
+ # },
50
+ # {
51
+ # product_code: 'product_2'
52
+ # unit_amount_in_cents: 3000,
53
+ # quantity: 5,
54
+ # revenue_schedule_type: :at_invoice
55
+ # }
56
+ # ]
57
+ # })
58
+ #
59
+ # begin
60
+ # preview_invoice = Recurly::Purchase.invoice!(purchase)
61
+ # puts preview_invoice.inspect
62
+ # rescue Recurly::Resource::Invalid => e
63
+ # # Invalid data
64
+ # end
65
+ #
66
+ # begin
67
+ # invoice = Recurly::Purchase.invoice!(purchase)
68
+ # rescue Recurly::Resource::Invalid => e
69
+ # # Invalid data
70
+ # rescue Recurly::Transaction::DeclinedError => e
71
+ # # Display e.message and/or subscription (and associated) errors...
72
+ # rescue Recurly::Transaction::RetryableError => e
73
+ # # You should be able to attempt to save this again later.
74
+ # rescue Recurly::Transaction::Error => e
75
+ # # Fallback transaction error
76
+ # # e.transaction
77
+ # # e.transaction_error_code
78
+ # end
79
+ #
80
+ class Purchase < Resource
81
+ # @return [[Adjustment], nil]
82
+ has_many :adjustments, class_name: :Adjustment, readonly: false
83
+
84
+ # @return [Account, nil]
85
+ has_one :account, readonly: false
86
+
87
+ define_attribute_methods %w(
88
+ currency
89
+ collection_method
90
+ po_number
91
+ net_terms
92
+ )
93
+
94
+ class << self
95
+
96
+ # Generate an invoice for the purchase and run any needed transactions
97
+ #
98
+ # @param purchase [Purchase] The purchase data for the request
99
+ # @return [Invoice] The new invoice representing this purchase
100
+ # @raise [Invalid] Raised if the account cannot be invoiced.
101
+ # @raise [Transaction::Error] Raised if the transaction failed
102
+ def invoice!(purchase)
103
+ post(purchase, collection_path)
104
+ end
105
+
106
+ # Generate a preview invoice for the purchase. Runs validations
107
+ # but does not run any transactions.
108
+ #
109
+ # @param purchase [Purchase] The purchase data for the request
110
+ # @return [Invoice] The new invoice representing this purchase
111
+ # @raise [Invalid] Raised if the account cannot be invoiced.
112
+ def preview!(purchase)
113
+ post(purchase, "#{collection_path}/preview")
114
+ end
115
+
116
+ def post(purchase, path)
117
+ response = API.send(:post, path, purchase.to_xml)
118
+ Invoice.from_response(response)
119
+ rescue API::UnprocessableEntity => e
120
+ purchase.apply_errors(e)
121
+ Transaction::Error.validate!(e, nil)
122
+ raise Resource::Invalid.new(purchase)
123
+ end
124
+ end
125
+
126
+ # This object does not represent a model on the server side
127
+ # so we do not need to expose thses methods.
128
+ protected(*%w(save save!))
129
+ private_class_method(*%w(all find_each first paginate scoped where post create! create))
130
+ end
131
+ end
@@ -163,7 +163,6 @@ module Recurly
163
163
  end
164
164
  end
165
165
 
166
-
167
166
  class << self
168
167
  # @return [String] The demodulized name of the resource class.
169
168
  # @example
@@ -336,8 +335,9 @@ module Recurly
336
335
  raise NotFound, "can't find a record with nil identifier"
337
336
  end
338
337
 
338
+ uri = uuid =~ /^http/ ? uuid : member_path(uuid)
339
339
  begin
340
- from_response API.get(member_path(uuid), {}, options)
340
+ from_response API.get(uri, {}, options)
341
341
  rescue API::NotFound => e
342
342
  raise NotFound, e.description
343
343
  end
@@ -509,7 +509,7 @@ module Recurly
509
509
  # @param collection_name [Symbol] Association name.
510
510
  # @param options [Hash] A hash of association options.
511
511
  # @option options [true, false] :readonly Don't define a setter.
512
- # [String] :resource_class Actual associated resource class name
512
+ # [String] :class_name Actual associated resource class name
513
513
  # if not same as collection_name.
514
514
  def has_many(collection_name, options = {})
515
515
  associations << Association.new(:has_many, collection_name.to_s, options)
@@ -535,7 +535,7 @@ module Recurly
535
535
  # @param member_name [Symbol] Association name.
536
536
  # @param options [Hash] A hash of association options.
537
537
  # @option options [true, false] :readonly Don't define a setter.
538
- # [String] :resource_class Actual associated resource class name
538
+ # [String] :class_name Actual associated resource class name
539
539
  # if not same as member_name.
540
540
  def has_one(member_name, options = {})
541
541
  associations << Association.new(:has_one, member_name.to_s, options)
@@ -573,7 +573,7 @@ module Recurly
573
573
  # @param parent_name [Symbol] Association name.
574
574
  # @param options [Hash] A hash of association options.
575
575
  # @option options [true, false] :readonly Don't define a setter.
576
- # [String] :resource_class Actual associated resource class name
576
+ # [String] :class_name Actual associated resource class name
577
577
  # if not same as parent_name.
578
578
  def belongs_to(parent_name, options = {})
579
579
  associations << Association.new(:belongs_to, parent_name.to_s, options)
@@ -634,12 +634,9 @@ module Recurly
634
634
  return if response.body.to_s.length.zero?
635
635
  fresh = self.class.from_response response
636
636
  else
637
- options = {:etag => (etag unless changed?)}
638
- fresh = if @href
639
- self.class.from_response API.get(@href, {}, options)
640
- else
641
- self.class.find(to_param, options)
642
- end
637
+ fresh = self.class.find(
638
+ @href || to_param, :etag => (etag unless changed?)
639
+ )
643
640
  end
644
641
  fresh and copy_from fresh
645
642
  persist! true
@@ -849,7 +846,7 @@ module Recurly
849
846
  if new_record? || changed?
850
847
  clear_errors
851
848
  @response = API.send(
852
- persisted? ? :put : :post, path, to_xml(:delta => true)
849
+ persisted? ? :put : :post, path, to_xml
853
850
  )
854
851
  reload response
855
852
  persist! true
@@ -1026,6 +1023,15 @@ module Recurly
1026
1023
  end
1027
1024
  alias to_s inspect
1028
1025
 
1026
+ def apply_errors(exception)
1027
+ @response = exception.response
1028
+ document = XML.new exception.response.body
1029
+ document.each_element 'error' do |el|
1030
+ attribute_path = el.attribute('field').value.split '.'
1031
+ invalid! attribute_path[1, attribute_path.length], el.text
1032
+ end
1033
+ end
1034
+
1029
1035
  protected
1030
1036
 
1031
1037
  def path
@@ -1064,23 +1070,17 @@ module Recurly
1064
1070
  end
1065
1071
  end
1066
1072
 
1067
- def apply_errors(exception)
1068
- @response = exception.response
1069
- document = XML.new exception.response.body
1070
- document.each_element 'error' do |el|
1071
- attribute_path = el.attribute('field').value.split '.'
1072
- invalid! attribute_path[1, attribute_path.length], el.text
1073
- end
1074
- end
1075
-
1076
1073
  private
1077
1074
 
1078
- def fetch_associated(name, value)
1075
+ def fetch_associated(name, value, options = {})
1079
1076
  case value
1080
1077
  when Array
1081
- value.map { |each| fetch_associated(Helper.singularize(name), each) }
1078
+ value.map do |v|
1079
+ fetch_associated(Helper.singularize(name), v, association_name: name)
1080
+ end
1082
1081
  when Hash
1083
- associated_class_name = self.class.find_association(name).class_name
1082
+ association_name = options[:association_name] || name
1083
+ associated_class_name = self.class.find_association(association_name).class_name
1084
1084
  associated_class_name ||= Helper.classify(name)
1085
1085
  Recurly.const_get(associated_class_name, false).send(:new, value)
1086
1086
  when Proc, Resource, Resource::Pager, nil
@@ -65,7 +65,7 @@ module Recurly
65
65
  @uri = options.delete :uri
66
66
  @etag = options.delete :etag
67
67
  @resource_class, @options = resource_class, options
68
- @collection = @count = nil
68
+ @collection = nil
69
69
  end
70
70
 
71
71
  # @return [Boolean] whether or not the xml element is present
@@ -81,7 +81,7 @@ module Recurly
81
81
  # @return [Integer] The total record count of the resource in question.
82
82
  # @see Resource.count
83
83
  def count
84
- @count ||= API.head(uri, @options)['X-Records'].to_i
84
+ API.head(uri, @options)['X-Records'].to_i
85
85
  end
86
86
 
87
87
  # @return [Array] Iterates through the current page of records.
@@ -134,7 +134,7 @@ module Recurly
134
134
  # Recurly::Account.active.paginate :per_page => 20
135
135
  def paginate options = {}
136
136
  dup.instance_eval {
137
- @collection = @count = @etag = nil
137
+ @collection = @etag = nil
138
138
  @options = @options.merge options
139
139
  self
140
140
  }
@@ -220,7 +220,6 @@ module Recurly
220
220
  response = API.get uri, params, options
221
221
 
222
222
  @etag = response['ETag']
223
- @count = response['X-Records'].to_i
224
223
  @links = {}
225
224
  if links = response['Link']
226
225
  links.scan(/<([^>]+)>; rel="([^"]+)"/).each do |link, rel|
@@ -73,6 +73,7 @@ module Recurly
73
73
  timeframe
74
74
  started_with_gift
75
75
  converted_at
76
+ no_billing_info_reason
76
77
  )
77
78
  alias to_param uuid
78
79
 
@@ -21,7 +21,7 @@ module Recurly
21
21
  belongs_to :subscription
22
22
 
23
23
  # @return [Transaction, nil]
24
- has_one :original_transaction, class_name: 'Transaction', readonly: true
24
+ has_one :original_transaction, class_name: :Transaction, readonly: true
25
25
 
26
26
  define_attribute_methods %w(
27
27
  id
@@ -1,8 +1,8 @@
1
1
  module Recurly
2
2
  module Version
3
3
  MAJOR = 2
4
- MINOR = 9
5
- PATCH = 3
4
+ MINOR = 10
5
+ PATCH = 0
6
6
  PRE = nil
7
7
 
8
8
  VERSION = [MAJOR, MINOR, PATCH, PRE].compact.join('.').freeze
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recurly
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.3
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Recurly
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-09 00:00:00.000000000 Z
11
+ date: 2017-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 11.1.0
33
+ version: '11.3'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 11.1.0
40
+ version: '11.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -171,6 +171,7 @@ files:
171
171
  - lib/recurly/measured_unit.rb
172
172
  - lib/recurly/money.rb
173
173
  - lib/recurly/plan.rb
174
+ - lib/recurly/purchase.rb
174
175
  - lib/recurly/redemption.rb
175
176
  - lib/recurly/resource.rb
176
177
  - lib/recurly/resource/association.rb
@@ -238,7 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
238
239
  version: '0'
239
240
  requirements: []
240
241
  rubyforge_project:
241
- rubygems_version: 2.7.6
242
+ rubygems_version: 2.6.8
242
243
  signing_key:
243
244
  specification_version: 4
244
245
  summary: Recurly API Client