affirm 0.0.1 → 1.0.0
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 +4 -4
- data/README.md +53 -51
- data/affirm.gemspec +1 -1
- data/lib/affirm/api.rb +0 -4
- data/lib/affirm/charge.rb +115 -0
- data/lib/affirm/charge_event.rb +20 -0
- data/lib/affirm/client.rb +9 -0
- data/lib/affirm/errors/error.rb +6 -2
- data/lib/affirm/response.rb +5 -3
- data/lib/affirm.rb +2 -1
- data/spec/charge_spec.rb +242 -0
- data/spec/client_spec.rb +11 -4
- data/spec/fixtures/charges/{authorize.json → create.json} +1 -1
- data/spec/fixtures/charges/{get.json → retrieve.json} +1 -1
- data/spec/fixtures/charges/update.json +1 -0
- metadata +7 -6
- data/lib/affirm/charges.rb +0 -103
- data/spec/charges_spec.rb +0 -170
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39c1e3c767578030a0b0d4040f1369a0984537dc
|
4
|
+
data.tar.gz: 41fe7e6a858817d7bb521c68dcc3238457e347e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d00eda5029a694f08f75cbd2512dbf6acd9ae10cd2a4de39e41b10c004d454623142aa791bb74ce4481ce39689a3d4790cbf4d5213739794c041a69e77060f23
|
7
|
+
data.tar.gz: 12f0057579aba7917cb39f3f5e5d12d38662b6c8639090652e2215c40be8c8278ed1dba5157adf93b118a9f5dd232f428f802eacdb8e466681eedd8c14469e38
|
data/README.md
CHANGED
@@ -7,15 +7,16 @@ Requires Ruby 2.1 or greater.
|
|
7
7
|
|
8
8
|
## Install
|
9
9
|
Add to your gemfile:
|
10
|
+
|
10
11
|
```ruby
|
11
|
-
gem 'affirm
|
12
|
+
gem 'affirm'
|
12
13
|
```
|
13
|
-
and `bundle install`.
|
14
14
|
|
15
|
-
|
15
|
+
and `bundle install`.
|
16
16
|
|
17
17
|
## Initialize
|
18
18
|
Initialize the client with your credentials (if you're using rails, this goes in `config/initializers`).
|
19
|
+
|
19
20
|
```ruby
|
20
21
|
Affirm::API.public_key = "xxx"
|
21
22
|
Affirm::API.secret_key = "xxx"
|
@@ -23,86 +24,87 @@ Affirm::API.api_url = "https://sandbox.affirm.com/api/v2/"
|
|
23
24
|
```
|
24
25
|
|
25
26
|
## Charges
|
26
|
-
The charges resource can be accessed through the api class:
|
27
|
-
```ruby
|
28
|
-
Affirm::API.charges
|
29
|
-
```
|
30
27
|
|
31
|
-
|
28
|
+
All API requests raise an `Affirm::Error` or subclass thereof on failure.
|
29
|
+
|
30
|
+
### Creating/authorizing a charge
|
31
|
+
|
32
32
|
```ruby
|
33
|
-
Affirm::
|
34
|
-
Affirm::API.charges.authorize!(checkout_token: "token") # raises Affirm::Error on failure
|
33
|
+
charge = Affirm::Charge.create("checkout_token")
|
35
34
|
```
|
36
35
|
|
37
|
-
###
|
36
|
+
### Retrieving a charge
|
37
|
+
|
38
38
|
```ruby
|
39
|
-
Affirm::
|
39
|
+
charge = Affirm::Charge.retrieve("ABCD-ABCD")
|
40
|
+
|
41
|
+
charge.id
|
42
|
+
charge.amount
|
43
|
+
charge.created
|
44
|
+
charge.currency
|
45
|
+
charge.auth_hold
|
46
|
+
charge.payable
|
47
|
+
charge.void?
|
48
|
+
charge.order_id
|
49
|
+
charge.events # array of Affirm::ChargeEvent
|
50
|
+
charge.details # hash of order details
|
40
51
|
```
|
41
52
|
|
53
|
+
Raises an `Affirm::ResourceNotFoundError` if the charge doesn't exist.
|
54
|
+
|
55
|
+
See https://docs.affirm.com/v2/api/charges/#charge-object for more info on the charge object
|
56
|
+
|
42
57
|
### Capturing a charge
|
43
58
|
Optionally takes an `order_id`, `shipping_carrier`, and `shipping_confirmation`.
|
59
|
+
|
44
60
|
```ruby
|
45
|
-
|
46
|
-
|
47
|
-
Affirm::API.charges.capture!(charge_id: "abcd") # raises Affirm::Error on failure
|
61
|
+
charge.capture
|
62
|
+
charge.capture(order_id: "1234", shipping_carrier: "USPS", shipping_confirmation: "ABCD1234")
|
48
63
|
```
|
49
64
|
|
65
|
+
Returns an `Affirm::ChargeEvent` object of type `capture`.
|
66
|
+
|
50
67
|
### Voiding a charge
|
68
|
+
|
51
69
|
```ruby
|
52
|
-
|
53
|
-
Affirm::API.charges.void!(charge_id: "abcd") # raises Affirm::Error on failure
|
70
|
+
charge.void
|
54
71
|
```
|
55
72
|
|
73
|
+
Returns an `Affirm::ChargeEvent` object of type `void`.
|
74
|
+
|
56
75
|
### Refunding a charge
|
57
76
|
Optionally takes an `amount` to refund (in cents).
|
77
|
+
|
58
78
|
```ruby
|
59
|
-
|
60
|
-
|
61
|
-
Affirm::API.charges.refund!(charge_id: "abcd") # raises Affirm::Error on failure
|
79
|
+
charge.refund
|
80
|
+
charge.refund(amount: 1000)
|
62
81
|
```
|
63
82
|
|
83
|
+
Returns an `Affirm::ChargeEvent` object of type `refund`.
|
84
|
+
|
64
85
|
### Updating tracking fields
|
65
86
|
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
87
|
|
71
|
-
## Responses
|
72
|
-
On successful api calls, the response json is
|
73
88
|
```ruby
|
74
|
-
|
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
|
89
|
+
charge.update(order_id: "1234", shipping_carrier: "USPS", shipping_confirmation: "ABCD1234")
|
84
90
|
```
|
85
91
|
|
86
|
-
|
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.
|
92
|
+
Returns an `Affirm::ChargeEvent` object of type `update`.
|
95
93
|
|
96
|
-
|
97
|
-
|
94
|
+
## Exceptions
|
95
|
+
Special exceptions are raised for 5xx, 404 and 401 responses, yielding an `Affirm::ServerError`,
|
98
96
|
`Affirm::ResourceNotFoundError` and `Affirm::AuthenticationError`, respectively. These are subclassed from
|
99
97
|
`Affirm::Error`.
|
98
|
+
|
99
|
+
All exceptions have the following methods on them:
|
100
|
+
|
100
101
|
```ruby
|
101
102
|
begin
|
102
|
-
Affirm::
|
103
|
-
rescue Affirm::
|
104
|
-
Logger.info e.
|
105
|
-
Logger.info e.
|
103
|
+
Affirm::Charge.create("checkout_token")
|
104
|
+
rescue Affirm::Error => e
|
105
|
+
Logger.info e.http_code # eg 422
|
106
|
+
Logger.info e.code # eg "auth-declined"
|
107
|
+
Logger.info e.message # eg "Invalid phone number format"
|
106
108
|
end
|
107
109
|
```
|
108
110
|
|
data/affirm.gemspec
CHANGED
@@ -2,7 +2,7 @@ Gem::Specification.new do |s|
|
|
2
2
|
s.name = "affirm"
|
3
3
|
s.summary = "Affirm Ruby Client Library"
|
4
4
|
s.description = "Ruby client library for integrating with Affirm financing payments"
|
5
|
-
s.version = "0.0
|
5
|
+
s.version = "1.0.0"
|
6
6
|
s.license = "Apache License Version 2.0"
|
7
7
|
s.author = "Reverb.com"
|
8
8
|
s.email = "dev@reverb.com"
|
data/lib/affirm/api.rb
CHANGED
@@ -0,0 +1,115 @@
|
|
1
|
+
module Affirm
|
2
|
+
class Charge
|
3
|
+
attr_reader :id, :amount, :created, :currency, :auth_hold, :payable, :order_id, :events, :details
|
4
|
+
|
5
|
+
##
|
6
|
+
# RETRIEVE
|
7
|
+
#
|
8
|
+
# id - (required) string. The charge id, in format 'XXXX-XXXX'
|
9
|
+
def self.retrieve(id, client: Affirm::API.client)
|
10
|
+
new(attrs: {"id" => id}, client: client).refresh
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# CREATE / AUTHORIZE
|
15
|
+
#
|
16
|
+
# checkout_token - (required) string. The charge token passed through the confirmation response.
|
17
|
+
def self.create(checkout_token, client: Affirm::API.client)
|
18
|
+
response = client.make_request!("/charges", :post, checkout_token: checkout_token)
|
19
|
+
|
20
|
+
new(attrs: response.body, client: client)
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# CAPTURE
|
25
|
+
#
|
26
|
+
# order_id - (optional) string. Your internal order id. This is stored for your own future reference.
|
27
|
+
# shipping_carrier - (optional) string. The shipping carrier used to ship the items in the charge.
|
28
|
+
# shipping_confirmation - (optional) string. The shipping confirmation for the shipment.
|
29
|
+
def capture(order_id: nil, shipping_carrier: nil, shipping_confirmation: nil)
|
30
|
+
api_request("/charges/#{id}/capture", :post, {
|
31
|
+
order_id: order_id,
|
32
|
+
shipping_carrier: shipping_carrier,
|
33
|
+
shipping_confirmation: shipping_confirmation
|
34
|
+
})
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# VOID
|
39
|
+
#
|
40
|
+
def void
|
41
|
+
api_request("/charges/#{id}/void", :post)
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# REFUND
|
46
|
+
#
|
47
|
+
# amount - (optional) integer or null. The amount to refund in cents. The default amount is the remaining balance on the charge.
|
48
|
+
def refund(amount: nil)
|
49
|
+
api_request("/charges/#{id}/refund", :post, {
|
50
|
+
amount: amount
|
51
|
+
})
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# UPDATE
|
56
|
+
#
|
57
|
+
# order_id - (optional) string. Your internal order id. This is stored for your own future reference.
|
58
|
+
# shipping_carrier - (optional) string. The shipping carrier used to ship the items in the charge.
|
59
|
+
# shipping_confirmation - (optional) string. The shipping confirmation for the shipment.
|
60
|
+
def update(order_id: nil, shipping_carrier: nil, shipping_confirmation: nil)
|
61
|
+
api_request("/charges/#{id}/update", :post, {
|
62
|
+
order_id: order_id,
|
63
|
+
shipping_carrier: shipping_carrier,
|
64
|
+
shipping_confirmation: shipping_confirmation
|
65
|
+
})
|
66
|
+
end
|
67
|
+
|
68
|
+
def initialize(attrs: {}, client: Affirm::API.client)
|
69
|
+
@client = client
|
70
|
+
set_attrs(attrs)
|
71
|
+
end
|
72
|
+
|
73
|
+
def refresh
|
74
|
+
response = @client.make_request("/charges/#{id}", :get)
|
75
|
+
|
76
|
+
set_attrs(response.body)
|
77
|
+
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def void?
|
82
|
+
@void
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def set_attrs(attrs)
|
88
|
+
@id = attrs["id"]
|
89
|
+
@amount = attrs["amount"]
|
90
|
+
@created = attrs["created"]
|
91
|
+
@currency = attrs["currency"]
|
92
|
+
@auth_hold = attrs["auth_hold"]
|
93
|
+
@payable = attrs["payable"]
|
94
|
+
@void = attrs["void"]
|
95
|
+
@order_id = attrs["order_id"]
|
96
|
+
@details = attrs["details"]
|
97
|
+
@events = parse_events(attrs["events"])
|
98
|
+
end
|
99
|
+
|
100
|
+
def parse_events(events_attrs)
|
101
|
+
if events_attrs
|
102
|
+
events_attrs.map { |event| ChargeEvent.new(event) }
|
103
|
+
else
|
104
|
+
[]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def api_request(url, method, params={})
|
109
|
+
response = @client.make_request!(url, method, params)
|
110
|
+
event = ChargeEvent.new(response.body)
|
111
|
+
@events << event
|
112
|
+
event
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Affirm
|
2
|
+
class ChargeEvent
|
3
|
+
attr_reader :id, :transaction_id, :type, :created,
|
4
|
+
:amount, :fee, :order_id, :shipping_carrier, :shipping_confirmation,
|
5
|
+
:fee_refunded
|
6
|
+
|
7
|
+
def initialize(attrs)
|
8
|
+
@id = attrs["id"]
|
9
|
+
@transaction_id = attrs["transaction_id"]
|
10
|
+
@type = attrs["type"]
|
11
|
+
@created = attrs["created"]
|
12
|
+
@amount = attrs["amount"]
|
13
|
+
@fee = attrs["fee"]
|
14
|
+
@order_id = attrs["order_id"]
|
15
|
+
@shipping_carrier = attrs["shipping_carrier"]
|
16
|
+
@shipping_confirmation = attrs["shipping_confirmation"]
|
17
|
+
@fee_refunded = attrs["fee_refunded"]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/affirm/client.rb
CHANGED
@@ -28,6 +28,15 @@ module Affirm
|
|
28
28
|
handle_errors(affirm_response)
|
29
29
|
end
|
30
30
|
|
31
|
+
# like make_request, but raise error on failure
|
32
|
+
def make_request!(path, method, data={})
|
33
|
+
response = make_request(path, method, data)
|
34
|
+
|
35
|
+
raise Affirm::Error.from_response(response) if response.error?
|
36
|
+
|
37
|
+
response
|
38
|
+
end
|
39
|
+
|
31
40
|
private
|
32
41
|
|
33
42
|
def parse_response(response)
|
data/lib/affirm/errors/error.rb
CHANGED
@@ -6,7 +6,7 @@ module Affirm
|
|
6
6
|
new(
|
7
7
|
status_code: response.status_code,
|
8
8
|
code: response.code,
|
9
|
-
message: response.message
|
9
|
+
message: response.message || response.raw_body
|
10
10
|
)
|
11
11
|
end
|
12
12
|
|
@@ -17,7 +17,11 @@ module Affirm
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def to_s
|
20
|
-
|
20
|
+
if code || message
|
21
|
+
"#{status_code} - (#{code}) #{message}"
|
22
|
+
else
|
23
|
+
status_code.to_s
|
24
|
+
end
|
21
25
|
end
|
22
26
|
end
|
23
27
|
end
|
data/lib/affirm/response.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Affirm
|
2
2
|
class Response
|
3
|
-
attr_reader :status_code
|
3
|
+
attr_reader :status_code, :raw_body
|
4
4
|
|
5
5
|
def initialize(success:, status_code:, body:)
|
6
6
|
@success = success
|
7
7
|
@status_code = status_code.to_i
|
8
|
-
@
|
8
|
+
@raw_body = body
|
9
9
|
end
|
10
10
|
|
11
11
|
def success?
|
@@ -17,7 +17,9 @@ module Affirm
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def body
|
20
|
-
JSON.parse(@
|
20
|
+
JSON.parse(@raw_body)
|
21
|
+
rescue JSON::ParserError
|
22
|
+
{}
|
21
23
|
end
|
22
24
|
|
23
25
|
def type
|
data/lib/affirm.rb
CHANGED
data/spec/charge_spec.rb
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Affirm::Charge do
|
4
|
+
let(:id) { "ABCD" }
|
5
|
+
let!(:request) do
|
6
|
+
stub_request(request_method, request_url).to_return(status: response_code, body: response_body)
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:response_code) { 200 }
|
10
|
+
|
11
|
+
let(:charge) { Affirm::Charge.new(attrs: {"id" => id}) }
|
12
|
+
|
13
|
+
describe "self.retrieve" do
|
14
|
+
let(:request_method) { :get }
|
15
|
+
let(:request_url) { "#{TEST_URL}/charges/#{id}" }
|
16
|
+
let(:response_body) { load_fixture("charges/retrieve.json") }
|
17
|
+
|
18
|
+
it "returns a charge" do
|
19
|
+
charge = Affirm::Charge.retrieve(id)
|
20
|
+
|
21
|
+
charge.should be_a Affirm::Charge
|
22
|
+
end
|
23
|
+
|
24
|
+
it "sets attributes" do
|
25
|
+
charge = Affirm::Charge.retrieve(id)
|
26
|
+
|
27
|
+
charge.id.should == id
|
28
|
+
charge.created.should == "2014-03-18T19:19:04Z"
|
29
|
+
charge.currency.should == "USD"
|
30
|
+
charge.amount.should == 6100
|
31
|
+
charge.auth_hold.should == 6100
|
32
|
+
charge.payable.should == 0
|
33
|
+
charge.void?.should == false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "parses events" do
|
37
|
+
charge = Affirm::Charge.retrieve(id)
|
38
|
+
events = charge.events
|
39
|
+
|
40
|
+
events.length.should == 1
|
41
|
+
events.first.type.should == "auth"
|
42
|
+
end
|
43
|
+
|
44
|
+
context "not found" do
|
45
|
+
let(:response_code) { 404 }
|
46
|
+
|
47
|
+
it "raises an error" do
|
48
|
+
expect { Affirm::Charge.retrieve(id) }.to raise_error(Affirm::ResourceNotFoundError)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "self.create" do
|
54
|
+
let(:request_method) { :post }
|
55
|
+
let(:request_url) { "#{TEST_URL}/charges" }
|
56
|
+
let(:response_body) { load_fixture("charges/create.json") }
|
57
|
+
|
58
|
+
it "returns a charge" do
|
59
|
+
charge = Affirm::Charge.create("token")
|
60
|
+
|
61
|
+
charge.should be_a Affirm::Charge
|
62
|
+
charge.id.should == id
|
63
|
+
end
|
64
|
+
|
65
|
+
it "sends the correct body" do
|
66
|
+
charge = Affirm::Charge.create("token")
|
67
|
+
|
68
|
+
expect(WebMock).to have_requested(request_method, request_url).with(body: {
|
69
|
+
checkout_token: "token"
|
70
|
+
}.to_json)
|
71
|
+
end
|
72
|
+
|
73
|
+
context "failed response" do
|
74
|
+
let(:response_code) { 422 }
|
75
|
+
let(:response_body) { load_fixture("charges/invalid_request.json") }
|
76
|
+
|
77
|
+
it "raises an error" do
|
78
|
+
expect { Affirm::Charge.create("token") }.to raise_error(Affirm::Error)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "capture" do
|
84
|
+
let(:request_method) { :post }
|
85
|
+
let(:request_url) { "#{TEST_URL}/charges/#{id}/capture" }
|
86
|
+
let(:response_body) { load_fixture("charges/capture.json") }
|
87
|
+
|
88
|
+
it "POSTs to the capture url" do
|
89
|
+
charge.capture
|
90
|
+
|
91
|
+
expect(WebMock).to have_requested(request_method, request_url)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "optionally allows tracking params" do
|
95
|
+
params = { order_id: "order_id", shipping_carrier: "carrier", shipping_confirmation: "confirmation" }
|
96
|
+
charge.capture(params)
|
97
|
+
|
98
|
+
expect(WebMock).to have_requested(request_method, request_url).with(body: params.to_json)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "adds the event to the charge" do
|
102
|
+
expect { charge.capture }.to change { charge.events.length }.by(1)
|
103
|
+
|
104
|
+
charge.events.last.type.should == "capture"
|
105
|
+
end
|
106
|
+
|
107
|
+
it "returns an event" do
|
108
|
+
charge.capture.should be_a Affirm::ChargeEvent
|
109
|
+
end
|
110
|
+
|
111
|
+
context "failed response" do
|
112
|
+
let(:response_code) { 422 }
|
113
|
+
let(:response_body) { load_fixture("charges/invalid_request.json") }
|
114
|
+
|
115
|
+
it "raises an error" do
|
116
|
+
expect { charge.capture }.to raise_error(Affirm::Error)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "void" do
|
122
|
+
let(:request_method) { :post }
|
123
|
+
let(:request_url) { "#{TEST_URL}/charges/#{id}/void" }
|
124
|
+
let(:response_body) { load_fixture("charges/void.json") }
|
125
|
+
|
126
|
+
it "POSTs to the void url" do
|
127
|
+
charge.void
|
128
|
+
|
129
|
+
expect(WebMock).to have_requested(request_method, request_url)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "adds the event to the charge" do
|
133
|
+
expect { charge.void }.to change { charge.events.length }.by(1)
|
134
|
+
|
135
|
+
charge.events.last.type.should == "void"
|
136
|
+
end
|
137
|
+
|
138
|
+
it "returns an event" do
|
139
|
+
charge.void.should be_a Affirm::ChargeEvent
|
140
|
+
end
|
141
|
+
|
142
|
+
context "failed response" do
|
143
|
+
let(:response_code) { 422 }
|
144
|
+
let(:response_body) { load_fixture("charges/invalid_request.json") }
|
145
|
+
|
146
|
+
it "raises an error" do
|
147
|
+
expect { charge.void }.to raise_error(Affirm::Error)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "refund" do
|
153
|
+
let(:request_method) { :post }
|
154
|
+
let(:request_url) { "#{TEST_URL}/charges/#{id}/refund" }
|
155
|
+
let(:response_body) { load_fixture("charges/refund.json") }
|
156
|
+
|
157
|
+
it "POSTs to the refund url" do
|
158
|
+
charge.refund
|
159
|
+
|
160
|
+
expect(WebMock).to have_requested(request_method, request_url)
|
161
|
+
end
|
162
|
+
|
163
|
+
it "adds the event to the charge" do
|
164
|
+
expect { charge.refund }.to change { charge.events.length }.by(1)
|
165
|
+
|
166
|
+
charge.events.last.type.should == "refund"
|
167
|
+
end
|
168
|
+
|
169
|
+
it "returns an event" do
|
170
|
+
charge.refund.should be_a Affirm::ChargeEvent
|
171
|
+
end
|
172
|
+
|
173
|
+
context "with amount" do
|
174
|
+
it "sends the amount" do
|
175
|
+
charge.refund(amount: 100)
|
176
|
+
|
177
|
+
expect(WebMock).to have_requested(request_method, request_url).with(body: {
|
178
|
+
amount: 100
|
179
|
+
}.to_json)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context "failed response" do
|
184
|
+
let(:response_code) { 422 }
|
185
|
+
let(:response_body) { load_fixture("charges/invalid_request.json") }
|
186
|
+
|
187
|
+
it "raises an error" do
|
188
|
+
expect { charge.refund }.to raise_error(Affirm::Error)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe "update" do
|
194
|
+
let(:request_method) { :post }
|
195
|
+
let(:request_url) { "#{TEST_URL}/charges/#{id}/update" }
|
196
|
+
let(:response_body) { load_fixture("charges/update.json") }
|
197
|
+
|
198
|
+
it "POSTs to the update url" do
|
199
|
+
charge.update
|
200
|
+
|
201
|
+
expect(WebMock).to have_requested(request_method, request_url)
|
202
|
+
end
|
203
|
+
|
204
|
+
it "optionally allows params" do
|
205
|
+
params = { order_id: "order_id", shipping_carrier: "carrier", shipping_confirmation: "confirmation" }
|
206
|
+
charge.update(params)
|
207
|
+
|
208
|
+
expect(WebMock).to have_requested(request_method, request_url).with(body: params.to_json)
|
209
|
+
end
|
210
|
+
|
211
|
+
it "adds the event to the charge" do
|
212
|
+
expect { charge.update }.to change { charge.events.length }.by(1)
|
213
|
+
|
214
|
+
charge.events.last.type.should == "update"
|
215
|
+
end
|
216
|
+
|
217
|
+
it "returns an event" do
|
218
|
+
charge.update.should be_a Affirm::ChargeEvent
|
219
|
+
end
|
220
|
+
|
221
|
+
context "failed response" do
|
222
|
+
let(:response_code) { 422 }
|
223
|
+
let(:response_body) { load_fixture("charges/invalid_request.json") }
|
224
|
+
|
225
|
+
it "raises an error" do
|
226
|
+
expect { charge.update }.to raise_error(Affirm::Error)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "with specified client" do
|
232
|
+
let(:client) { Affirm::Client.new(public_key: "other_public", secret_key: "other_secret") }
|
233
|
+
|
234
|
+
let(:request_method) { :get }
|
235
|
+
let(:response_body) { load_fixture("charges/retrieve.json") }
|
236
|
+
let(:request_url) { /.*other_public:other_secret.*/ }
|
237
|
+
|
238
|
+
it "uses the client's creds" do
|
239
|
+
Affirm::Charge.retrieve(id, client: client)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
data/spec/client_spec.rb
CHANGED
@@ -54,10 +54,6 @@ describe Affirm::Client do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
57
|
-
|
58
|
-
context "authentication error" do
|
59
|
-
|
60
|
-
end
|
61
57
|
end
|
62
58
|
|
63
59
|
describe "get" do
|
@@ -137,5 +133,16 @@ describe Affirm::Client do
|
|
137
133
|
expect { client.get("testpath") }.to raise_error(Affirm::ResourceNotFoundError)
|
138
134
|
end
|
139
135
|
end
|
136
|
+
|
137
|
+
context "non-json response" do
|
138
|
+
before do
|
139
|
+
stub_request(:get, affirm_url).
|
140
|
+
to_return(status: 500, body: "the server is down")
|
141
|
+
end
|
142
|
+
|
143
|
+
it "doesn't blow up" do
|
144
|
+
expect { client.get("testpath") }.to raise_error(Affirm::ServerError)
|
145
|
+
end
|
146
|
+
end
|
140
147
|
end
|
141
148
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: affirm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reverb.com
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: typhoeus
|
@@ -90,20 +90,21 @@ files:
|
|
90
90
|
- affirm.gemspec
|
91
91
|
- lib/affirm.rb
|
92
92
|
- lib/affirm/api.rb
|
93
|
-
- lib/affirm/
|
93
|
+
- lib/affirm/charge.rb
|
94
|
+
- lib/affirm/charge_event.rb
|
94
95
|
- lib/affirm/client.rb
|
95
96
|
- lib/affirm/errors/authentication_error.rb
|
96
97
|
- lib/affirm/errors/error.rb
|
97
98
|
- lib/affirm/errors/resource_not_found_error.rb
|
98
99
|
- lib/affirm/errors/server_error.rb
|
99
100
|
- lib/affirm/response.rb
|
100
|
-
- spec/
|
101
|
+
- spec/charge_spec.rb
|
101
102
|
- spec/client_spec.rb
|
102
|
-
- spec/fixtures/charges/authorize.json
|
103
103
|
- spec/fixtures/charges/capture.json
|
104
|
-
- spec/fixtures/charges/
|
104
|
+
- spec/fixtures/charges/create.json
|
105
105
|
- spec/fixtures/charges/invalid_request.json
|
106
106
|
- spec/fixtures/charges/refund.json
|
107
|
+
- spec/fixtures/charges/retrieve.json
|
107
108
|
- spec/fixtures/charges/update.json
|
108
109
|
- spec/fixtures/charges/void.json
|
109
110
|
- spec/spec_helper.rb
|
data/lib/affirm/charges.rb
DELETED
@@ -1,103 +0,0 @@
|
|
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
|
data/spec/charges_spec.rb
DELETED
@@ -1,170 +0,0 @@
|
|
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
|