Skip to content

Commit

Permalink
1.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
braintreeps committed Apr 9, 2010
1 parent 31f9019 commit 168d742
Show file tree
Hide file tree
Showing 33 changed files with 892 additions and 138 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.rdoc
@@ -1,3 +1,10 @@
== 1.2.0

* Added Subscription search
* Updated production CA SSL certificate authority
* Updated credit cards to include associated subscriptions when finding in vault
* Fixed bug where we used to raise a "forged query string" exception when we were down for maintenance.

== 1.1.3

* Fixed a bug with empty search results
Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Expand Up @@ -23,7 +23,7 @@ Spec::Rake::SpecTask.new("spec:integration") do |t|
end

desc "run specs after preping the gateway"
task :run_specs_for_cruise do
task :cruise do
begin
Rake::Task["prep_gateway"].invoke
Rake::Task["spec:unit"].invoke
Expand Down
1 change: 1 addition & 0 deletions bt_rdoc_template/braintree.rb
Expand Up @@ -66,6 +66,7 @@ module Page
<li><a href="BASE_URL/classes/Braintree/Customer.html">Customer</a></li>
<li><a href="BASE_URL/classes/Braintree/CreditCard.html">CreditCard</a></li>
<li><a href="BASE_URL/classes/Braintree/Address.html">Address</a></li>
<li><a href="BASE_URL/classes/Braintree/Subscription.html">Subscription</a></li>
</ul>
</div>
Expand Down
2 changes: 1 addition & 1 deletion cruise_config.rb
Expand Up @@ -3,7 +3,7 @@

case project.name
when "client_library_ruby_integration_master"
project.build_command = "CRUISE_BUILD=#{project.name} GATEWAY_PORT=3010 SPHINX_PORT=3322 rake run_specs_for_cruise --trace"
project.build_command = "CRUISE_BUILD=#{project.name} GATEWAY_PORT=3010 SPHINX_PORT=3322 rake cruise --trace"
project.triggered_by :gateway_master
end
end
2 changes: 2 additions & 0 deletions lib/braintree.rb
Expand Up @@ -23,6 +23,7 @@ module Braintree
require "braintree/base_module"

require "braintree/address.rb"
require "braintree/advanced_search.rb"
require "braintree/configuration.rb"
require "braintree/credit_card.rb"
require "braintree/credit_card_verification.rb"
Expand All @@ -35,6 +36,7 @@ module Braintree
require "braintree/paged_collection.rb"
require "braintree/ssl_expiration_check.rb"
require "braintree/subscription"
require "braintree/subscription_search"
require "braintree/successful_result.rb"
require "braintree/test/credit_card_numbers.rb"
require "braintree/test/transaction_amounts.rb"
Expand Down
69 changes: 69 additions & 0 deletions lib/braintree/advanced_search.rb
@@ -0,0 +1,69 @@
module Braintree
class AdvancedSearch
class SearchNode
def self.operators(*operator_names)
operator_names.each do |operator|
define_method(operator) do |value|
@parent.add_criteria(@node_name, operator => value.to_s)
end
end
end

def initialize(name, parent)
@node_name, @parent = name, parent
end
end

class TextNode < SearchNode
operators :is, :is_not, :ends_with, :starts_with, :contains
end

class MultipleValueNode < SearchNode
def in(*values)
values.flatten!

unless allowed_values.nil?
bad_values = values - allowed_values
raise ArgumentError.new("Invalid argument(s) for #{@node_name}: #{bad_values.join(", ")}") if bad_values.any?
end

@parent.add_criteria(@node_name, values)
end

def initialize(name, parent, options)
super(name, parent)
@options = options
end

def allowed_values
@options[:allows]
end
end

def self.search_fields(*fields)
fields.each do |field|
define_method(field) do
TextNode.new(field, self)
end
end
end

def self.multiple_value_field(field, options={})
define_method(field) do
MultipleValueNode.new(field, self, options)
end
end

def initialize
@criteria = {}
end

def add_criteria(key, value)
@criteria[key] = value
end

def to_hash
@criteria
end
end
end
4 changes: 2 additions & 2 deletions lib/braintree/configuration.rb
Expand Up @@ -37,9 +37,9 @@ def self.base_merchant_path # :nodoc:
def self.ca_file # :nodoc:
case environment
when :qa, :sandbox
File.expand_path(File.join(File.dirname(__FILE__), "..", "ssl", "valicert_ca.crt"))
File.expand_path(File.join(File.dirname(__FILE__), "..", "ssl", "sandbox_braintreegateway_com.ca.crt"))
when :production
File.expand_path(File.join(File.dirname(__FILE__), "..", "ssl", "securetrust_ca.crt"))
File.expand_path(File.join(File.dirname(__FILE__), "..", "ssl", "www_braintreegateway_com.ca.crt"))
end
end

Expand Down
3 changes: 2 additions & 1 deletion lib/braintree/credit_card.rb
Expand Up @@ -3,7 +3,7 @@ class CreditCard
include BaseModule # :nodoc:

attr_reader :billing_address, :bin, :card_type, :cardholder_name, :created_at, :customer_id, :expiration_month,
:expiration_year, :last_4, :token, :updated_at
:expiration_year, :last_4, :subscriptions, :token, :updated_at

def self.create(attributes)
if attributes.has_key?(:expiration_date) && (attributes.has_key?(:expiration_month) || attributes.has_key?(:expiration_year))
Expand Down Expand Up @@ -99,6 +99,7 @@ def self.update_credit_card_url

def initialize(attributes) # :nodoc:
_init attributes
@subscriptions = (@subscriptions || []).map { |subscription_hash| Subscription.new(subscription_hash) }
end

# Creates a credit transaction for this credit card.
Expand Down
51 changes: 50 additions & 1 deletion lib/braintree/subscription.rb
@@ -1,4 +1,25 @@
module Braintree
# == Creating a Subscription
#
# At minimum, a plan_id and payment_method_token are required. Any other values not
# provided will be defaulted to the plan's values:
#
# Braintree::Subscription.create(
# :payment_method_token => "my_token",
# :plan_id => "my_plan"
# )
#
# Full example:
#
# Braintree::Subscription.create(
# :id => "my_id",
# :payment_method_token => "my_token",
# :plan_id => "my_plan",
# :price => "1.00",
# :trial_period => true,
# :trial_duration => "2",
# :trial_duration_unit => Subscription::TrialDurationUnit::Day
# )
class Subscription
include BaseModule

Expand All @@ -13,7 +34,7 @@ module TrialDurationUnit
Month = "month"
end

attr_reader :price, :plan_id, :id, :status, :payment_method_token
attr_reader :price, :plan_id, :id, :status, :payment_method_token, :merchant_account_id
attr_reader :first_billing_date, :next_billing_date, :billing_period_start_date, :billing_period_end_date
attr_reader :trial_period, :trial_duration, :trial_duration_unit
attr_reader :failure_count
Expand Down Expand Up @@ -46,6 +67,32 @@ def self.find(id)
raise NotFoundError, "subscription with id #{id.inspect} not found"
end

# Allows searching on subscriptions. There are two types of fields that are searchable: text and
# multiple value fields. Searchable text fields are:
# - plan_id
# - days_past_due
#
# Searchable multiple value fields are:
# - status
#
# For text fields, you can search using the following operators: is, is_not, starts_with, ends_with
# and contains. For mutiple value fields, you can search using the in operator. An example:
#
# Subscription.search do |s|
# s.plan_id.starts_with "abc"
# s.days_past_due.is "30"
# s.status.in [Subscription::Status::PastDue]
# end
def self.search(page=1, &block)
search = SubscriptionSearch.new
block.call(search)

response = Http.post "/subscriptions/advanced_search?page=#{page}", {:search => search.to_hash}
attributes = response[:subscriptions]
attributes[:items] = Util.extract_attribute_as_array(attributes, :subscription).map { |attrs| new(attrs) }
PagedCollection.new(attributes) { |page_number| Subscription.search(page_number, &block) }
end

def self.update(subscription_id, attributes)
Util.verify_keys(_update_signature, attributes)
response = Http.put "/subscriptions/#{subscription_id}", :subscription => attributes
Expand All @@ -61,6 +108,7 @@ def self.update(subscription_id, attributes)
def self._create_signature # :nodoc:
[
:id,
:merchant_account_id,
:payment_method_token,
:plan_id,
:price,
Expand Down Expand Up @@ -100,6 +148,7 @@ def _init(attributes) # :nodoc:
def self._update_signature # :nodoc:
[
:id,
:merchant_account_id,
:plan_id,
:price
]
Expand Down
10 changes: 10 additions & 0 deletions lib/braintree/subscription_search.rb
@@ -0,0 +1,10 @@
module Braintree
class SubscriptionSearch < AdvancedSearch
search_fields :plan_id, :days_past_due
multiple_value_field :status, :allows => [
Subscription::Status::Active,
Subscription::Status::Canceled,
Subscription::Status::PastDue
]
end
end
13 changes: 8 additions & 5 deletions lib/braintree/transparent_redirect.rb
Expand Up @@ -41,12 +41,15 @@ def self.create_customer_data(params)
def self.parse_and_validate_query_string(query_string) # :nodoc:
params = Util.symbolize_keys(Util.parse_query_string(query_string))
query_string_without_hash = query_string[/(.*)&hash=.*/, 1]

if params[:http_status] == nil
raise UnexpectedError, "expected query string to have an http_status param"
elsif params[:http_status] != '200'
Util.raise_exception_for_status_code(params[:http_status])
end

if _hash(query_string_without_hash) == params[:hash]
if params[:http_status] == '200'
params
else
Util.raise_exception_for_status_code(params[:http_status])
end
params
else
raise ForgedQueryString
end
Expand Down
4 changes: 2 additions & 2 deletions lib/braintree/version.rb
@@ -1,8 +1,8 @@
module Braintree
module Version
Major = 1
Minor = 1
Tiny = 3
Minor = 2
Tiny = 0

String = "#{Major}.#{Minor}.#{Tiny}"
end
Expand Down
9 changes: 7 additions & 2 deletions lib/braintree/xml/generator.rb
Expand Up @@ -62,7 +62,6 @@ def self._convert_to_xml(hash_to_convert, options = {})
end

def self._array_to_xml(array, options = {})
raise "expected all elements to be hashes" unless array.all? { |e| e.is_a?(Hash) }
raise "expected options[:root]" unless options[:root]
raise "expected options[:builder]" unless options[:builder]
options[:indent] ||= 2
Expand All @@ -71,7 +70,13 @@ def self._array_to_xml(array, options = {})
options[:builder].tag!(root, :type => "array")
else
options[:builder].tag!(root, :type => "array") do
array.each { |e| _convert_to_xml(e, options.merge(:root => "item", :skip_instruct => true)) }
array.each do |e|
if e.is_a?(Hash)
_convert_to_xml(e, options.merge(:root => "item", :skip_instruct => true))
else
options[:builder].tag!("item", e)
end
end
end
end
end
Expand Down
@@ -1,3 +1,4 @@
Subject: L=ValiCert Validation Network, O=ValiCert, Inc., OU=ValiCert Class 2 Policy Validation Authority, CN=http://www.valicert.com//emailAddress=info@valicert.com
-----BEGIN CERTIFICATE-----
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
Expand Down

0 comments on commit 168d742

Please sign in to comment.