affirm 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []