braintree 2.4.0 → 2.5.1

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 (66) hide show
  1. data/README.rdoc +4 -0
  2. data/lib/braintree.rb +43 -32
  3. data/lib/braintree/add_on.rb +4 -0
  4. data/lib/braintree/address.rb +18 -72
  5. data/lib/braintree/address_gateway.rb +76 -0
  6. data/lib/braintree/advanced_search.rb +31 -13
  7. data/lib/braintree/base_module.rb +6 -0
  8. data/lib/braintree/configuration.rb +57 -39
  9. data/lib/braintree/credit_card.rb +75 -129
  10. data/lib/braintree/credit_card_gateway.rb +133 -0
  11. data/lib/braintree/credit_card_verification.rb +8 -0
  12. data/lib/braintree/customer.rb +70 -123
  13. data/lib/braintree/customer_gateway.rb +121 -0
  14. data/lib/braintree/digest.rb +2 -2
  15. data/lib/braintree/discount.rb +4 -0
  16. data/lib/braintree/error_codes.rb +50 -5
  17. data/lib/braintree/error_result.rb +4 -18
  18. data/lib/braintree/errors.rb +1 -2
  19. data/lib/braintree/exceptions.rb +11 -16
  20. data/lib/braintree/gateway.rb +39 -0
  21. data/lib/braintree/http.rb +30 -26
  22. data/lib/braintree/modification.rb +23 -0
  23. data/lib/braintree/resource_collection.rb +1 -1
  24. data/lib/braintree/subscription.rb +29 -129
  25. data/lib/braintree/subscription_gateway.rb +122 -0
  26. data/lib/braintree/subscription_search.rb +6 -7
  27. data/lib/braintree/successful_result.rb +1 -12
  28. data/lib/braintree/test/credit_card_numbers.rb +4 -2
  29. data/lib/braintree/test/transaction_amounts.rb +3 -0
  30. data/lib/braintree/transaction.rb +83 -243
  31. data/lib/braintree/transaction/credit_card_details.rb +4 -4
  32. data/lib/braintree/transaction_gateway.rb +124 -0
  33. data/lib/braintree/transaction_search.rb +5 -3
  34. data/lib/braintree/transparent_redirect.rb +19 -112
  35. data/lib/braintree/transparent_redirect_gateway.rb +105 -0
  36. data/lib/braintree/util.rb +4 -0
  37. data/lib/braintree/validation_error.rb +1 -0
  38. data/lib/braintree/validation_error_collection.rb +5 -23
  39. data/lib/braintree/version.rb +2 -2
  40. data/lib/braintree/xml/parser.rb +1 -1
  41. data/lib/braintree/xml/rexml.rb +2 -2
  42. data/spec/integration/braintree/advanced_search_spec.rb +532 -0
  43. data/spec/integration/braintree/credit_card_spec.rb +5 -8
  44. data/spec/integration/braintree/http_spec.rb +53 -39
  45. data/spec/integration/braintree/subscription_spec.rb +678 -213
  46. data/spec/integration/braintree/transaction_search_spec.rb +318 -43
  47. data/spec/integration/braintree/transaction_spec.rb +134 -3
  48. data/spec/integration/braintree/transparent_redirect_spec.rb +1 -1
  49. data/spec/spec_helper.rb +55 -4
  50. data/spec/unit/braintree/address_spec.rb +8 -8
  51. data/spec/unit/braintree/base_module_spec.rb +1 -1
  52. data/spec/unit/braintree/configuration_spec.rb +34 -29
  53. data/spec/unit/braintree/credit_card_spec.rb +14 -12
  54. data/spec/unit/braintree/credit_card_verification_spec.rb +16 -0
  55. data/spec/unit/braintree/customer_spec.rb +10 -8
  56. data/spec/unit/braintree/digest_spec.rb +8 -17
  57. data/spec/unit/braintree/error_result_spec.rb +12 -2
  58. data/spec/unit/braintree/http_spec.rb +2 -2
  59. data/spec/unit/braintree/subscription_search_spec.rb +77 -0
  60. data/spec/unit/braintree/subscription_spec.rb +16 -8
  61. data/spec/unit/braintree/transaction_spec.rb +17 -12
  62. data/spec/unit/braintree/transparent_redirect_spec.rb +12 -12
  63. data/spec/unit/braintree/util_spec.rb +24 -0
  64. data/spec/unit/braintree/xml/parser_spec.rb +1 -1
  65. data/spec/unit/braintree_spec.rb +1 -1
  66. metadata +16 -5
@@ -1,29 +1,15 @@
1
1
  module Braintree
2
- # An ErrorResult will be returned from non-bang methods when
3
- # validations fail. It will provide access to the params passed
4
- # to the server. The params are primarily useful for re-populaing
5
- # web forms when using transparent redirect. ErrorResult also
6
- # provides access to the validation errors.
7
- #
8
- # result = Braintree::Customer.create(:email => "invalid.email.address")
9
- # if result.success?
10
- # # have a SuccessfulResult
11
- # else
12
- # # have an ErrorResult
13
- # puts "Validations failed when attempting to create customer."
14
- # result.errors.for(:customer).each do |error|
15
- # puts error.message
16
- # end
17
- # end
2
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/result_objects
18
3
  class ErrorResult
19
4
 
20
5
  attr_reader :credit_card_verification, :transaction, :errors, :params, :message
21
6
 
22
- def initialize(data) # :nodoc:
7
+ def initialize(gateway, data) # :nodoc:
8
+ @gateway = gateway
23
9
  @params = data[:params]
24
10
  @credit_card_verification = CreditCardVerification._new(data[:verification]) if data[:verification]
25
11
  @message = data[:message]
26
- @transaction = Transaction._new(data[:transaction]) if data[:transaction]
12
+ @transaction = Transaction._new(gateway, data[:transaction]) if data[:transaction]
27
13
  @errors = Errors.new(data[:errors])
28
14
  end
29
15
 
@@ -1,5 +1,5 @@
1
1
  module Braintree
2
- # Provides access to errors from an ErrorResult.
2
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/validation_errors
3
3
  class Errors
4
4
  include Enumerable
5
5
 
@@ -11,7 +11,6 @@ module Braintree
11
11
  @errors.deep_errors.each(&block)
12
12
  end
13
13
 
14
- # Accesses validation errors for the given +scope+.
15
14
  def for(scope)
16
15
  @errors.for(scope)
17
16
  end
@@ -2,46 +2,41 @@ module Braintree # :nodoc:
2
2
  # Super class for all Braintree exceptions.
3
3
  class BraintreeError < ::StandardError; end
4
4
 
5
- # Raised when authentication fails. This may be caused by an incorrect <tt>Braintree::Configuration</tt>
5
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/exceptions
6
6
  class AuthenticationError < BraintreeError; end
7
7
 
8
- # Raised when the API key being used is not authorized to perform the attempted action according
9
- # to the roles assigned to the user who owns the API key.
8
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/exceptions
10
9
  class AuthorizationError < BraintreeError; end
11
10
 
12
- # Raised when the Braintree gem is not completely configured. See <tt>Braintree::Configuration</tt>.
11
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/exceptions
13
12
  class ConfigurationError < BraintreeError
14
13
  def initialize(setting, message) # :nodoc:
15
14
  super "Braintree::Configuration.#{setting} #{message}"
16
15
  end
17
16
  end
18
17
 
19
- # Raised when the gateway is down for maintenance.
18
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/exceptions
20
19
  class DownForMaintenanceError < BraintreeError; end
21
20
 
22
- # Raised from methods that confirm transparent request requests
23
- # when the given query string cannot be verified. This may indicate
24
- # an attempted hack on the merchant's transparent redirect
25
- # confirmation URL.
21
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/exceptions
26
22
  class ForgedQueryString < BraintreeError; end
27
23
 
28
- # Raised when a record could not be found.
24
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/exceptions
29
25
  class NotFoundError < BraintreeError; end
30
26
 
31
- # Raised when an unexpected server error occurs.
27
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/exceptions
32
28
  class ServerError < BraintreeError; end
33
29
 
34
- # Raised when the SSL certificate fails verification.
30
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/exceptions
35
31
  class SSLCertificateError < BraintreeError; end
36
32
 
37
- # Raised when an error occurs that the client library is not built to handle.
38
- # This shouldn't happen.
33
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/exceptions
39
34
  class UnexpectedError < BraintreeError; end
40
35
 
41
- # Raised when a client library that has been End of Life'd is being used.
36
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/exceptions
42
37
  class UpgradeRequiredError < BraintreeError; end
43
38
 
44
- # Raised from bang methods when validations fail.
39
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/general/exceptions
45
40
  class ValidationsFailed < BraintreeError
46
41
  attr_reader :error_result
47
42
 
@@ -0,0 +1,39 @@
1
+ module Braintree
2
+ class Gateway # :nodoc:
3
+ attr_reader :config
4
+
5
+ def initialize(config)
6
+ if config.is_a?(Hash)
7
+ @config = Configuration.new config
8
+ elsif config.is_a?(Braintree::Configuration)
9
+ @config = config
10
+ else
11
+ raise ArgumentError, "config is an invalid type"
12
+ end
13
+ end
14
+
15
+ def address
16
+ AddressGateway.new(self)
17
+ end
18
+
19
+ def credit_card
20
+ CreditCardGateway.new(self)
21
+ end
22
+
23
+ def customer
24
+ CustomerGateway.new(self)
25
+ end
26
+
27
+ def subscription
28
+ SubscriptionGateway.new(self)
29
+ end
30
+
31
+ def transparent_redirect
32
+ TransparentRedirectGateway.new(self)
33
+ end
34
+
35
+ def transaction
36
+ TransactionGateway.new(self)
37
+ end
38
+ end
39
+ end
@@ -1,7 +1,11 @@
1
1
  module Braintree
2
- module Http # :nodoc:
2
+ class Http # :nodoc:
3
3
 
4
- def self.delete(path)
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ def delete(path)
5
9
  response = _http_do Net::HTTP::Delete, path
6
10
  if response.code.to_i == 200
7
11
  true
@@ -10,7 +14,7 @@ module Braintree
10
14
  end
11
15
  end
12
16
 
13
- def self.get(path)
17
+ def get(path)
14
18
  response = _http_do Net::HTTP::Get, path
15
19
  if response.code.to_i == 200
16
20
  Xml.hash_from_xml(_body(response))
@@ -19,7 +23,7 @@ module Braintree
19
23
  end
20
24
  end
21
25
 
22
- def self.post(path, params = nil)
26
+ def post(path, params = nil)
23
27
  response = _http_do Net::HTTP::Post, path, _build_xml(params)
24
28
  if response.code.to_i == 200 || response.code.to_i == 201 || response.code.to_i == 422
25
29
  Xml.hash_from_xml(_body(response))
@@ -28,7 +32,7 @@ module Braintree
28
32
  end
29
33
  end
30
34
 
31
- def self.put(path, params = nil)
35
+ def put(path, params = nil)
32
36
  response = _http_do Net::HTTP::Put, path, _build_xml(params)
33
37
  if response.code.to_i == 200 || response.code.to_i == 201 || response.code.to_i == 422
34
38
  Xml.hash_from_xml(_body(response))
@@ -37,65 +41,65 @@ module Braintree
37
41
  end
38
42
  end
39
43
 
40
- def self._build_xml(params)
44
+ def _build_xml(params)
41
45
  return nil if params.nil?
42
46
  Braintree::Xml.hash_to_xml params
43
47
  end
44
48
 
45
- def self._http_do(http_verb, path, body = nil)
46
- connection = Net::HTTP.new(Configuration.server, Configuration.port)
47
- if Configuration.ssl?
49
+ def _http_do(http_verb, path, body = nil)
50
+ connection = Net::HTTP.new(@config.server, @config.port)
51
+ if @config.ssl?
48
52
  connection.use_ssl = true
49
53
  connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
50
- connection.ca_file = Configuration.ca_file
54
+ connection.ca_file = @config.ca_file
51
55
  connection.verify_callback = proc { |preverify_ok, ssl_context| _verify_ssl_certificate(preverify_ok, ssl_context) }
52
56
  end
53
57
  connection.start do |http|
54
- request = http_verb.new("#{Configuration.base_merchant_path}#{path}")
58
+ request = http_verb.new("#{@config.base_merchant_path}#{path}")
55
59
  request["Accept"] = "application/xml"
56
- request["User-Agent"] = Configuration.user_agent
60
+ request["User-Agent"] = @config.user_agent
57
61
  request["Accept-Encoding"] = "gzip"
58
- request["X-ApiVersion"] = Configuration::API_VERSION
59
- request.basic_auth Configuration.public_key, Configuration.private_key
60
- Configuration.logger.debug "[Braintree] [#{_current_time}] #{request.method} #{path}"
62
+ request["X-ApiVersion"] = @config.api_version
63
+ request.basic_auth @config.public_key, @config.private_key
64
+ @config.logger.debug "[Braintree] [#{_current_time}] #{request.method} #{path}"
61
65
  if body
62
66
  request["Content-Type"] = "application/xml"
63
67
  request.body = body
64
- Configuration.logger.debug _format_and_sanitize_body_for_log(body)
68
+ @config.logger.debug _format_and_sanitize_body_for_log(body)
65
69
  end
66
70
  response = http.request(request)
67
- Configuration.logger.info "[Braintree] [#{_current_time}] #{request.method} #{path} #{response.code}"
68
- Configuration.logger.debug "[Braintree] [#{_current_time}] #{response.code} #{response.message}"
69
- if Configuration.logger.level == Logger::DEBUG
70
- Configuration.logger.debug _format_and_sanitize_body_for_log(_body(response))
71
+ @config.logger.info "[Braintree] [#{_current_time}] #{request.method} #{path} #{response.code}"
72
+ @config.logger.debug "[Braintree] [#{_current_time}] #{response.code} #{response.message}"
73
+ if @config.logger.level == Logger::DEBUG
74
+ @config.logger.debug _format_and_sanitize_body_for_log(_body(response))
71
75
  end
72
76
  response
73
77
  end
74
78
  end
75
79
 
76
- def self._body(response)
80
+ def _body(response)
77
81
  if response.header["Content-Encoding"] == "gzip"
78
82
  Zlib::GzipReader.new(StringIO.new(response.body)).read
79
83
  else
80
- raise UnexpectedError, "expected a gzip'd response"
84
+ raise UnexpectedError, "expected a gzipped response"
81
85
  end
82
86
  end
83
87
 
84
- def self._current_time
88
+ def _current_time
85
89
  Time.now.utc.strftime("%d/%b/%Y %H:%M:%S %Z")
86
90
  end
87
91
 
88
- def self._format_and_sanitize_body_for_log(input_xml)
92
+ def _format_and_sanitize_body_for_log(input_xml)
89
93
  formatted_xml = input_xml.gsub(/^/, "[Braintree] ")
90
94
  formatted_xml = formatted_xml.gsub(/<number>(.{6}).+?(.{4})<\/number>/, '<number>\1******\2</number>')
91
95
  formatted_xml = formatted_xml.gsub(/<cvv>.+?<\/cvv>/, '<cvv>***</cvv>')
92
96
  formatted_xml
93
97
  end
94
98
 
95
- def self._verify_ssl_certificate(preverify_ok, ssl_context)
99
+ def _verify_ssl_certificate(preverify_ok, ssl_context)
96
100
  if preverify_ok != true || ssl_context.error != 0
97
101
  err_msg = "SSL Verification failed -- Preverify: #{preverify_ok}, Error: #{ssl_context.error_string} (#{ssl_context.error})"
98
- Configuration.logger.error err_msg
102
+ @config.logger.error err_msg
99
103
  raise SSLCertificateError.new(err_msg)
100
104
  end
101
105
  true
@@ -0,0 +1,23 @@
1
+ module Braintree
2
+ class Modification # :nodoc:
3
+ include BaseModule
4
+
5
+ attr_reader :amount, :id, :number_of_billing_cycles, :quantity
6
+
7
+ class << self
8
+ protected :new
9
+ def _new(*args) # :nodoc:
10
+ self.new *args
11
+ end
12
+ end
13
+
14
+ def initialize(attributes) # :nodoc:
15
+ set_instance_variables_from_hash(attributes)
16
+ @amount = Util.to_big_decimal(amount)
17
+ end
18
+
19
+ def never_expires?
20
+ @never_expires
21
+ end
22
+ end
23
+ end
@@ -1,5 +1,5 @@
1
1
  module Braintree
2
- class ResourceCollection
2
+ class ResourceCollection # :nodoc:
3
3
  include Enumerable
4
4
 
5
5
  def initialize(response, &block) # :nodoc:
@@ -1,36 +1,16 @@
1
1
  module Braintree
2
- # == Creating a Subscription
3
- #
4
- # At minimum, a plan_id and payment_method_token are required. Any other values not
5
- # provided will be defaulted to the plan's values:
6
- #
7
- # Braintree::Subscription.create(
8
- # :payment_method_token => "my_token",
9
- # :plan_id => "my_plan"
10
- # )
11
- #
12
- # Full example:
13
- #
14
- # Braintree::Subscription.create(
15
- # :id => "my_id",
16
- # :payment_method_token => "my_token",
17
- # :plan_id => "my_plan",
18
- # :price => "1.00",
19
- # :trial_period => true,
20
- # :trial_duration => "2",
21
- # :trial_duration_unit => Subscription::TrialDurationUnit::Day
22
- # )
23
- #
24
- # == More Information
25
- #
26
- # For more detailed documentation on Subscriptions, see http://www.braintreepaymentsolutions.com/gateway/subscription-api
2
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/subscriptions/overview
27
3
  class Subscription
28
4
  include BaseModule
29
5
 
30
6
  module Status
31
7
  Active = 'Active'
32
8
  Canceled = 'Canceled'
9
+ Expired = 'Expired'
33
10
  PastDue = 'Past Due'
11
+ Pending = 'Pending'
12
+
13
+ All = constants.map { |c| const_get(c) }
34
14
  end
35
15
 
36
16
  module TrialDurationUnit
@@ -38,111 +18,57 @@ module Braintree
38
18
  Month = "month"
39
19
  end
40
20
 
41
- attr_reader :price, :plan_id, :id, :status, :payment_method_token, :merchant_account_id
21
+ attr_reader :days_past_due, :price, :plan_id, :id, :status, :payment_method_token, :merchant_account_id
42
22
  attr_reader :first_billing_date, :next_billing_date, :billing_period_start_date, :billing_period_end_date
43
23
  attr_reader :trial_period, :trial_duration, :trial_duration_unit
44
24
  attr_reader :failure_count
45
25
  attr_reader :transactions
26
+ attr_reader :next_bill_amount
27
+ attr_reader :number_of_billing_cycles, :billing_day_of_month
28
+ attr_reader :add_ons, :discounts
46
29
 
30
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/subscriptions/cancel
47
31
  def self.cancel(subscription_id)
48
- response = Http.put "/subscriptions/#{subscription_id}/cancel"
49
- if response[:subscription]
50
- SuccessfulResult.new(:subscription => _new(response[:subscription]))
51
- elsif response[:api_error_response]
52
- ErrorResult.new(response[:api_error_response])
53
- else
54
- raise UnexpectedError, "expected :subscription or :api_error_response"
55
- end
56
- rescue NotFoundError
57
- raise NotFoundError, "subscription with id #{subscription_id.inspect} not found"
32
+ Configuration.gateway.subscription.cancel(subscription_id)
58
33
  end
59
34
 
35
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/subscriptions/create
60
36
  def self.create(attributes)
61
- Util.verify_keys(_create_signature, attributes)
62
- _do_create "/subscriptions", :subscription => attributes
37
+ Configuration.gateway.subscription.create(attributes)
63
38
  end
64
39
 
65
- # Finds the subscription with the given id. Raises a Braintree::NotFoundError
66
- # if the subscription cannot be found.
40
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/subscriptions/search
67
41
  def self.find(id)
68
- response = Http.get "/subscriptions/#{id}"
69
- _new(response[:subscription])
70
- rescue NotFoundError
71
- raise NotFoundError, "subscription with id #{id.inspect} not found"
42
+ Configuration.gateway.subscription.find(id)
72
43
  end
73
44
 
74
45
  def self.retry_charge(subscription_id, amount=nil)
75
- attributes = {
76
- :amount => amount,
77
- :subscription_id => subscription_id,
78
- :type => Transaction::Type::Sale
79
- }
80
-
81
- Transaction.send(:_do_create, "/transactions", :transaction => attributes)
46
+ Configuration.gateway.transaction.retry_subscription_charge(subscription_id, amount)
82
47
  end
83
48
 
84
- # Allows searching on subscriptions. There are two types of fields that are searchable: text and
85
- # multiple value fields. Searchable text fields are:
86
- # - plan_id
87
- # - days_past_due
88
- #
89
- # Searchable multiple value fields are:
90
- # - status
91
- #
92
- # For text fields, you can search using the following operators: is, is_not, starts_with, ends_with
93
- # and contains. For mutiple value fields, you can search using the in operator. An example:
94
- #
95
- # Subscription.search do |s|
96
- # s.plan_id.starts_with "abc"
97
- # s.days_past_due.is "30"
98
- # s.status.in [Subscription::Status::PastDue]
99
- # end
49
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/subscriptions/search
100
50
  def self.search(&block)
101
- search = SubscriptionSearch.new
102
- block.call(search) if block
103
-
104
- response = Http.post "/subscriptions/advanced_search_ids", {:search => search.to_hash}
105
- ResourceCollection.new(response) { |ids| _fetch_subscriptions(search, ids) }
106
- end
107
-
108
- def self._fetch_subscriptions(search, ids)
109
- search.ids.in ids
110
- response = Http.post "/subscriptions/advanced_search", {:search => search.to_hash}
111
- attributes = response[:subscriptions]
112
- Util.extract_attribute_as_array(attributes, :subscription).map { |attrs| _new(attrs) }
51
+ Configuration.gateway.subscription.search(&block)
113
52
  end
114
53
 
54
+ # See http://www.braintreepaymentsolutions.com/docs/ruby/subscriptions/update
115
55
  def self.update(subscription_id, attributes)
116
- Util.verify_keys(_update_signature, attributes)
117
- response = Http.put "/subscriptions/#{subscription_id}", :subscription => attributes
118
- if response[:subscription]
119
- SuccessfulResult.new(:subscription => _new(response[:subscription]))
120
- elsif response[:api_error_response]
121
- ErrorResult.new(response[:api_error_response])
122
- else
123
- raise UnexpectedError, "expected :subscription or :api_error_response"
124
- end
56
+ Configuration.gateway.subscription.update(subscription_id, attributes)
125
57
  end
126
58
 
127
- def self._create_signature # :nodoc:
128
- [
129
- :id,
130
- :merchant_account_id,
131
- :payment_method_token,
132
- :plan_id,
133
- :price,
134
- :trial_duration,
135
- :trial_duration_unit,
136
- :trial_period
137
- ]
59
+ def initialize(gateway, attributes) # :nodoc:
60
+ @gateway = gateway
61
+ set_instance_variables_from_hash(attributes)
62
+ @price = Util.to_big_decimal(price)
63
+ transactions.map! { |attrs| Transaction._new(gateway, attrs) }
64
+ add_ons.map! { |attrs| AddOn._new(attrs) }
65
+ discounts.map! { |attrs| Discount._new(attrs) }
138
66
  end
139
67
 
140
- def initialize(attributes) # :nodoc:
141
- _init attributes
142
- transactions.map! {|attrs| Transaction._new(attrs) }
68
+ def never_expires?
69
+ @never_expires
143
70
  end
144
71
 
145
-
146
72
  # True if <tt>other</tt> has the same id.
147
73
  def ==(other)
148
74
  return false unless other.is_a?(Subscription)
@@ -155,31 +81,5 @@ module Braintree
155
81
  self.new *args
156
82
  end
157
83
  end
158
-
159
- def self._do_create(url, params) # :nodoc:
160
- response = Http.post url, params
161
- if response[:subscription]
162
- SuccessfulResult.new(:subscription => new(response[:subscription]))
163
- elsif response[:api_error_response]
164
- ErrorResult.new(response[:api_error_response])
165
- else
166
- raise UnexpectedError, "expected :subscription or :api_error_response"
167
- end
168
- end
169
-
170
- def _init(attributes) # :nodoc:
171
- set_instance_variables_from_hash(attributes)
172
- @price = Util.to_big_decimal(price)
173
- end
174
-
175
- def self._update_signature # :nodoc:
176
- [
177
- :id,
178
- :merchant_account_id,
179
- :payment_method_token,
180
- :plan_id,
181
- :price
182
- ]
183
- end
184
84
  end
185
85
  end