payjp 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rubocop.yml +82 -0
- data/.rubocop_todo.yml +35 -0
- data/.travis.yml +22 -0
- data/CONTRIBUTORS +0 -0
- data/Gemfile +8 -0
- data/History.txt +0 -0
- data/LICENSE +21 -0
- data/README.rdoc +43 -0
- data/Rakefile +8 -0
- data/VERSION +1 -0
- data/gemfiles/default-with-activesupport.gemfile +10 -0
- data/gemfiles/json.gemfile +12 -0
- data/gemfiles/yajl.gemfile +12 -0
- data/lib/payjp.rb +279 -0
- data/lib/payjp/account.rb +11 -0
- data/lib/payjp/api_operations/create.rb +16 -0
- data/lib/payjp/api_operations/delete.rb +11 -0
- data/lib/payjp/api_operations/list.rb +17 -0
- data/lib/payjp/api_operations/request.rb +39 -0
- data/lib/payjp/api_operations/update.rb +17 -0
- data/lib/payjp/api_resource.rb +35 -0
- data/lib/payjp/card.rb +18 -0
- data/lib/payjp/charge.rb +27 -0
- data/lib/payjp/customer.rb +8 -0
- data/lib/payjp/errors/api_connection_error.rb +4 -0
- data/lib/payjp/errors/api_error.rb +4 -0
- data/lib/payjp/errors/authentication_error.rb +4 -0
- data/lib/payjp/errors/card_error.rb +11 -0
- data/lib/payjp/errors/invalid_request_error.rb +10 -0
- data/lib/payjp/errors/payjp_error.rb +20 -0
- data/lib/payjp/event.rb +5 -0
- data/lib/payjp/list_object.rb +33 -0
- data/lib/payjp/payjp_object.rb +259 -0
- data/lib/payjp/plan.rb +8 -0
- data/lib/payjp/subscription.rb +37 -0
- data/lib/payjp/token.rb +5 -0
- data/lib/payjp/transfer.rb +5 -0
- data/lib/payjp/util.rb +121 -0
- data/lib/payjp/version.rb +3 -0
- data/payjp.gemspec +27 -0
- data/test/payjp/account_test.rb +17 -0
- data/test/payjp/api_resource_test.rb +445 -0
- data/test/payjp/charge_test.rb +83 -0
- data/test/payjp/customer_card_test.rb +61 -0
- data/test/payjp/customer_test.rb +35 -0
- data/test/payjp/event_test.rb +26 -0
- data/test/payjp/list_object_test.rb +16 -0
- data/test/payjp/metadata_test.rb +103 -0
- data/test/payjp/payjp_object_test.rb +28 -0
- data/test/payjp/plan_test.rb +48 -0
- data/test/payjp/subscription_test.rb +58 -0
- data/test/payjp/token_test.rb +34 -0
- data/test/payjp/transfer_test.rb +34 -0
- data/test/payjp/util_test.rb +34 -0
- data/test/test_data.rb +291 -0
- data/test/test_helper.rb +45 -0
- metadata +201 -0
data/lib/payjp/plan.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Payjp
|
2
|
+
class Subscription < APIResource
|
3
|
+
include Payjp::APIOperations::List
|
4
|
+
include Payjp::APIOperations::Create
|
5
|
+
include Payjp::APIOperations::Update
|
6
|
+
include Payjp::APIOperations::Delete
|
7
|
+
|
8
|
+
def pause(params = {}, opts = {})
|
9
|
+
response, opts = request(:post, pause_url, params, opts)
|
10
|
+
refresh_from(response, opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
def resume(params = {}, opts = {})
|
14
|
+
response, opts = request(:post, resume_url, params, opts)
|
15
|
+
refresh_from(response, opts)
|
16
|
+
end
|
17
|
+
|
18
|
+
def cancel(params = {}, opts = {})
|
19
|
+
response, opts = request(:post, cancel_url, params, opts)
|
20
|
+
refresh_from(response, opts)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def pause_url
|
26
|
+
url + '/pause'
|
27
|
+
end
|
28
|
+
|
29
|
+
def resume_url
|
30
|
+
url + '/resume'
|
31
|
+
end
|
32
|
+
|
33
|
+
def cancel_url
|
34
|
+
url + '/cancel'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/payjp/token.rb
ADDED
data/lib/payjp/util.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
module Payjp
|
2
|
+
module Util
|
3
|
+
def self.objects_to_ids(h)
|
4
|
+
case h
|
5
|
+
when APIResource
|
6
|
+
h.id
|
7
|
+
when Hash
|
8
|
+
res = {}
|
9
|
+
h.each { |k, v| res[k] = objects_to_ids(v) unless v.nil? }
|
10
|
+
res
|
11
|
+
when Array
|
12
|
+
h.map { |v| objects_to_ids(v) }
|
13
|
+
else
|
14
|
+
h
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.object_classes
|
19
|
+
@object_classes ||= {
|
20
|
+
# data structures
|
21
|
+
'list' => ListObject,
|
22
|
+
|
23
|
+
# business objects
|
24
|
+
'account' => Account,
|
25
|
+
'card' => Card,
|
26
|
+
'charge' => Charge,
|
27
|
+
'customer' => Customer,
|
28
|
+
'event' => Event,
|
29
|
+
'plan' => Plan,
|
30
|
+
'subscription' => Subscription,
|
31
|
+
'token' => Token,
|
32
|
+
'transfer' => Transfer
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.convert_to_payjp_object(resp, opts)
|
37
|
+
case resp
|
38
|
+
when Array
|
39
|
+
resp.map { |i| convert_to_payjp_object(i, opts) }
|
40
|
+
when Hash
|
41
|
+
# Try converting to a known object class. If none available, fall back to generic PayjpObject
|
42
|
+
object_classes.fetch(resp[:object], PayjpObject).construct_from(resp, opts)
|
43
|
+
else
|
44
|
+
resp
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.symbolize_names(object)
|
49
|
+
case object
|
50
|
+
when Hash
|
51
|
+
new_hash = {}
|
52
|
+
object.each do |key, value|
|
53
|
+
key = (key.to_sym rescue key) || key
|
54
|
+
new_hash[key] = symbolize_names(value)
|
55
|
+
end
|
56
|
+
new_hash
|
57
|
+
when Array
|
58
|
+
object.map { |value| symbolize_names(value) }
|
59
|
+
else
|
60
|
+
object
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.url_encode(key)
|
65
|
+
URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.flatten_params(params, parent_key = nil)
|
69
|
+
result = []
|
70
|
+
params.each do |key, value|
|
71
|
+
calculated_key = parent_key ? "#{parent_key}[#{url_encode(key)}]" : url_encode(key)
|
72
|
+
if value.is_a?(Hash)
|
73
|
+
result += flatten_params(value, calculated_key)
|
74
|
+
elsif value.is_a?(Array)
|
75
|
+
result += flatten_params_array(value, calculated_key)
|
76
|
+
else
|
77
|
+
result << [calculated_key, value]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
result
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.flatten_params_array(value, calculated_key)
|
84
|
+
result = []
|
85
|
+
value.each do |elem|
|
86
|
+
if elem.is_a?(Hash)
|
87
|
+
result += flatten_params(elem, calculated_key)
|
88
|
+
elsif elem.is_a?(Array)
|
89
|
+
result += flatten_params_array(elem, calculated_key)
|
90
|
+
else
|
91
|
+
result << ["#{calculated_key}[]", elem]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
result
|
95
|
+
end
|
96
|
+
|
97
|
+
# The secondary opts argument can either be a string or hash
|
98
|
+
# Turn this value into an api_key and a set of headers
|
99
|
+
def self.normalize_opts(opts)
|
100
|
+
case opts
|
101
|
+
when String
|
102
|
+
{ :api_key => opts }
|
103
|
+
when Hash
|
104
|
+
check_api_key!(opts.fetch(:api_key)) if opts.has_key?(:api_key)
|
105
|
+
opts.clone
|
106
|
+
else
|
107
|
+
raise TypeError.new('normalize_opts expects a string or a hash')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.check_string_argument!(key)
|
112
|
+
raise TypeError.new("argument must be a string") unless key.is_a?(String)
|
113
|
+
key
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.check_api_key!(key)
|
117
|
+
raise TypeError.new("api_key must be a string") unless key.is_a?(String)
|
118
|
+
key
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/payjp.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
2
|
+
|
3
|
+
require 'payjp/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'payjp'
|
7
|
+
s.version = Payjp::VERSION
|
8
|
+
s.summary = 'Ruby bindings for the Payjp API'
|
9
|
+
s.description = 'PAY.JP is the easiest way to accept payments online.'
|
10
|
+
s.authors = ['PAY.JP']
|
11
|
+
s.email = ['support@pay.jp']
|
12
|
+
s.homepage = 'https://pay.jp'
|
13
|
+
s.license = 'MIT'
|
14
|
+
|
15
|
+
s.add_dependency('rest-client', '~> 1.4')
|
16
|
+
s.add_dependency('json', '~> 1.8.1')
|
17
|
+
|
18
|
+
s.add_development_dependency('mocha', '~> 0.13.2')
|
19
|
+
s.add_development_dependency('shoulda', '~> 3.4.0')
|
20
|
+
s.add_development_dependency('test-unit')
|
21
|
+
s.add_development_dependency('rake')
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
26
|
+
s.require_paths = ['lib']
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
module Payjp
|
4
|
+
class AccountTest < Test::Unit::TestCase
|
5
|
+
should "be retrievable" do
|
6
|
+
resp = { :email => "test+bindings@pay.jp", :accounts_enabled => ['merchant', 'customer'], :merchant => { :bank_enabled => false } }
|
7
|
+
@mock.expects(:get).
|
8
|
+
once.
|
9
|
+
with("#{Payjp.api_base}/v1/accounts", nil, nil).
|
10
|
+
returns(test_response(resp))
|
11
|
+
a = Payjp::Account.retrieve
|
12
|
+
assert_equal "test+bindings@pay.jp", a.email
|
13
|
+
assert_equal ['merchant', 'customer'], a.accounts_enabled
|
14
|
+
assert !a.merchant.bank_enabled
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,445 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require File.expand_path('../../test_helper', __FILE__)
|
3
|
+
|
4
|
+
module Payjp
|
5
|
+
class ApiResourceTest < Test::Unit::TestCase
|
6
|
+
should "creating a new APIResource should not fetch over the network" do
|
7
|
+
@mock.expects(:get).never
|
8
|
+
Payjp::Customer.new("someid")
|
9
|
+
end
|
10
|
+
|
11
|
+
should "creating a new APIResource from a hash should not fetch over the network" do
|
12
|
+
@mock.expects(:get).never
|
13
|
+
Payjp::Customer.construct_from({
|
14
|
+
:id => "somecustomer",
|
15
|
+
:card => { :id => "somecard", :object => "card" },
|
16
|
+
:object => "customer"
|
17
|
+
})
|
18
|
+
end
|
19
|
+
|
20
|
+
should "setting an attribute should not cause a network request" do
|
21
|
+
@mock.expects(:get).never
|
22
|
+
@mock.expects(:post).never
|
23
|
+
c = Payjp::Customer.new("test_customer")
|
24
|
+
c.card = { :id => "somecard", :object => "card" }
|
25
|
+
end
|
26
|
+
|
27
|
+
should "accessing id should not issue a fetch" do
|
28
|
+
@mock.expects(:get).never
|
29
|
+
c = Payjp::Customer.new("test_customer")
|
30
|
+
c.id
|
31
|
+
end
|
32
|
+
|
33
|
+
should "not specifying api credentials should raise an exception" do
|
34
|
+
Payjp.api_key = nil
|
35
|
+
assert_raises Payjp::AuthenticationError do
|
36
|
+
Payjp::Customer.new("test_customer").refresh
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
should "using a nil api key should raise an exception" do
|
41
|
+
assert_raises TypeError do
|
42
|
+
Payjp::Customer.all({}, nil)
|
43
|
+
end
|
44
|
+
assert_raises TypeError do
|
45
|
+
Payjp::Customer.all({}, { :api_key => nil })
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
should "specifying api credentials containing whitespace should raise an exception" do
|
50
|
+
Payjp.api_key = "key "
|
51
|
+
assert_raises Payjp::AuthenticationError do
|
52
|
+
Payjp::Customer.new("test_customer").refresh
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
should "specifying invalid api credentials should raise an exception" do
|
57
|
+
Payjp.api_key = "invalid"
|
58
|
+
response = test_response(test_invalid_api_key_error, 401)
|
59
|
+
assert_raises Payjp::AuthenticationError do
|
60
|
+
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
|
61
|
+
Payjp::Customer.retrieve("failing_customer")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
should "AuthenticationErrors should have an http status, http body, and JSON body" do
|
66
|
+
Payjp.api_key = "invalid"
|
67
|
+
response = test_response(test_invalid_api_key_error, 401)
|
68
|
+
begin
|
69
|
+
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
|
70
|
+
Payjp::Customer.retrieve("failing_customer")
|
71
|
+
rescue Payjp::AuthenticationError => e
|
72
|
+
assert_equal(401, e.http_status)
|
73
|
+
assert_equal(true, !!e.http_body)
|
74
|
+
assert_equal(true, !!e.json_body[:error][:message])
|
75
|
+
assert_equal(test_invalid_api_key_error[:error][:message], e.json_body[:error][:message])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
should "send expand on fetch properly" do
|
80
|
+
@mock.expects(:get).once.
|
81
|
+
with("#{Payjp.api_base}/v1/charges/ch_test_charge?expand[]=customer", nil, nil).
|
82
|
+
returns(test_response(test_charge))
|
83
|
+
|
84
|
+
Payjp::Charge.retrieve({ :id => 'ch_test_charge', :expand => [:customer] })
|
85
|
+
end
|
86
|
+
|
87
|
+
should "preserve expand across refreshes" do
|
88
|
+
@mock.expects(:get).twice.
|
89
|
+
with("#{Payjp.api_base}/v1/charges/ch_test_charge?expand[]=customer", nil, nil).
|
90
|
+
returns(test_response(test_charge))
|
91
|
+
|
92
|
+
ch = Payjp::Charge.retrieve({ :id => 'ch_test_charge', :expand => [:customer] })
|
93
|
+
ch.refresh
|
94
|
+
end
|
95
|
+
|
96
|
+
should "send payjp account as header when set" do
|
97
|
+
payjp_account = "acct_0000"
|
98
|
+
Payjp.expects(:execute_request).with do |opts|
|
99
|
+
opts[:headers][:payjp_account] == payjp_account
|
100
|
+
end.returns(test_response(test_charge))
|
101
|
+
|
102
|
+
Payjp::Charge.create({ :card => { :number => '4242424242424242' } },
|
103
|
+
{ :payjp_account => payjp_account, :api_key => 'sk_test_local' })
|
104
|
+
end
|
105
|
+
|
106
|
+
should "not send payjp account as header when not set" do
|
107
|
+
Payjp.expects(:execute_request).with do |opts|
|
108
|
+
opts[:headers][:payjp_account].nil?
|
109
|
+
end.returns(test_response(test_charge))
|
110
|
+
|
111
|
+
Payjp::Charge.create({ :card => { :number => '4242424242424242' } },
|
112
|
+
'sk_test_local')
|
113
|
+
end
|
114
|
+
|
115
|
+
context "when specifying per-object credentials" do
|
116
|
+
context "with no global API key set" do
|
117
|
+
should "use the per-object credential when creating" do
|
118
|
+
api_key = 'sk_test_local'
|
119
|
+
Payjp.expects(:execute_request).with do |opts|
|
120
|
+
opts[:headers][:authorization] == encode_credentials(api_key)
|
121
|
+
end.returns(test_response(test_charge))
|
122
|
+
|
123
|
+
Payjp::Charge.create({ :card => { :number => '4242424242424242' } },
|
124
|
+
api_key)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "with a global API key set" do
|
129
|
+
setup do
|
130
|
+
Payjp.api_key = "global"
|
131
|
+
end
|
132
|
+
|
133
|
+
teardown do
|
134
|
+
Payjp.api_key = nil
|
135
|
+
end
|
136
|
+
|
137
|
+
should "use the per-object credential when creating" do
|
138
|
+
api_key = 'local'
|
139
|
+
Payjp.expects(:execute_request).with do |opts|
|
140
|
+
opts[:headers][:authorization] == encode_credentials(api_key)
|
141
|
+
end.returns(test_response(test_charge))
|
142
|
+
|
143
|
+
Payjp::Charge.create({ :card => { :number => '4242424242424242' } },
|
144
|
+
api_key)
|
145
|
+
end
|
146
|
+
|
147
|
+
should "use the per-object credential when retrieving and making other calls" do
|
148
|
+
api_key = 'local'
|
149
|
+
Payjp.expects(:execute_request).with do |opts|
|
150
|
+
opts[:url] == "#{Payjp.api_base}/v1/charges/ch_test_charge" &&
|
151
|
+
opts[:headers][:authorization] == encode_credentials(api_key)
|
152
|
+
end.returns(test_response(test_charge))
|
153
|
+
Payjp.expects(:execute_request).with do |opts|
|
154
|
+
opts[:url] == "#{Payjp.api_base}/v1/charges/ch_test_charge/refund" &&
|
155
|
+
opts[:headers][:authorization] == encode_credentials(api_key)
|
156
|
+
end.returns(test_response(test_charge))
|
157
|
+
|
158
|
+
ch = Payjp::Charge.retrieve('ch_test_charge', api_key)
|
159
|
+
ch.refund
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "with valid credentials" do
|
165
|
+
should "send along the idempotency-key header" do
|
166
|
+
Payjp.expects(:execute_request).with do |opts|
|
167
|
+
opts[:headers][:idempotency_key] == 'bar'
|
168
|
+
end.returns(test_response(test_charge))
|
169
|
+
|
170
|
+
Payjp::Charge.create({ :card => { :number => '4242424242424242' } }, {
|
171
|
+
:idempotency_key => 'bar',
|
172
|
+
:api_key => 'local'
|
173
|
+
})
|
174
|
+
end
|
175
|
+
|
176
|
+
should "urlencode values in GET params" do
|
177
|
+
response = test_response(test_charge_array)
|
178
|
+
@mock.expects(:get).with("#{Payjp.api_base}/v1/charges?customer=test%20customer", nil, nil).returns(response)
|
179
|
+
charges = Payjp::Charge.all(:customer => 'test customer').data
|
180
|
+
assert charges.is_a? Array
|
181
|
+
end
|
182
|
+
|
183
|
+
should "a 400 should give an InvalidRequestError with http status, body, and JSON body" do
|
184
|
+
response = test_response(test_missing_id_error, 400)
|
185
|
+
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
|
186
|
+
begin
|
187
|
+
Payjp::Customer.retrieve("foo")
|
188
|
+
rescue Payjp::InvalidRequestError => e
|
189
|
+
assert_equal(400, e.http_status)
|
190
|
+
assert_equal(true, !!e.http_body)
|
191
|
+
assert_equal(true, e.json_body.is_a?(Hash))
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
should "a 401 should give an AuthenticationError with http status, body, and JSON body" do
|
196
|
+
response = test_response(test_missing_id_error, 401)
|
197
|
+
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
|
198
|
+
begin
|
199
|
+
Payjp::Customer.retrieve("foo")
|
200
|
+
rescue Payjp::AuthenticationError => e
|
201
|
+
assert_equal(401, e.http_status)
|
202
|
+
assert_equal(true, !!e.http_body)
|
203
|
+
assert_equal(true, e.json_body.is_a?(Hash))
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
should "a 402 should give a CardError with http status, body, and JSON body" do
|
208
|
+
response = test_response(test_missing_id_error, 402)
|
209
|
+
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
|
210
|
+
begin
|
211
|
+
Payjp::Customer.retrieve("foo")
|
212
|
+
rescue Payjp::CardError => e
|
213
|
+
assert_equal(402, e.http_status)
|
214
|
+
assert_equal(true, !!e.http_body)
|
215
|
+
assert_equal(true, e.json_body.is_a?(Hash))
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
should "a 404 should give an InvalidRequestError with http status, body, and JSON body" do
|
220
|
+
response = test_response(test_missing_id_error, 404)
|
221
|
+
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
|
222
|
+
begin
|
223
|
+
Payjp::Customer.retrieve("foo")
|
224
|
+
rescue Payjp::InvalidRequestError => e
|
225
|
+
assert_equal(404, e.http_status)
|
226
|
+
assert_equal(true, !!e.http_body)
|
227
|
+
assert_equal(true, e.json_body.is_a?(Hash))
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
should "setting a nil value for a param should exclude that param from the request" do
|
232
|
+
@mock.expects(:get).with do |url, _api_key, _params|
|
233
|
+
uri = URI(url)
|
234
|
+
query = CGI.parse(uri.query)
|
235
|
+
(url =~ %r{^#{Payjp.api_base}/v1/charges?} &&
|
236
|
+
query.keys.sort == ['offset', 'sad'])
|
237
|
+
end.returns(test_response({ :count => 1, :data => [test_charge] }))
|
238
|
+
Payjp::Charge.all(:count => nil, :offset => 5, :sad => false)
|
239
|
+
|
240
|
+
@mock.expects(:post).with do |url, api_key, params|
|
241
|
+
url == "#{Payjp.api_base}/v1/charges" &&
|
242
|
+
api_key.nil? &&
|
243
|
+
CGI.parse(params) == { 'amount' => ['50'], 'currency' => ['jpy'] }
|
244
|
+
end.returns(test_response({ :count => 1, :data => [test_charge] }))
|
245
|
+
Payjp::Charge.create(:amount => 50, :currency => 'jpy', :card => { :number => nil })
|
246
|
+
end
|
247
|
+
|
248
|
+
should "requesting with a unicode ID should result in a request" do
|
249
|
+
response = test_response(test_missing_id_error, 404)
|
250
|
+
@mock.expects(:get).once.with("#{Payjp.api_base}/v1/customers/%E2%98%83", nil, nil).raises(RestClient::ExceptionWithResponse.new(response, 404))
|
251
|
+
c = Payjp::Customer.new("☃")
|
252
|
+
assert_raises(Payjp::InvalidRequestError) { c.refresh }
|
253
|
+
end
|
254
|
+
|
255
|
+
should "requesting with no ID should result in an InvalidRequestError with no request" do
|
256
|
+
c = Payjp::Customer.new
|
257
|
+
assert_raises(Payjp::InvalidRequestError) { c.refresh }
|
258
|
+
end
|
259
|
+
|
260
|
+
should "making a GET request with parameters should have a query string and no body" do
|
261
|
+
params = { :limit => 1 }
|
262
|
+
@mock.expects(:get).once.with("#{Payjp.api_base}/v1/charges?limit=1", nil, nil).returns(test_response([test_charge]))
|
263
|
+
Payjp::Charge.all(params)
|
264
|
+
end
|
265
|
+
|
266
|
+
should "making a POST request with parameters should have a body and no query string" do
|
267
|
+
params = { :amount => 100, :currency => 'jpy', :card => 'sc_token' }
|
268
|
+
@mock.expects(:post).once.with do |_url, get, post|
|
269
|
+
get.nil? && CGI.parse(post) == { 'amount' => ['100'], 'currency' => ['jpy'], 'card' => ['sc_token'] }
|
270
|
+
end.returns(test_response(test_charge))
|
271
|
+
Payjp::Charge.create(params)
|
272
|
+
end
|
273
|
+
|
274
|
+
should "loading an object should issue a GET request" do
|
275
|
+
@mock.expects(:get).once.returns(test_response(test_customer))
|
276
|
+
c = Payjp::Customer.new("test_customer")
|
277
|
+
c.refresh
|
278
|
+
end
|
279
|
+
|
280
|
+
should "using array accessors should be the same as the method interface" do
|
281
|
+
@mock.expects(:get).once.returns(test_response(test_customer))
|
282
|
+
c = Payjp::Customer.new("test_customer")
|
283
|
+
c.refresh
|
284
|
+
assert_equal c.created, c[:created]
|
285
|
+
assert_equal c.created, c['created']
|
286
|
+
c['created'] = 12345
|
287
|
+
assert_equal c.created, 12345
|
288
|
+
end
|
289
|
+
|
290
|
+
should "updating an object should issue a POST request with only the changed properties" do
|
291
|
+
@mock.expects(:post).with do |url, api_key, params|
|
292
|
+
url == "#{Payjp.api_base}/v1/customers/c_test_customer" && api_key.nil? && CGI.parse(params) == { 'description' => ['another_mn'] }
|
293
|
+
end.once.returns(test_response(test_customer))
|
294
|
+
c = Payjp::Customer.construct_from(test_customer)
|
295
|
+
c.description = "another_mn"
|
296
|
+
c.save
|
297
|
+
end
|
298
|
+
|
299
|
+
should "updating should merge in returned properties" do
|
300
|
+
@mock.expects(:post).once.returns(test_response(test_customer))
|
301
|
+
c = Payjp::Customer.new("c_test_customer")
|
302
|
+
c.description = "another_mn"
|
303
|
+
c.save
|
304
|
+
assert_equal false, c.livemode
|
305
|
+
end
|
306
|
+
|
307
|
+
should "deleting should send no props and result in an object that has no props other deleted" do
|
308
|
+
@mock.expects(:get).never
|
309
|
+
@mock.expects(:post).never
|
310
|
+
@mock.expects(:delete).with("#{Payjp.api_base}/v1/customers/c_test_customer", nil, nil).once.returns(test_response({ "id" => "test_customer", "deleted" => true }))
|
311
|
+
c = Payjp::Customer.construct_from(test_customer)
|
312
|
+
c.delete
|
313
|
+
assert_equal true, c.deleted
|
314
|
+
|
315
|
+
assert_raises NoMethodError do
|
316
|
+
c.livemode
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
should "loading an object with properties that have specific types should instantiate those classes" do
|
321
|
+
@mock.expects(:get).once.returns(test_response(test_charge))
|
322
|
+
c = Payjp::Charge.retrieve("test_charge")
|
323
|
+
assert c.card.is_a?(Payjp::PayjpObject) && c.card.object == 'card'
|
324
|
+
end
|
325
|
+
|
326
|
+
should "loading all of an APIResource should return an array of recursively instantiated objects" do
|
327
|
+
@mock.expects(:get).once.returns(test_response(test_charge_array))
|
328
|
+
c = Payjp::Charge.all.data
|
329
|
+
assert c.is_a? Array
|
330
|
+
assert c[0].is_a? Payjp::Charge
|
331
|
+
assert c[0].card.is_a?(Payjp::PayjpObject) && c[0].card.object == 'card'
|
332
|
+
end
|
333
|
+
|
334
|
+
should "passing in a payjp_account header should pass it through on call" do
|
335
|
+
Payjp.expects(:execute_request).with do |opts|
|
336
|
+
opts[:method] == :get &&
|
337
|
+
opts[:url] == "#{Payjp.api_base}/v1/customers/c_test_customer" &&
|
338
|
+
opts[:headers][:payjp_account] == 'acct_abc'
|
339
|
+
end.once.returns(test_response(test_customer))
|
340
|
+
Payjp::Customer.retrieve("c_test_customer", { :payjp_account => 'acct_abc' })
|
341
|
+
end
|
342
|
+
|
343
|
+
should "passing in a payjp_account header should pass it through on save" do
|
344
|
+
Payjp.expects(:execute_request).with do |opts|
|
345
|
+
opts[:method] == :get &&
|
346
|
+
opts[:url] == "#{Payjp.api_base}/v1/customers/c_test_customer" &&
|
347
|
+
opts[:headers][:payjp_account] == 'acct_abc'
|
348
|
+
end.once.returns(test_response(test_customer))
|
349
|
+
c = Payjp::Customer.retrieve("c_test_customer", { :payjp_account => 'acct_abc' })
|
350
|
+
|
351
|
+
Payjp.expects(:execute_request).with do |opts|
|
352
|
+
opts[:method] == :post &&
|
353
|
+
opts[:url] == "#{Payjp.api_base}/v1/customers/c_test_customer" &&
|
354
|
+
opts[:headers][:payjp_account] == 'acct_abc' &&
|
355
|
+
opts[:payload] == 'description=FOO'
|
356
|
+
end.once.returns(test_response(test_customer))
|
357
|
+
c.description = 'FOO'
|
358
|
+
c.save
|
359
|
+
end
|
360
|
+
|
361
|
+
context "error checking" do
|
362
|
+
should "404s should raise an InvalidRequestError" do
|
363
|
+
response = test_response(test_missing_id_error, 404)
|
364
|
+
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 404))
|
365
|
+
|
366
|
+
rescued = false
|
367
|
+
begin
|
368
|
+
Payjp::Customer.new("test_customer").refresh
|
369
|
+
assert false # shouldn't get here either
|
370
|
+
rescue Payjp::InvalidRequestError => e # we don't use assert_raises because we want to examine e
|
371
|
+
rescued = true
|
372
|
+
assert e.is_a? Payjp::InvalidRequestError
|
373
|
+
assert_equal "id", e.param
|
374
|
+
assert_equal "Missing id", e.message
|
375
|
+
end
|
376
|
+
|
377
|
+
assert_equal true, rescued
|
378
|
+
end
|
379
|
+
|
380
|
+
should "5XXs should raise an APIError" do
|
381
|
+
response = test_response(test_api_error, 500)
|
382
|
+
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 500))
|
383
|
+
|
384
|
+
rescued = false
|
385
|
+
begin
|
386
|
+
Payjp::Customer.new("test_customer").refresh
|
387
|
+
assert false # shouldn't get here either
|
388
|
+
rescue Payjp::APIError => e # we don't use assert_raises because we want to examine e
|
389
|
+
rescued = true
|
390
|
+
assert e.is_a? Payjp::APIError
|
391
|
+
end
|
392
|
+
|
393
|
+
assert_equal true, rescued
|
394
|
+
end
|
395
|
+
|
396
|
+
should "402s should raise a CardError" do
|
397
|
+
response = test_response(test_invalid_exp_year_error, 402)
|
398
|
+
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 402))
|
399
|
+
|
400
|
+
rescued = false
|
401
|
+
begin
|
402
|
+
Payjp::Customer.new("test_customer").refresh
|
403
|
+
assert false # shouldn't get here either
|
404
|
+
rescue Payjp::CardError => e # we don't use assert_raises because we want to examine e
|
405
|
+
rescued = true
|
406
|
+
assert e.is_a? Payjp::CardError
|
407
|
+
assert_equal "invalid_expiry_year", e.code
|
408
|
+
assert_equal "exp_year", e.param
|
409
|
+
assert_equal "Your card's expiration year is invalid", e.message
|
410
|
+
end
|
411
|
+
|
412
|
+
assert_equal true, rescued
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
should 'save nothing if nothing changes' do
|
417
|
+
ch = Payjp::Charge.construct_from({
|
418
|
+
:id => 'charge_id',
|
419
|
+
:customer => {
|
420
|
+
:object => 'customer',
|
421
|
+
:id => 'customer_id'
|
422
|
+
}
|
423
|
+
})
|
424
|
+
|
425
|
+
@mock.expects(:post).once.with("#{Payjp.api_base}/v1/charges/charge_id", nil, '').returns(test_response({ "id" => "charge_id" }))
|
426
|
+
ch.save
|
427
|
+
end
|
428
|
+
|
429
|
+
should 'not save nested API resources' do
|
430
|
+
ch = Payjp::Charge.construct_from({
|
431
|
+
:id => 'charge_id',
|
432
|
+
:customer => {
|
433
|
+
:object => 'customer',
|
434
|
+
:id => 'customer_id'
|
435
|
+
}
|
436
|
+
})
|
437
|
+
|
438
|
+
@mock.expects(:post).once.with("#{Payjp.api_base}/v1/charges/charge_id", nil, '').returns(test_response({ "id" => "charge_id" }))
|
439
|
+
|
440
|
+
ch.customer.description = 'Bob'
|
441
|
+
ch.save
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|