affirm 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 689c049744aaff6ed260bd43c0bcdf0219e6689a
4
+ data.tar.gz: 90142f082b195d4b12b593ba8dbe8f0a9d0a01b1
5
+ SHA512:
6
+ metadata.gz: 6b0c3a679864712c47a0053eff6135b0fc2ba4c6a24cb49f9d66b019c95b1ca3b724f2eff90d0a4a8e07f46a5654c8667b44a9e87edbb3d817995b25eca1369b
7
+ data.tar.gz: 0836887d3303dbb09d741b13e8894b5f6ec7c987c8218cc1a744e053b4b99a86fb440a14ecb93ba9aabb80051fab200833c211ff7d0e3071d54c35e6c84110a4
data/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # affirm-ruby
2
+ Ruby client library for integrating with Affirm financing (https://www.affirm.com/)
3
+
4
+ Requires Ruby 2.1 or greater.
5
+
6
+ [![Build Status](https://travis-ci.org/reverbdotcom/affirm-ruby.png?branch=master)](https://travis-ci.org/reverbdotcom/affirm-ruby)
7
+
8
+ ## Install
9
+ Add to your gemfile:
10
+ ```ruby
11
+ gem 'affirm-ruby'
12
+ ```
13
+ and `bundle install`.
14
+
15
+ *Note*: This gem is not yet registered with Rubygems. In the meantime, you can use the 'git' option in your Gemfile.
16
+
17
+ ## Initialize
18
+ Initialize the client with your credentials (if you're using rails, this goes in `config/initializers`).
19
+ ```ruby
20
+ Affirm::API.public_key = "xxx"
21
+ Affirm::API.secret_key = "xxx"
22
+ Affirm::API.api_url = "https://sandbox.affirm.com/api/v2/"
23
+ ```
24
+
25
+ ## Charges
26
+ The charges resource can be accessed through the api class:
27
+ ```ruby
28
+ Affirm::API.charges
29
+ ```
30
+
31
+ ### Authorizing a charge
32
+ ```ruby
33
+ Affirm::API.charges.authorize(checkout_token: "token")
34
+ Affirm::API.charges.authorize!(checkout_token: "token") # raises Affirm::Error on failure
35
+ ```
36
+
37
+ ### Reading a charge
38
+ ```ruby
39
+ Affirm::API.charges.get(charge_id: "abcd")
40
+ ```
41
+
42
+ ### Capturing a charge
43
+ Optionally takes an `order_id`, `shipping_carrier`, and `shipping_confirmation`.
44
+ ```ruby
45
+ Affirm::API.charges.capture(charge_id: "abcd")
46
+ Affirm::API.charges.capture(charge_id: "abcd", order_id: "1234", shipping_carrier: "USPS", shipping_confirmation: "ABCD1234")
47
+ Affirm::API.charges.capture!(charge_id: "abcd") # raises Affirm::Error on failure
48
+ ```
49
+
50
+ ### Voiding a charge
51
+ ```ruby
52
+ Affirm::API.charges.void(charge_id: "abcd")
53
+ Affirm::API.charges.void!(charge_id: "abcd") # raises Affirm::Error on failure
54
+ ```
55
+
56
+ ### Refunding a charge
57
+ Optionally takes an `amount` to refund (in cents).
58
+ ```ruby
59
+ Affirm::API.charges.refund(charge_id: "abcd")
60
+ Affirm::API.charges.refund(charge_id: "abcd", amount: 5000)
61
+ Affirm::API.charges.refund!(charge_id: "abcd") # raises Affirm::Error on failure
62
+ ```
63
+
64
+ ### Updating tracking fields
65
+ Optionally takes an `order_id`, `shipping_carrier`, and `shipping_confirmation`.
66
+ ```ruby
67
+ Affirm::API.charges.update(charge_id: "abcd", order_id: "1234", shipping_carrier: "USPS", shipping_confirmation: "ABCD1234")
68
+ Affirm::API.charges.update!(charge_id: "abcd", order_id: "1234") # raises Affirm::Error on failure
69
+ ```
70
+
71
+ ## Responses
72
+ On successful api calls, the response json is
73
+ ```ruby
74
+ response = Affirm::API.charges.get(charge_id: "abcd")
75
+
76
+ response.success? # => true
77
+ response.error? # => false
78
+
79
+ response.status_code # => 200
80
+
81
+ response.body # => hash of values
82
+ response.body["id"] # eg "abcd"
83
+ response.body["details"]["shipping_amount"] # eg 400
84
+ ```
85
+
86
+ ### Error responses
87
+ Unsuccessful responses have a few methods built in:
88
+ ```ruby
89
+ response.code # eg "auth-declined"
90
+ response.type # eg "invalid_request"
91
+ response.message # eg "Invalid phone number format"
92
+ response.field # eg "shipping_address.phone"
93
+ ```
94
+ See https://docs.affirm.com/v2/api/errors/#error-object.
95
+
96
+ ### Exceptions
97
+ Exceptions are raised for 5xx, 404 and 401 responses, yielding an `Affirm::ServerError`,
98
+ `Affirm::ResourceNotFoundError` and `Affirm::AuthenticationError`, respectively. These are subclassed from
99
+ `Affirm::Error`.
100
+ ```ruby
101
+ begin
102
+ Affirm::API.charges.authorize(checkout_token: "token")
103
+ rescue Affirm::ServerError => e
104
+ Logger.info e.code
105
+ Logger.info e.message
106
+ end
107
+ ```
108
+
109
+ ## Running specs
110
+ After bundling, run `bundle exec rspec` to run the tests.
111
+
112
+ ## License
113
+ Copyright 2015 Reverb.com, LLC
114
+
115
+ Licensed under the Apache License, Version 2.0 (the "License");
116
+ you may not use this file except in compliance with the License.
117
+ You may obtain a copy of the License at
118
+
119
+ http://www.apache.org/licenses/LICENSE-2.0
120
+
121
+ Unless required by applicable law or agreed to in writing, software
122
+ distributed under the License is distributed on an "AS IS" BASIS,
123
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124
+ See the License for the specific language governing permissions and
125
+ limitations under the License.
data/affirm.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "affirm"
3
+ s.summary = "Affirm Ruby Client Library"
4
+ s.description = "Ruby client library for integrating with Affirm financing payments"
5
+ s.version = "0.0.1"
6
+ s.license = "Apache License Version 2.0"
7
+ s.author = "Reverb.com"
8
+ s.email = "dev@reverb.com"
9
+ s.has_rdoc = false
10
+ s.files = Dir.glob ["README.md", "lib/**/*.{rb}", "spec/**/*", "*.gemspec"]
11
+
12
+ s.add_dependency "typhoeus"
13
+
14
+ s.add_development_dependency "bundler"
15
+ s.add_development_dependency "rake"
16
+ s.add_development_dependency "rspec", "3.2.0"
17
+ s.add_development_dependency "webmock", "1.21.0"
18
+ end
data/lib/affirm/api.rb ADDED
@@ -0,0 +1,20 @@
1
+ module Affirm
2
+ class API
3
+ class << self
4
+ attr_accessor :public_key, :secret_key, :api_url
5
+ @@client = nil
6
+
7
+ def charges
8
+ @@charges ||= Affirm::Charges.new(client)
9
+ end
10
+
11
+ def client
12
+ @@client ||= Affirm::Client.new(
13
+ public_key: public_key,
14
+ secret_key: secret_key,
15
+ api_url: api_url
16
+ )
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,103 @@
1
+ module Affirm
2
+ class Charges
3
+ def initialize(client)
4
+ @client = client
5
+ @namespace = "charges"
6
+ end
7
+
8
+ ######
9
+ # GET
10
+ #
11
+ def get(charge_id:)
12
+ make_request(charge_id, :get)
13
+ end
14
+
15
+ ######
16
+ # AUTHORIZE
17
+ #
18
+ # checkout_token - (required) string. The charge token passed through the confirmation response.
19
+ def authorize(checkout_token:)
20
+ make_request("/", :post, checkout_token: checkout_token)
21
+ end
22
+
23
+ def authorize!(checkout_token:)
24
+ response = authorize(checkout_token: checkout_token)
25
+ assert_success(response)
26
+ end
27
+
28
+ ######
29
+ # CAPTURE
30
+ #
31
+ # order_id - (optional) string. Your internal order id. This is stored for your own future reference.
32
+ # shipping_carrier - (optional) string. The shipping carrier used to ship the items in the charge.
33
+ # shipping_confirmation - (optional) string. The shipping confirmation for the shipment.
34
+ def capture(charge_id:, order_id: nil, shipping_carrier: nil, shipping_confirmation: nil)
35
+ make_request("#{charge_id}/capture", :post, {
36
+ order_id: order_id,
37
+ shipping_carrier: shipping_carrier,
38
+ shipping_confirmation: shipping_confirmation
39
+ })
40
+ end
41
+
42
+ def capture!(charge_id:, order_id: nil, shipping_carrier: nil, shipping_confirmation: nil)
43
+ response = capture(charge_id: charge_id, order_id: order_id, shipping_carrier: shipping_carrier, shipping_confirmation: shipping_confirmation)
44
+ assert_success(response)
45
+ end
46
+
47
+ ######
48
+ # VOID
49
+ #
50
+ def void(charge_id:)
51
+ make_request("#{charge_id}/void", :post)
52
+ end
53
+
54
+ def void!(charge_id:)
55
+ response = void(charge_id: charge_id)
56
+ assert_success(response)
57
+ end
58
+
59
+ ######
60
+ # REFUND
61
+ #
62
+ # amount - (optional) integer or null. The amount to refund in cents. The default amount is the remaining balance on the charge.
63
+ def refund(charge_id:, amount: nil)
64
+ make_request("#{charge_id}/refund", :post, amount: amount)
65
+ end
66
+
67
+ def refund!(charge_id:, amount: nil)
68
+ response = refund(charge_id: charge_id, amount: amount)
69
+ assert_success(response)
70
+ end
71
+
72
+ ######
73
+ # UPDATE
74
+ #
75
+ # order_id - (optional) string. Your internal order id. This is stored for your own future reference.
76
+ # shipping_carrier - (optional) string. The shipping carrier used to ship the items in the charge.
77
+ # shipping_confirmation - (optional) string. The shipping confirmation for the shipment.
78
+ def update(charge_id:, order_id: nil, shipping_carrier: nil, shipping_confirmation: nil)
79
+ make_request("#{charge_id}/update", :post, {
80
+ order_id: order_id,
81
+ shipping_carrier: shipping_carrier,
82
+ shipping_confirmation: shipping_confirmation
83
+ })
84
+ end
85
+
86
+ def update!(charge_id:, order_id: nil, shipping_carrier: nil, shipping_confirmation: nil)
87
+ response = update(charge_id: charge_id, order_id: order_id, shipping_carrier: shipping_carrier, shipping_confirmation: shipping_confirmation)
88
+ assert_success(response)
89
+ end
90
+
91
+ private
92
+
93
+ def make_request(path, method, data={})
94
+ @client.make_request(File.join(@namespace, path), method, data)
95
+ end
96
+
97
+ def assert_success(response)
98
+ raise Affirm::Error.from_response(response) if response.error?
99
+
100
+ response
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,69 @@
1
+ module Affirm
2
+ class Client
3
+ def initialize(public_key:, secret_key:, api_url: Affirm::API.api_url)
4
+ @public_key = public_key
5
+ @secret_key = secret_key
6
+ @api_url = api_url
7
+ end
8
+
9
+ def post(path, data={})
10
+ make_request(path, :post, data)
11
+ end
12
+
13
+ def get(path, data={})
14
+ make_request(path, :get, data)
15
+ end
16
+
17
+ def make_request(path, method, data={})
18
+ response = Typhoeus::Request.new(
19
+ url(path),
20
+ method: method,
21
+ body: data.to_json,
22
+ headers: affirm_headers(data),
23
+ userpwd: user_password
24
+ ).run
25
+
26
+ affirm_response = parse_response(response)
27
+
28
+ handle_errors(affirm_response)
29
+ end
30
+
31
+ private
32
+
33
+ def parse_response(response)
34
+ Affirm::Response.new(
35
+ success: response.success?,
36
+ status_code: response.code,
37
+ body: response.body
38
+ )
39
+ end
40
+
41
+ def handle_errors(affirm_response)
42
+ if affirm_response.status_code == 401
43
+ raise_error(Affirm::AuthenticationError, affirm_response)
44
+ elsif affirm_response.status_code == 404
45
+ raise_error(Affirm::ResourceNotFoundError, affirm_response)
46
+ elsif affirm_response.status_code >= 500
47
+ raise_error(Affirm::ServerError, affirm_response)
48
+ end
49
+
50
+ affirm_response
51
+ end
52
+
53
+ def raise_error(error_class, affirm_response)
54
+ raise error_class.from_response(affirm_response)
55
+ end
56
+
57
+ def affirm_headers(data)
58
+ { "Content-Type" => "application/json" } if data.length > 0
59
+ end
60
+
61
+ def user_password
62
+ "#{@public_key}:#{@secret_key}"
63
+ end
64
+
65
+ def url(path)
66
+ File.join(@api_url, path)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module Affirm
2
+ class AuthenticationError < Error; end
3
+ end
@@ -0,0 +1,23 @@
1
+ module Affirm
2
+ class Error < RuntimeError
3
+ attr_reader :status_code, :code, :message
4
+
5
+ def self.from_response(response)
6
+ new(
7
+ status_code: response.status_code,
8
+ code: response.code,
9
+ message: response.message
10
+ )
11
+ end
12
+
13
+ def initialize(status_code:, code:, message:)
14
+ @status_code = status_code
15
+ @code = code
16
+ @message = message
17
+ end
18
+
19
+ def to_s
20
+ "#{status_code} #{code}: #{message}"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module Affirm
2
+ class ResourceNotFoundError < Error; end
3
+ end
@@ -0,0 +1,3 @@
1
+ module Affirm
2
+ class ServerError < Error; end
3
+ end
@@ -0,0 +1,39 @@
1
+ module Affirm
2
+ class Response
3
+ attr_reader :status_code
4
+
5
+ def initialize(success:, status_code:, body:)
6
+ @success = success
7
+ @status_code = status_code.to_i
8
+ @body = body
9
+ end
10
+
11
+ def success?
12
+ @success
13
+ end
14
+
15
+ def error?
16
+ !success?
17
+ end
18
+
19
+ def body
20
+ JSON.parse(@body)
21
+ end
22
+
23
+ def type
24
+ body["type"]
25
+ end
26
+
27
+ def code
28
+ body["code"]
29
+ end
30
+
31
+ def message
32
+ body["message"]
33
+ end
34
+
35
+ def field
36
+ body["field"]
37
+ end
38
+ end
39
+ end
data/lib/affirm.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'json'
2
+
3
+ require 'affirm/api'
4
+ require 'affirm/client'
5
+ require 'affirm/response'
6
+ require 'affirm/charges'
7
+
8
+ require 'affirm/errors/error'
9
+ require 'affirm/errors/authentication_error'
10
+ require 'affirm/errors/resource_not_found_error'
11
+ require 'affirm/errors/server_error'
@@ -0,0 +1,170 @@
1
+ require 'spec_helper'
2
+
3
+ describe Affirm::Charges do
4
+ let!(:request) do
5
+ stub_request(request_method, request_url).to_return(status: response_code, body: response_body)
6
+ end
7
+
8
+ let(:response_code) { 200 }
9
+
10
+ describe "self.get" do
11
+ let(:request_method) { :get }
12
+ let(:request_url) { "#{TEST_URL}/charges/ABCD" }
13
+ let(:response_body) { load_fixture("charges/get.json") }
14
+
15
+ it "is successful" do
16
+ response = Affirm::API.charges.get(charge_id: "ABCD")
17
+ response.should be_success
18
+ response.body["amount"].should == 6100
19
+ end
20
+ end
21
+
22
+ describe "self.authorize" do
23
+ let(:request_method) { :post }
24
+ let(:request_url) { "#{TEST_URL}/charges/" }
25
+ let(:response_body) { load_fixture("charges/authorize.json") }
26
+
27
+ it "is successful" do
28
+ response = Affirm::API.charges.authorize(checkout_token: "token")
29
+ response.should be_success
30
+ end
31
+
32
+ it "sends the correct body" do
33
+ Affirm::API.charges.authorize(checkout_token: "token")
34
+
35
+ expect(WebMock).to have_requested(request_method, request_url).with(body: {
36
+ checkout_token: "token"
37
+ }.to_json)
38
+ end
39
+
40
+ context "bang method" do
41
+ let(:response_code) { 422 }
42
+ let(:response_body) { load_fixture("charges/invalid_request.json") }
43
+
44
+ it "raises an error on failure" do
45
+ expect { Affirm::API.charges.authorize!(checkout_token: "token") }.to raise_error(Affirm::Error)
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "self.capture" do
51
+ let(:request_method) { :post }
52
+ let(:request_url) { "#{TEST_URL}/charges/ABCD/capture" }
53
+ let(:response_body) { load_fixture("charges/capture.json") }
54
+
55
+ it "is successful" do
56
+ response = Affirm::API.charges.capture(charge_id: "ABCD")
57
+ response.should be_success
58
+ end
59
+
60
+ it "optionally allows tracking params" do
61
+ params = { order_id: "order_id", shipping_carrier: "carrier", shipping_confirmation: "confirmation" }
62
+ Affirm::API.charges.capture({charge_id: "ABCD"}.merge(params))
63
+
64
+ expect(WebMock).to have_requested(request_method, request_url).with(body: params.to_json)
65
+ end
66
+
67
+ context "bang method" do
68
+ let(:response_code) { 422 }
69
+ let(:response_body) { load_fixture("charges/invalid_request.json") }
70
+
71
+ it "raises an error on failure" do
72
+ expect { Affirm::API.charges.capture!(charge_id: "ABCD") }.to raise_error(Affirm::Error)
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "self.void" do
78
+ let(:request_method) { :post }
79
+ let(:request_url) { "#{TEST_URL}/charges/ABCD/void" }
80
+ let(:response_body) { load_fixture("charges/void.json") }
81
+
82
+ it "is successful" do
83
+ response = Affirm::API.charges.void(charge_id: "ABCD")
84
+ response.should be_success
85
+ end
86
+
87
+ context "bang method" do
88
+ let(:response_code) { 422 }
89
+ let(:response_body) { load_fixture("charges/invalid_request.json") }
90
+
91
+ it "raises an error on failure" do
92
+ expect { Affirm::API.charges.void!(charge_id: "ABCD") }.to raise_error(Affirm::Error)
93
+ end
94
+ end
95
+ end
96
+
97
+ describe "self.refund" do
98
+ let(:request_method) { :post }
99
+ let(:request_url) { "#{TEST_URL}/charges/ABCD/refund" }
100
+ let(:response_body) { load_fixture("charges/refund.json") }
101
+
102
+ it "is successful" do
103
+ response = Affirm::API.charges.refund(charge_id: "ABCD")
104
+ response.should be_success
105
+ end
106
+
107
+ context "with amount" do
108
+ it "sends the correct body" do
109
+ Affirm::API.charges.refund(charge_id: "ABCD", amount: 100)
110
+
111
+ expect(WebMock).to have_requested(request_method, request_url).with(body: {
112
+ amount: 100
113
+ }.to_json)
114
+ end
115
+ end
116
+
117
+ context "bang method" do
118
+ let(:response_code) { 422 }
119
+ let(:response_body) { load_fixture("charges/invalid_request.json") }
120
+
121
+ it "raises an error on failure" do
122
+ expect { Affirm::API.charges.refund!(charge_id: "ABCD") }.to raise_error(Affirm::Error)
123
+ end
124
+ end
125
+ end
126
+
127
+ describe "self.update" do
128
+ let(:request_method) { :post }
129
+ let(:request_url) { "#{TEST_URL}/charges/ABCD/update" }
130
+ let(:response_body) { load_fixture("charges/update.json") }
131
+
132
+ it "is successful" do
133
+ response = Affirm::API.charges.update(charge_id: "ABCD", order_id: "order_id", shipping_carrier: "carrier", shipping_confirmation: "confirmation")
134
+ response.should be_success
135
+ end
136
+
137
+ it "sends the correct body" do
138
+ params = { order_id: "order_id", shipping_carrier: "carrier", shipping_confirmation: "confirmation" }
139
+ Affirm::API.charges.update({charge_id: "ABCD"}.merge(params))
140
+
141
+ expect(WebMock).to have_requested(request_method, request_url).with(body: params.to_json)
142
+ end
143
+
144
+ it "doesn't require the params" do
145
+ response = Affirm::API.charges.update(charge_id: "ABCD")
146
+ response.should be_success
147
+ end
148
+
149
+ context "bang method" do
150
+ let(:response_code) { 422 }
151
+ let(:response_body) { load_fixture("charges/invalid_request.json") }
152
+
153
+ it "raises an error on failure" do
154
+ expect { Affirm::API.charges.update!(charge_id: "ABCD") }.to raise_error(Affirm::Error)
155
+ end
156
+ end
157
+ end
158
+
159
+ context "with specified client" do
160
+ let(:client) { Affirm::Client.new(public_key: "other_public", secret_key: "other_secret") }
161
+
162
+ let(:request_method) { :get }
163
+ let(:response_body) { load_fixture("charges/get.json") }
164
+ let(:request_url) { /.*other_public:other_secret.*/ }
165
+
166
+ it "uses the client's creds" do
167
+ described_class.new(client).get(charge_id: "ABCD")
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+
3
+ describe Affirm::Client do
4
+ let(:client) { described_class.new(public_key: public_key, secret_key: secret_key, api_url: api_url) }
5
+ let(:public_key) { "public_key" }
6
+ let(:secret_key) { "secret_key" }
7
+ let(:api_url) { "https://testaffirm.com" }
8
+
9
+ let(:request) { double("request", run: response) }
10
+ let(:response) { double("response", success?: true, body: "", code: 200) }
11
+ let(:affirm_url) { "https://public_key:secret_key@testaffirm.com/testpath" }
12
+
13
+ describe "make_request" do
14
+ before do
15
+ Typhoeus::Request.stub(:new) { request }
16
+ end
17
+
18
+ it "makes the request to the api url" do
19
+ client.make_request("testpath", :get)
20
+
21
+ Typhoeus::Request.should have_received(:new) do |url, options|
22
+ url.should == "https://testaffirm.com/testpath"
23
+ options[:method].should == :get
24
+ end
25
+
26
+ request.should have_received(:run)
27
+ end
28
+
29
+ it "sends keys as basic auth" do
30
+ client.make_request("testpath", :get)
31
+
32
+ Typhoeus::Request.should have_received(:new) do |url, options|
33
+ options[:userpwd].should == "public_key:secret_key"
34
+ end
35
+ end
36
+
37
+ context "with params" do
38
+ let(:params) { {"foo" => "bar"} }
39
+
40
+ it "sends the params" do
41
+ client.make_request("testpath", :post, params)
42
+
43
+ Typhoeus::Request.should have_received(:new) do |url, options|
44
+ options[:method].should == :post
45
+ JSON.parse(options[:body]).should == params
46
+ end
47
+ end
48
+
49
+ it "sets content type to application/json" do
50
+ client.make_request("testpath", :post, params)
51
+
52
+ Typhoeus::Request.should have_received(:new) do |url, options|
53
+ options[:headers]["Content-Type"].should == "application/json"
54
+ end
55
+ end
56
+ end
57
+
58
+ context "authentication error" do
59
+
60
+ end
61
+ end
62
+
63
+ describe "get" do
64
+ before do
65
+ Typhoeus::Request.stub(:new) { request }
66
+ end
67
+
68
+ it "sends a get request" do
69
+ client.get("testpath")
70
+
71
+ Typhoeus::Request.should have_received(:new) do |url, options|
72
+ options[:method].should == :get
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "post" do
78
+ before do
79
+ Typhoeus::Request.stub(:new) { request }
80
+ end
81
+
82
+ it "sends a post request" do
83
+ client.post("testpath")
84
+
85
+ Typhoeus::Request.should have_received(:new) do |url, options|
86
+ options[:method].should == :post
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "response" do
92
+ before do
93
+ stub_request(:get, affirm_url).
94
+ to_return(status: 200, body: {foo: "bar"}.to_json)
95
+ end
96
+
97
+ it "parses the response" do
98
+ response = client.get("testpath")
99
+
100
+ response.should be_a(Affirm::Response)
101
+ response.should be_success
102
+ response.status_code.should == 200
103
+ response.body["foo"].should == "bar"
104
+ end
105
+ end
106
+
107
+ describe "errors" do
108
+ context "authentication error" do
109
+ before do
110
+ stub_request(:get, affirm_url).
111
+ to_return(status: 401, body: {status_code: 401, type: "unauthorized", code: "public-api-key-invalid"}.to_json)
112
+ end
113
+
114
+ it "raises Affirm::AuthenticationError" do
115
+ expect { client.get("testpath") }.to raise_error(Affirm::AuthenticationError)
116
+ end
117
+ end
118
+
119
+ context "server error" do
120
+ before do
121
+ stub_request(:get, affirm_url).
122
+ to_return(status: 500, body: {status_code: 500}.to_json)
123
+ end
124
+
125
+ it "raises Affirm::ServerError" do
126
+ expect { client.get("testpath") }.to raise_error(Affirm::ServerError)
127
+ end
128
+ end
129
+
130
+ context "resource not found" do
131
+ before do
132
+ stub_request(:get, affirm_url).
133
+ to_return(status: 404, body: {status_code: 404}.to_json)
134
+ end
135
+
136
+ it "raises Affirm::ResourceNotFoundError" do
137
+ expect { client.get("testpath") }.to raise_error(Affirm::ResourceNotFoundError)
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,45 @@
1
+ {
2
+ "id":"TEST-ALO4-UVGR",
3
+ "created":"2014-03-18T19:19:04Z",
4
+ "currency":"USD",
5
+ "amount":6100,
6
+ "auth_hold":6100,
7
+ "payable":0,
8
+ "void":false,
9
+ "events":[
10
+ {
11
+ "created":"2014-03-20T14:00:33Z",
12
+ "currency":"USD",
13
+ "id":"UI1ZOXSXQ44QUXQL",
14
+ "transaction_id":"TpR3Xrx8TkvuGio0",
15
+ "type":"auth"
16
+ }
17
+ ],
18
+ "details":{
19
+ "items":{
20
+ "sweater-a92123":{
21
+ "sku":"sweater-a92123",
22
+ "display_name":"Sweater",
23
+ "qty":1,
24
+ "item_type":"physical",
25
+ "item_image_url":"http://placehold.it/350x150",
26
+ "item_url":"http://placehold.it/350x150",
27
+ "unit_price":5000
28
+ }
29
+ },
30
+ "shipping_amount":400,
31
+ "tax_amount":700,
32
+ "shipping":{
33
+ "name":{
34
+ "full":"John Doe"
35
+ },
36
+ "address":{
37
+ "line1":"325 Pacific Ave",
38
+ "city":"San Francisco",
39
+ "state":"CA",
40
+ "zipcode":"94112",
41
+ "country":"USA"
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "type": "capture",
3
+ "created": "2014-03-18T19:19:04Z",
4
+ "amount": 500,
5
+ "fee": 15,
6
+ "id": "JAPP6MISWXVA4OQ5",
7
+ "transaction_id": "9qkLbxrtEJG0ImEQ"
8
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "id":"TEST-ALO4-UVGR",
3
+ "created":"2014-03-18T19:19:04Z",
4
+ "currency":"USD",
5
+ "amount":6100,
6
+ "auth_hold":6100,
7
+ "payable":0,
8
+ "void":false,
9
+ "events":[
10
+ {
11
+ "created":"2014-03-20T14:00:33Z",
12
+ "currency":"USD",
13
+ "id":"UI1ZOXSXQ44QUXQL",
14
+ "transaction_id":"TpR3Xrx8TkvuGio0",
15
+ "type":"auth"
16
+ }
17
+ ],
18
+ "details":{
19
+ "items":{
20
+ "sweater-a92123":{
21
+ "sku":"sweater-a92123",
22
+ "display_name":"Sweater",
23
+ "qty":1,
24
+ "item_type":"physical",
25
+ "item_image_url":"http://placehold.it/350x150",
26
+ "item_url":"http://placehold.it/350x150",
27
+ "unit_price":5000
28
+ }
29
+ },
30
+ "shipping_amount":400,
31
+ "tax_amount":700,
32
+ "shipping":{
33
+ "name":{
34
+ "full":"John Doe"
35
+ },
36
+ "address":{
37
+ "line1":"325 Pacific Ave",
38
+ "city":"San Francisco",
39
+ "state":"CA",
40
+ "zipcode":"94112",
41
+ "country":"USA"
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "status_code": 422,
3
+ "type": "invalid_request",
4
+ "code": "test-code"
5
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "created": "2014-03-18T19:20:30Z",
3
+ "fee_refunded": 15,
4
+ "amount": 500,
5
+ "type": "refund",
6
+ "id": "OWA49MWUCA29SBVQ",
7
+ "transaction_id": "r86zdkHONPcaiVJJ"
8
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "created": "2014-10-08T23:48:04Z",
3
+ "id": "DCPO5PP6QA9V7T60",
4
+ "order_id": "CUSTOM_ORDER_ID",
5
+ "shipping_carrier": "USPS",
6
+ "shipping_confirmation": "1Z23223"
7
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "type": "void",
3
+ "id": "N5E9OXSIDJ8TKZZ9",
4
+ "transaction_id": "G9TqohxBlRPWTGB2",
5
+ "created": "2014-03-17T22:52:16Z"
6
+ }
@@ -0,0 +1,38 @@
1
+ require 'webmock'
2
+ require 'webmock/rspec'
3
+ require 'affirm'
4
+
5
+ include WebMock::API
6
+
7
+ TEST_URL = "https://public_key:secret_key@test.affirm.com"
8
+
9
+ RSpec.configure do |config|
10
+ config.before(:suite) do
11
+ Affirm::API.public_key = "public_key"
12
+ Affirm::API.secret_key = "secret_key"
13
+ Affirm::API.api_url = "https://test.affirm.com"
14
+ end
15
+
16
+ config.expect_with :rspec do |expectations|
17
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
18
+ expectations.syntax = [:should, :expect]
19
+ end
20
+
21
+ config.mock_with :rspec do |mocks|
22
+ mocks.verify_partial_doubles = true
23
+ mocks.syntax = [:should, :expect]
24
+ end
25
+
26
+ config.filter_run :focus
27
+ config.run_all_when_everything_filtered = true
28
+
29
+ if config.files_to_run.one?
30
+ config.default_formatter = 'doc'
31
+ end
32
+
33
+ config.order = :random
34
+ end
35
+
36
+ def load_fixture(path)
37
+ File.read(File.join(__dir__, "fixtures", path))
38
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: affirm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Reverb.com
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: typhoeus
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 3.2.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 3.2.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '='
74
+ - !ruby/object:Gem::Version
75
+ version: 1.21.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 1.21.0
83
+ description: Ruby client library for integrating with Affirm financing payments
84
+ email: dev@reverb.com
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - README.md
90
+ - affirm.gemspec
91
+ - lib/affirm.rb
92
+ - lib/affirm/api.rb
93
+ - lib/affirm/charges.rb
94
+ - lib/affirm/client.rb
95
+ - lib/affirm/errors/authentication_error.rb
96
+ - lib/affirm/errors/error.rb
97
+ - lib/affirm/errors/resource_not_found_error.rb
98
+ - lib/affirm/errors/server_error.rb
99
+ - lib/affirm/response.rb
100
+ - spec/charges_spec.rb
101
+ - spec/client_spec.rb
102
+ - spec/fixtures/charges/authorize.json
103
+ - spec/fixtures/charges/capture.json
104
+ - spec/fixtures/charges/get.json
105
+ - spec/fixtures/charges/invalid_request.json
106
+ - spec/fixtures/charges/refund.json
107
+ - spec/fixtures/charges/update.json
108
+ - spec/fixtures/charges/void.json
109
+ - spec/spec_helper.rb
110
+ homepage:
111
+ licenses:
112
+ - Apache License Version 2.0
113
+ metadata: {}
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubyforge_project:
130
+ rubygems_version: 2.2.2
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: Affirm Ruby Client Library
134
+ test_files: []