1.5.0 release

This commit is contained in:
Greg Brockman 2011-05-26 11:46:39 -07:00
commit 0813418b74
11 changed files with 4565 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/stripe-*.gem

24
History.txt Normal file
View File

@ -0,0 +1,24 @@
=== 1.5.0 2011-05-09
* 1 major enhancement:
* Update for new RESTful API
=== 1.3.4 2011-01-07
* 1 major enhancement:
* Rename to Stripe
=== 1.2 2010-06-06
* 1 major enhancement:
* Support for the set_customer_subscription and delete_customer API methods
=== 1.1 2010-03-14
* 1 major enhancement:
* Support for recurring billing
=== 1.0 2010-01-05
* 1 major enhancement:
* Initial release

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2010 Stripe (https://stripe.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

15
README.rdoc Normal file
View File

@ -0,0 +1,15 @@
= Stripe Ruby bindings
== Installation
You don't need this source code unless you want to modify the gem. If you just want to use the Stripe Ruby bindings, you should run:
sudo gem install --source https://gems.stripe.com stripe
If you want to build the gem from source:
sudo gem build stripe.gemspec
== REQUIREMENTS:
* rest-client, json

1
VERSION Normal file
View File

@ -0,0 +1 @@
1.5.0

7
bin/stripe-console Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env ruby
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
libs = " -r irb/completion"
libs << " -r #{File.dirname(__FILE__) + '/../lib/stripe'}"
puts "Loading stripe gem"
exec "#{irb} #{libs} --simple-prompt"

3524
lib/data/ca-certificates.crt Normal file

File diff suppressed because it is too large Load Diff

511
lib/stripe.rb Normal file
View File

@ -0,0 +1,511 @@
# Stripe Ruby bindings
# API spec at http://stripe.com/api/spec
# Authors: Ross Boucher <boucher@stripe.com> and Greg Brockman <gdb@stripe.com>
require 'set'
require 'rubygems'
require 'json'
require 'openssl'
require 'rest_client'
module Stripe
@@version = '1.5.0'
@@ssl_bundle_path = File.join(File.dirname(__FILE__), 'data/ca-certificates.crt')
@@api_key = nil
@@api_base = 'https://api.stripe.com/v1'
module Util
def self.objects_to_ids(h)
case h
when APIResource
h.id
when Hash
res = {}
h.each { |k, v| res[k] = objects_to_ids(v) }
res
else
h
end
end
def self.convert_to_stripe_object(resp, api_key)
types = {
'charge' => Charge,
'customer' => Customer,
'invoice_item' => InvoiceItem,
'invoice' => Invoice
}
case resp
when Array
resp.map { |i| convert_to_stripe_object(i, api_key) }
when Hash
# Try converting to a known object class. If none available, fall back to generic APIResource
if klass_name = resp[:object]
klass = types[klass_name]
end
klass ||= StripeObject
klass.construct_from(resp, api_key)
else
resp
end
end
end
module APIOperations
module Create
module ClassMethods
def create(params={}, api_key=nil)
response, api_key = Stripe.request(:post, self.url, api_key, params)
Util.convert_to_stripe_object(response, api_key)
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
module Update
def save
if @unsaved_values.length > 0
values = {}
@unsaved_values.each { |k| values[k] = @values[k] }
response, api_key = Stripe.request(:post, url, @api_key, values)
refresh_from(response, api_key)
end
self
end
end
module Delete
def delete
response, api_key = Stripe.request(:delete, url, @api_key)
refresh_from(response, api_key)
self
end
end
module List
module ClassMethods
def all(filters={}, api_key=nil)
response, api_key = Stripe.request(:get, url, api_key, filters)
Util.convert_to_stripe_object(response, api_key)
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
end
class StripeObject
attr_accessor :api_key
@@permanent_attributes = Set.new([:api_key])
@@ignored_attributes = Set.new([:id, :api_key, :object])
# The default :id method is deprecated and isn't useful to us
if method_defined?(:id)
undef :id
end
def initialize(id=nil, api_key=nil)
@api_key = api_key
@values = {}
# This really belongs in APIResource, but not putting it there allows us
# to have a unified inspect method
@unsaved_values = Set.new
@transient_values = Set.new
self.id = id if id
end
def self.construct_from(values, api_key=nil)
obj = self.new(values[:id], api_key)
obj.refresh_from(values, api_key)
obj
end
def to_s(*args); inspect(*args); end
def inspect(verbose=false, nested=false)
str = ["#<#{self.class}"]
if (desc = ident.compact).length > 0
str << "[#{desc.join(', ')}]"
end
if !verbose and nested
str << ' ...'
else
content = @values.keys.sort { |a, b| a.to_s <=> b.to_s }.map do |k|
if @@ignored_attributes.include?(k)
nil
else
v = @values[k]
v = v.kind_of?(StripeObject) ? v.inspect(verbose, true) : v.inspect
v = @unsaved_values.include?(k) ? "#{v} (unsaved)" : v
"#{k}=#{v}"
end
end.compact.join(', ')
str << ' '
if content.length > 0
str << content
else
str << '(no attributes)'
end
end
str << ">"
str.join
end
def refresh_from(values, api_key, partial=false)
@api_key = api_key
removed = partial ? Set.new : Set.new(@values.keys - values.keys)
added = Set.new(values.keys - @values.keys)
# Wipe old state before setting new. This is useful for e.g. updating a
# customer, where there is no persistent card parameter. Mark those values
# which don't persist as transient
instance_eval do
remove_accessors(removed)
add_accessors(added)
end
removed.each do |k|
@values.delete(k)
@transient_values.add(k)
@unsaved_values.delete(k)
end
values.each do |k, v|
@values[k] = Util.convert_to_stripe_object(v, api_key)
@transient_values.delete(k)
@unsaved_values.delete(k)
end
end
def [](k)
k = k.to_sym if k.kind_of?(String)
@values[k]
end
def []=(k, v)
send(:"#{k}=", v)
end
def keys; @values.keys; end
def values; @values.values; end
def to_json(*a); @values.to_json(*a); end
protected
def ident
[@values[:object], @values[:id]]
end
def metaclass
class << self; self; end
end
def remove_accessors(keys)
metaclass.instance_eval do
keys.each do |k|
next if @@permanent_attributes.include?(k)
k_eq = :"#{k}="
remove_method(k) if method_defined?(k)
remove_method(k_eq) if method_defined?(k_eq)
end
end
end
def add_accessors(keys)
metaclass.instance_eval do
keys.each do |k|
next if @@permanent_attributes.include?(k)
k_eq = :"#{k}="
define_method(k) { @values[k] }
define_method(k_eq) do |v|
@values[k] = v
@unsaved_values.add(k) unless @@ignored_attributes.include?(k)
end
end
end
end
def method_missing(name, *args)
# TODO: only allow setting in updateable classes.
if name.to_s.end_with?('=')
attr = name.to_s[0...-1].to_sym
@values[attr] = args[0]
@unsaved_values.add(attr)
add_accessors([attr])
return
else
return @values[name] if @values.has_key?(name)
end
begin
super
rescue NoMethodError => e
if @transient_values.include?(name)
raise NoMethodError.new(e.message + ". HINT: The '#{name}' attribute was set in the past, however. It was then wiped when refreshing the object with the result returned by Stripe's API, probably as a result of a save(). The attributes currently available on this object are: #{@values.keys.join(', ')}")
else
raise
end
end
end
end
class APIResource < StripeObject
def self.url
if self == APIResource
raise NotImplementedError.new("APIResource is an abstract class. You should perform actions on its subclasses (Charge, Customer, etc.)")
end
shortname = self.name.split('::')[-1]
"/#{CGI.escape(shortname.downcase)}s"
end
def url; "#{self.class.url}/#{CGI.escape(id)}"; end
def refresh
response, api_key = Stripe.request(:get, url, @api_key)
refresh_from(response, api_key)
self
end
def self.retrieve(id, api_key=nil)
instance = self.new(id, api_key)
instance.refresh
instance
end
protected
def ident
[@values[:id]]
end
end
class Customer < APIResource
include Stripe::APIOperations::Create
include Stripe::APIOperations::Delete
include Stripe::APIOperations::Update
include Stripe::APIOperations::List
def add_invoice_item(params)
InvoiceItem.create(params.merge(:customer_id => id), @api_key)
end
def invoices
Invoice.all({ :customer_id => id }, @api_key)
end
def invoice_items
InvoiceItem.all({ :customer_id => id }, @api_key)
end
def charges
Charge.all({ :customer_id => id }, @api_key)
end
def cancel_subscription
response, api_key = Stripe.request(:delete, subscription_url, @api_key)
refresh_from({ :subscription => response }, api_key, true)
subscription
end
def update_subscription(params)
response, api_key = Stripe.request(:post, subscription_url, @api_key)
refresh_from({ :subscription => response }, api_key, true)
subscription
end
private
def subscription_url
url + '/subscription'
end
end
class Invoice < APIResource
include Stripe::APIOperations::List
def self.upcoming(params)
response, api_key = Stripe.request(:get, upcoming_url, @api_key, params)
Util.convert_to_stripe_object(response, api_key)
end
private
def self.upcoming_url
url + '/upcoming'
end
end
class InvoiceItem < APIResource
include Stripe::APIOperations::List
include Stripe::APIOperations::Create
include Stripe::APIOperations::Delete
include Stripe::APIOperations::Update
end
class Charge < APIResource
include Stripe::APIOperations::List
include Stripe::APIOperations::Create
def refund
response, api_key = Stripe.request(:post, refund_url, @api_key)
refresh_from(response, api_key)
self
end
private
def refund_url
url + '/refund'
end
end
class StripeError < StandardError; end
class APIError < StripeError; end
class APIConnectionError < StripeError; end
class CardError < StripeError
attr_reader :param, :code
def initialize(message, param, code)
super(message)
@param = param
@code = code
end
end
class InvalidRequestError < StripeError
attr_accessor :param
def initialize(message, param)
super(message)
@param = param
end
end
class AuthenticationError < StripeError; end
def self.api_url(url=''); @@api_base + url; end
def self.api_key=(api_key); @@api_key = api_key; end
def self.api_key; @@api_key; end
def self.api_base=(api_base); @@api_base = api_base; end
def self.api_base; @@api_base; end
def self.version; @@version; end
def self.request(method, url, api_key, params=nil, headers={})
api_key ||= @@api_key
raise AuthenticationError.new('No API key provided. (HINT: set your API key using "Stripe.api_key = <API-KEY>". You can generate API keys from the Stripe web interface. See https://stripe.com/api for details, or email support@stripe.com if you have any questions.') unless api_key
if !File.exists?(@@ssl_bundle_path)
unless @no_bundle
$stderr.puts "WARNING: Running without SSL cert verification because #{@@ssl_bundle_path} does not exist"
@no_bundle = true
end
ssl_opts = { :verify_ssl => false }
else
ssl_opts = {
:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
:ssl_ca_file => @@ssl_bundle_path
}
end
uname = (@@uname ||= RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil)
lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
ua = {
:bindings_version => Stripe.version,
:lang => 'ruby',
:lang_version => lang_version,
:platform => RUBY_PLATFORM,
:publisher => 'stripe',
:uname => uname
}
headers = {
:x_stripe_client_user_agent => JSON.dump(ua),
:user_agent => "Stripe/v1 RubyBindings/#{Stripe.version}"
}.merge(headers)
opts = {
:method => method,
:url => self.api_url(url),
:user => api_key,
:headers => headers,
:payload => Util.objects_to_ids(params),
:open_timeout => 30,
:timeout => 80
}.merge(ssl_opts)
begin
response = execute_request(opts)
rescue SocketError => e
self.handle_restclient_error(e)
rescue NoMethodError => e
# Work around RestClient bug
if e.message =~ /\WRequestFailed\W/
e = APIConnectionError.new('Unexpected HTTP response code')
self.handle_restclient_error(e)
else
raise
end
rescue RestClient::ExceptionWithResponse => e
if rcode = e.http_code and rbody = e.http_body
self.handle_api_error(rcode, rbody)
else
self.handle_restclient_error(e)
end
rescue RestClient::Exception, Errno::ECONNREFUSED => e
self.handle_restclient_error(e)
end
rbody = response.body
rcode = response.code
begin
resp = JSON.parse(rbody, :symbolize_names => true)
rescue JSON::ParseError
raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})")
end
[resp, api_key]
end
private
def self.execute_request(opts)
RestClient::Request.execute(opts)
end
def self.handle_api_error(rcode, rbody)
begin
error_obj = JSON.parse(rbody, :symbolize_names => true)
error = error_obj[:error] or raise StripeError.new
rescue JSON::ParserError, StripeError
raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})")
end
case rcode
when 400, 404 then
raise invalid_request_error(error)
when 401
raise authentication_error(error)
when 402
raise card_error(error)
else
raise api_error(error)
end
end
def self.invalid_request_error(error); InvalidRequestError.new(error[:message], error[:param]); end
def self.authentication_error(error); AuthenticationError.new(error[:message]); end
def self.card_error(error); CardError.new(error[:message], error[:param], error[:code]); end
def self.api_error(error); APIError.new(error[:message]); end
def self.handle_restclient_error(e)
case e
when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
message = "Could not connect to Stripe (#{@@api_base}). Please check your internet connection and try again. If this problem persists, you should check Stripe's service status at https://twitter.com/stripe, or let us know at support@stripe.com."
when RestClient::SSLCertificateNotVerified
message = "Could not verify Stripe's SSL certificate. Please make sure that your network is not intercepting certificates. (Try going to https://api.stripe.com in your browser.) If this problem persists, let us know at support@stripe.com."
when SocketError
message = "Unexpected error communicating when trying to connect to Stripe. HINT: You may be seeing this message because your DNS is not working. To check, try running 'host stripe.com' from the command line."
else
message = "Unexpected error communicating with Stripe. If this problem persists, let us know at support@stripe.com."
end
message += "\n\n(Network error: #{e.message})"
raise APIConnectionError.new(message)
end
end

24
stripe.gemspec Normal file
View File

@ -0,0 +1,24 @@
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
require 'stripe'
spec = Gem::Specification.new do |s|
s.name = 'stripe'
s.version = Stripe.version
s.summary = 'Ruby bindings for the Stripe API'
s.description = 'Stripe is the easiest way to accept payments online. See https://stripe.com for details.'
s.authors = ['Ross Boucher', 'Greg Brockman']
s.email = ['boucher@stripe.com', 'gdb@stripe.com']
s.homepage = 'https://stripe.com/api'
s.executables = 'stripe-console'
s.require_paths = %w{lib}
s.add_dependency('json')
s.add_dependency('rest-client')
s.files = %w{
bin/stripe-console
lib/stripe.rb
lib/data/ca-certificates.crt
}
end

136
test/test_helper.rb Normal file
View File

@ -0,0 +1,136 @@
require 'stringio'
require 'test/unit'
require File.dirname(__FILE__) + '/../lib/stripe'
require 'mocha'
include Mocha
#monkeypatch request methods
module Stripe
@mock_rest_client = nil
def self.mock_rest_client=(mock_client)
@mock_rest_client = mock_client
end
def self.execute_request(opts)
case opts[:method]
when :get: @mock_rest_client.get opts[:url]
when :post: @mock_rest_client.post opts[:url], opts[:payload]
when :delete: @mock_rest_client.delete opts[:url]
end
end
end
def test_response(body, code=200)
# When an exception is raised, restclient clobbers method_missing. Hence we
# can't just use the stubs interface.
body = body.to_json if !(body.kind_of? String)
m = mock
m.instance_variable_set('@stripe_values', { :body => body, :code => code })
def m.body; @stripe_values[:body]; end
def m.code; @stripe_values[:code]; end
m
end
def test_customer(params={})
{
:subscription_history => [],
:bills => [],
:charges => [],
:livemode => false,
:object => "customer",
:id => "c_test_customer",
:active_card => {
:type => "Visa",
:last4 => "4242",
:exp_month => 11,
:country => "US",
:exp_year => 2012,
:id => "cc_test_card",
:object => "card"
},
:created => 1304114758
}.merge(params)
end
def test_customer_array
[test_customer, test_customer, test_customer]
end
def test_charge(params={})
{
:refunded => false,
:paid => true,
:amount => 100,
:card => {
:type => "Visa",
:last4 => "4242",
:exp_month => 11,
:country => "US",
:exp_year => 2012,
:id => "cc_test_card",
:object => "card"
},
:id => "ch_test_charge",
:reason => "execute_charge",
:livemode => false,
:currency => "usd",
:object => "charge",
:created => 1304114826
}.merge(params)
end
def test_charge_array
[test_charge, test_charge, test_charge]
end
def test_card(params={})
{
:type => "Visa",
:last4 => "4242",
:exp_month => 11,
:country => "US",
:exp_year => 2012,
:id => "cc_test_card",
:object => "card"
}.merge(params)
end
def test_invalid_api_key_error
{
"error" => {
"type" => "invalid_request_error",
"message" => "Invalid API Key provided: invalid"
}
}
end
def test_invalid_exp_year_error
{
"error" => {
"code" => "invalid_expiry_year",
"param" => "exp_year",
"type" => "card_error",
"message" => "Your card's expiration year is invalid"
}
}
end
def test_missing_id_error
{
:error => {
:param => "id",
:type => "invalid_request_error",
:message => "Missing id"
}
}
end
def test_api_error
{
:error => {
:type => "api_error"
}
}
end

301
test/test_stripe.rb Normal file
View File

@ -0,0 +1,301 @@
require File.dirname(__FILE__) + '/test_helper.rb'
require 'test/unit'
require 'context'
require 'mocha'
require 'pp'
require 'rest-client'
class TestStripeRuby < Test::Unit::TestCase
include Mocha
context "API Bindings" do
before do
@mock = mock
Stripe.mock_rest_client = @mock
end
after do
Stripe.mock_rest_client = nil
end
test "creating a new APIResource should not fetch over the network" do
@mock.expects(:get).never
c = Stripe::Customer.new("someid")
end
test "creating a new APIResource from a hash should not fetch over the network" do
@mock.expects(:get).never
c = Stripe::Customer.construct_from({
:id => "somecustomer",
:card => {:id => "somecard", :object => "card"},
:object => "customer"
})
end
test "setting an attribute should not cause a network request" do
@mock.expects(:get).never
@mock.expects(:post).never
c = Stripe::Customer.new("test_customer");
c.card = {:id => "somecard", :object => "card"}
end
test "accessing id should not issue a fetch" do
@mock.expects(:get).never
c = Stripe::Customer.new("test_customer");
c.id
end
test "not specifying api credentials should raise an exception" do
assert_raises Stripe::AuthenticationError do
Stripe::Customer.new("test_customer").refresh
end
end
test "specifying invalid api credentials should raise an exception" do
Stripe.api_key="invalid"
response = test_response(test_invalid_api_key_error, 401)
assert_raises Stripe::AuthenticationError do
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
Stripe::Customer.retrieve("failing_customer")
end
end
context "with valid credentials" do
before do
Stripe.api_key="foo"
end
after do
Stripe.api_key=nil
end
test "requesting with a unicode ID should result in a request" do
response = test_response(test_missing_id_error, 404)
@mock.expects(:get).once.with("https://api.stripe.com/v1/customers/%E2%98%83").raises(RestClient::ExceptionWithResponse.new(response, 404))
c = Stripe::Customer.new("")
assert_raises(Stripe::InvalidRequestError) { c.refresh }
end
test "loading an object should issue a GET request" do
@mock.expects(:get).once.returns(test_response(test_customer))
c = Stripe::Customer.new("test_customer")
c.refresh
end
test "using array accessors should be the same as the method interface" do
@mock.expects(:get).once.returns(test_response(test_customer))
c = Stripe::Customer.new("test_customer")
c.refresh
assert_equal c.created, c[:created]
assert_equal c.created, c['created']
c['created'] = 12345
assert_equal c.created, 12345
end
test "accessing a property other than id or parent on an unfetched object should fetch it" do
@mock.expects(:get).once.returns(test_response(test_customer))
c = Stripe::Customer.new("test_customer")
c.charges
end
test "updating an object should issue a POST request with only the changed properties" do
@mock.expects(:post).with("https://api.stripe.com/v1/customers/c_test_customer", {:mnemonic => 'another_mn'}).once.returns(test_response(test_customer))
c = Stripe::Customer.construct_from(test_customer)
c.mnemonic = "another_mn"
c.save
end
test "updating should merge in returned properties" do
@mock.expects(:post).once.returns(test_response(test_customer))
c = Stripe::Customer.new("c_test_customer")
c.mnemonic = "another_mn"
c.save
assert_equal false, c.livemode
end
test "deleting should send no props and result in an object that has no props other deleted" do
@mock.expects(:get).never
@mock.expects(:post).never
@mock.expects(:delete).with("https://api.stripe.com/v1/customers/c_test_customer").once.returns(test_response({ "id" => "test_customer", "deleted" => true }))
c = Stripe::Customer.construct_from(test_customer)
c.delete
assert_equal true, c.deleted
assert_raises NoMethodError do
c.livemode
end
end
test "loading an object with properties that have specific types should instantiate those classes" do
@mock.expects(:get).once.returns(test_response(test_charge))
c = Stripe::Charge.retrieve("test_charge")
assert c.card.kind_of?(Stripe::StripeObject) && c.card.object == 'card'
end
test "loading all of an APIResource should return an array of recursively instantiated objects" do
@mock.expects(:get).once.returns(test_response(test_charge_array))
c = Stripe::Charge.all
assert c.kind_of? Array
assert c[0].kind_of? Stripe::Charge
assert c[0].card.kind_of?(Stripe::StripeObject) && c[0].card.object == 'card'
end
context "charge tests" do
test "charges should be listable" do
@mock.expects(:get).once.returns(test_response(test_charge_array))
c = Stripe::Charge.all
assert c.kind_of? Array
end
test "charges should be refundable" do
@mock.expects(:get).never
@mock.expects(:post).once.returns(test_response({:id => "ch_test_charge", :refunded => true}))
c = Stripe::Charge.new("test_charge")
c.refund
assert c.refunded
end
test "charges should not be deletable" do
assert_raises NoMethodError do
@mock.expects(:get).once.returns(test_response(test_charge))
c = Stripe::Charge.retrieve("test_charge")
c.delete
end
end
test "charges should not be updateable" do
assert_raises NoMethodError do
@mock.expects(:get).once.returns(test_response(test_charge))
c = Stripe::Charge.new("test_charge")
c.refresh
c.mnemonic= "YAY PASSING TEST!"
c.save
end
end
test "charges should have Card objects associated with their Card property" do
@mock.expects(:get).once.returns(test_response(test_charge))
c = Stripe::Charge.retrieve("test_charge")
assert c.card.kind_of?(Stripe::StripeObject) && c.card.object == 'card'
end
test "execute should return a new, fully executed charge when passed correct parameters" do
@mock.expects(:post).with('https://api.stripe.com/v1/charges', {
:currency => 'usd', :amount => 100,
:card => {:exp_year => 2012, :number => '4242424242424242', :exp_month => 11}
}).once.returns(test_response(test_charge))
c = Stripe::Charge.create({
:amount => 100,
:card => {
:number => "4242424242424242",
:exp_month => 11,
:exp_year => 2012,
},
:currency => "usd"
})
assert c.paid
end
end
context "customer tests" do
test "customers should be listable" do
@mock.expects(:get).once.returns(test_response(test_customer_array))
c = Stripe::Customer.all
assert c.kind_of? Array
assert c[0].kind_of? Stripe::Customer
end
test "customers should be deletable" do
@mock.expects(:delete).once.returns(test_response(test_customer({:deleted => true})))
c = Stripe::Customer.new("test_customer")
c.delete
assert c.deleted
end
test "customers should be updateable" do
@mock.expects(:get).once.returns(test_response(test_customer({:mnemonic => "foo"})))
@mock.expects(:post).once.returns(test_response(test_customer({:mnemonic => "bar"})))
c = Stripe::Customer.new("test_customer").refresh
assert_equal c.mnemonic, "foo"
c.mnemonic = "bar"
c.save
assert_equal c.mnemonic, "bar"
end
test "customers should have Card objects associated with their active_ard property" do
@mock.expects(:get).once.returns(test_response(test_customer))
c = Stripe::Customer.retrieve("test_customer")
assert c.active_card.kind_of?(Stripe::StripeObject) && c.active_card.object == 'card'
end
test "create should return a new customer" do
@mock.expects(:post).once.returns(test_response(test_customer))
c = Stripe::Customer.create
assert_equal "c_test_customer", c.id
end
end
context "card tests" do
end
context "error checking" do
test "404s should raise an InvalidRequestError" do
response = test_response(test_missing_id_error, 404)
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
begin
Stripe::Customer.new("test_customer").refresh
assert false #shouldn't get here either
rescue Stripe::InvalidRequestError => e # we don't use assert_raises because we want to examine e
assert e.kind_of? Stripe::InvalidRequestError
assert_equal "id", e.param
assert_equal "Missing id", e.message
return
end
assert false #shouldn't get here
end
test "5XXs should raise an APIError" do
response = test_response(test_api_error, 500)
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 500))
begin
Stripe::Customer.new("test_customer").refresh
assert false #shouldn't get here either
rescue Stripe::APIError => e # we don't use assert_raises because we want to examine e
assert e.kind_of? Stripe::APIError
return
end
assert false #shouldn't get here
end
test "402s should raise a CardError" do
response = test_response(test_invalid_exp_year_error, 402)
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 402))
begin
Stripe::Customer.new("test_customer").refresh
assert false #shouldn't get here either
rescue Stripe::CardError => e # we don't use assert_raises because we want to examine e
assert e.kind_of? Stripe::CardError
assert_equal "invalid_expiry_year", e.code
assert_equal "exp_year", e.param
assert_equal "Your card's expiration year is invalid", e.message
return
end
assert false #shouldn't get here
end
end
end
end
end