taxjar-ruby 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 +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +173 -0
- data/Rakefile +2 -0
- data/lib/taxjar.rb +15 -0
- data/lib/taxjar/api/api.rb +37 -0
- data/lib/taxjar/api/request.rb +70 -0
- data/lib/taxjar/api/utils.rb +40 -0
- data/lib/taxjar/base.rb +88 -0
- data/lib/taxjar/breakdown.rb +16 -0
- data/lib/taxjar/breakdown_line_item.rb +10 -0
- data/lib/taxjar/category.rb +7 -0
- data/lib/taxjar/client.rb +25 -0
- data/lib/taxjar/error.rb +74 -0
- data/lib/taxjar/line_item.rb +9 -0
- data/lib/taxjar/order.rb +13 -0
- data/lib/taxjar/rate.rb +9 -0
- data/lib/taxjar/refund.rb +13 -0
- data/lib/taxjar/shipping.rb +9 -0
- data/lib/taxjar/tax.rb +11 -0
- data/lib/taxjar/version.rb +38 -0
- data/spec/fixtures/categories.json +39 -0
- data/spec/fixtures/order.json +26 -0
- data/spec/fixtures/rates.json +13 -0
- data/spec/fixtures/refund.json +27 -0
- data/spec/fixtures/taxes.json +42 -0
- data/spec/helper.rb +45 -0
- data/spec/taxjar/api/api_spec.rb +207 -0
- data/spec/taxjar/api/request_spec.rb +134 -0
- data/spec/taxjar/base_spec.rb +28 -0
- data/spec/taxjar/client_spec.rb +23 -0
- data/spec/taxjar/error_spec.rb +21 -0
- data/spec/taxjar/version_spec.rb +34 -0
- data/taxjar-ruby.gemspec +26 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2a129c647b66e1054f0438dcd65485090e28b533
|
4
|
+
data.tar.gz: d5a63ab29627fb0c98deab4e59007712f934d726
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9e2f7ec935c28f4262da4555e1419d399ddf720d5a39f0f12dc370b3f78e0b3f452396ab555e0f5a69476c909af7b98154bd29d61a48b21c6f89e858c5e7b744
|
7
|
+
data.tar.gz: 5a13e3b0650ecc020f1d028d37aca79dd89af0ae16bad544ac9e3e12a01cbc578d1dbb90b3e6143d59732c3be44a964cc9afbdc108164b80352cecc122d83f58
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Tim Case
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
# The Taxjar Ruby Gem
|
2
|
+
[](http://developers.taxjar.com)
|
3
|
+
|
4
|
+
A Ruby interface to the Taxjar API. TaxJar makes sales tax filing easier for online sellers and merchants.
|
5
|
+
See local jurisdictional tax reports, get payment reminders, and more. You can use our API to access TaxJar API endpoints,
|
6
|
+
which can get information on sales tax rates, categories or upload transactions.
|
7
|
+
|
8
|
+
* This wrapper supports 100% of the [Taxjar API Version 2](http://developers.taxjar.com/api/#introduction)
|
9
|
+
* Data returned from API calls are mapped into Ruby objects
|
10
|
+
|
11
|
+
|
12
|
+
## Gem Dependencies
|
13
|
+
Installing this gem also installs the following gems:
|
14
|
+
|
15
|
+
* [http](https://github.com/httprb/http.rb) http.rb: a fast Ruby HTTP client with a chainable API and full streaming support
|
16
|
+
* [addressable](https://github.com/sporkmonger/addressable) Addressable is a replacement for the URI implementation that is part of Ruby's standard library. It more closely conforms to the relevant RFCs and adds support for IRIs and URI templates.
|
17
|
+
* [memoizable](https://github.com/dkubb/memoizable) Memoize method return values
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Add this line to your application's Gemfile:
|
22
|
+
```ruby
|
23
|
+
gem 'taxjar-ruby', require: 'taxjar'
|
24
|
+
```
|
25
|
+
And then execute:
|
26
|
+
```shell
|
27
|
+
$ bundle install
|
28
|
+
```
|
29
|
+
Or install it yourself as:
|
30
|
+
```shell
|
31
|
+
$ gem install taxjar-ruby
|
32
|
+
```
|
33
|
+
## Quick Start Guide
|
34
|
+
|
35
|
+
First, [get an api key from Taxar][https://app.taxjar.com/api_sign_up/plus/].
|
36
|
+
|
37
|
+
Then copy and paste in your API keys:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
client = Taxjar::Client.new(api_key: "YOUR KEY")
|
41
|
+
```
|
42
|
+
|
43
|
+
|
44
|
+
You are ready to use Taxjar.
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
### List all tax categories
|
49
|
+
```ruby
|
50
|
+
categories = client.categories
|
51
|
+
|
52
|
+
```
|
53
|
+
### List tax rates for a location (by zip/postal code)
|
54
|
+
```ruby
|
55
|
+
rates = client.rates_for_location('10001')
|
56
|
+
```
|
57
|
+
|
58
|
+
### Calculate Sales tax for an order
|
59
|
+
```ruby
|
60
|
+
order = client.tax_for_order({
|
61
|
+
:to_country => 'US',
|
62
|
+
:to_zip => '90002',
|
63
|
+
:to_city => 'Los Angeles',
|
64
|
+
:from_country => 'US',
|
65
|
+
:from_zip => '92093',
|
66
|
+
:from_city => 'San Diego',
|
67
|
+
:amount => 16.50,
|
68
|
+
:shipping => 1.5,
|
69
|
+
:line_items => [{:quantity => 1,
|
70
|
+
:product_identifier => '12-34243-9',
|
71
|
+
:unit_price => 15.0,
|
72
|
+
:product_tax_code => 31000}]
|
73
|
+
})
|
74
|
+
```
|
75
|
+
|
76
|
+
### Create order transaction
|
77
|
+
```ruby
|
78
|
+
order = client.create_order({
|
79
|
+
:transaction_id => '123',
|
80
|
+
:transaction_date => '2015/05/14',
|
81
|
+
:to_country => 'US',
|
82
|
+
:to_zip => '90002',
|
83
|
+
:to_city => 'Los Angeles',
|
84
|
+
:to_street => '123 Palm Grove Ln',
|
85
|
+
:amount => 17.45,
|
86
|
+
:shipping => 1.5,
|
87
|
+
:sales_tax => 0.95,
|
88
|
+
:line_items => [{:quantity => 1,
|
89
|
+
:product_identifier => '12-34243-9',
|
90
|
+
:descriptiion => 'Fuzzy Widget',
|
91
|
+
:unit_price => 15.0,
|
92
|
+
:sales_tax => 0.95}]
|
93
|
+
})
|
94
|
+
```
|
95
|
+
|
96
|
+
### Update order transaction
|
97
|
+
```ruby
|
98
|
+
order = client.update_order({
|
99
|
+
:transaction_id => '123',
|
100
|
+
:amount => 17.95,
|
101
|
+
:shipping => 2.0,
|
102
|
+
:line_items => [{:quantity => 1,
|
103
|
+
:product_identifier => '12-34243-0',
|
104
|
+
:descriptiion => 'Heavy Widget',
|
105
|
+
:unit_price => 15.0,
|
106
|
+
:discount => 0.0,
|
107
|
+
:sales_tax => 0.95}]
|
108
|
+
})
|
109
|
+
```
|
110
|
+
|
111
|
+
### Create refund transaction
|
112
|
+
```ruby
|
113
|
+
refund = client.create_refund({
|
114
|
+
:transaction_id => '321',
|
115
|
+
:transaction_date => '2015/05/14',
|
116
|
+
:transaction_reference_id => '123',
|
117
|
+
:to_country => 'US',
|
118
|
+
:to_zip => '90002',
|
119
|
+
:to_state => 'CA',
|
120
|
+
:to_city => 'Los Angeles',
|
121
|
+
:to_street => '123 Palm Grove Ln',
|
122
|
+
:amount => 17.45,
|
123
|
+
:shipping => 1.5,
|
124
|
+
:sales_tax => 0.95,
|
125
|
+
:line_items => [{:quantity => 1,
|
126
|
+
:product_identifier => '12-34243-9',
|
127
|
+
:descriptiion => 'Fuzzy Widget',
|
128
|
+
:unit_price => 15.0,
|
129
|
+
:sales_tax => 0.95}]
|
130
|
+
})
|
131
|
+
```
|
132
|
+
|
133
|
+
### Update refund transaction
|
134
|
+
```ruby
|
135
|
+
refund = client.update_refund{
|
136
|
+
:transaction_id => '321',
|
137
|
+
:amount => 17.95,
|
138
|
+
:shipping => 2.0,
|
139
|
+
:sales_tax => 0.95,
|
140
|
+
:line_items => [{:quantity => 1,
|
141
|
+
:product_identifier => '12-34243-9',
|
142
|
+
:descriptiion => 'Heavy Widget',
|
143
|
+
:unit_price => 15.0,
|
144
|
+
:sales_tax => 0.95}]
|
145
|
+
})
|
146
|
+
```
|
147
|
+
## Tests
|
148
|
+
An Rspec test suite is available to ensure API functionality:
|
149
|
+
|
150
|
+
1. $ git clone git://github.com/taxjar/taxjar-ruby.git
|
151
|
+
2. $ bundle install (installs rspec and other supporting gems, see
|
152
|
+
[GEMFILE](https://github.com/taxjar/taxjar-ruby/blob/master/Gemfile)
|
153
|
+
for complete listing)
|
154
|
+
3. $ rspec
|
155
|
+
|
156
|
+
## More Information
|
157
|
+
More information can be found on the [Taxjar developer site](https://developers.taxjar.com).
|
158
|
+
|
159
|
+
## License
|
160
|
+
Taxjar is released under the [MIT
|
161
|
+
License](https://github.com/taxjar/taxjar-ruby/blob/master/LICENSE.txt).
|
162
|
+
|
163
|
+
## Support
|
164
|
+
Bug reports and feature requests should be filed on the [github issue
|
165
|
+
tracking page](https://github.com/taxjar/taxjar-ruby/issues).
|
166
|
+
|
167
|
+
## Contributing
|
168
|
+
|
169
|
+
1. Fork it
|
170
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
171
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
172
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
173
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/taxjar.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "taxjar/base"
|
2
|
+
require "taxjar/breakdown"
|
3
|
+
require "taxjar/breakdown_line_item"
|
4
|
+
require "taxjar/category"
|
5
|
+
require "taxjar/client"
|
6
|
+
require "taxjar/line_item"
|
7
|
+
require "taxjar/order"
|
8
|
+
require "taxjar/rate"
|
9
|
+
require "taxjar/refund"
|
10
|
+
require "taxjar/shipping"
|
11
|
+
require "taxjar/tax"
|
12
|
+
require "taxjar/version"
|
13
|
+
|
14
|
+
module Taxjar
|
15
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'taxjar/api/utils'
|
2
|
+
module Taxjar
|
3
|
+
module API
|
4
|
+
include Taxjar::API::Utils
|
5
|
+
|
6
|
+
def categories(options = {})
|
7
|
+
perform_get_with_objects("/v2/categories", 'categories', options, Taxjar::Category)
|
8
|
+
end
|
9
|
+
|
10
|
+
def rates_for_location(postal_code, options ={})
|
11
|
+
perform_get_with_object("/v2/rates/#{postal_code}", 'rate', options, Taxjar::Rate)
|
12
|
+
end
|
13
|
+
|
14
|
+
def tax_for_order(options = {})
|
15
|
+
perform_post_with_object("/v2/taxes", 'tax', options, Taxjar::Tax)
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_order(options = {})
|
19
|
+
perform_post_with_object("/v2/transactions/orders", 'order', options, Taxjar::Order)
|
20
|
+
end
|
21
|
+
|
22
|
+
def update_order(options = {})
|
23
|
+
id = options.fetch(:transaction_id)
|
24
|
+
perform_put_with_object("/v2/transactions/orders/#{id}", 'order', options, Taxjar::Order)
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_refund(options = {})
|
28
|
+
perform_post_with_object("/v2/transactions/refunds", 'refund', options, Taxjar::Refund)
|
29
|
+
end
|
30
|
+
|
31
|
+
def update_refund(options = {})
|
32
|
+
id = options.fetch(:transaction_id)
|
33
|
+
perform_put_with_object("/v2/transactions/refunds/#{id}", 'refund', options, Taxjar::Refund)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'http'
|
3
|
+
|
4
|
+
module Taxjar
|
5
|
+
module API
|
6
|
+
class Request
|
7
|
+
BASE_URL = 'https://api.taxjar.com'
|
8
|
+
|
9
|
+
attr_reader :client, :uri, :headers, :request_method, :path, :object_key, :options
|
10
|
+
|
11
|
+
# @param client [Taxjar::Client]
|
12
|
+
# @param request_method [String, Symbol]
|
13
|
+
# @param path [String]
|
14
|
+
# @param object_key [String]
|
15
|
+
def initialize(client, request_method, path, object_key, options = {})
|
16
|
+
@client = client
|
17
|
+
@request_method = request_method
|
18
|
+
@path = path
|
19
|
+
@uri = Addressable::URI.parse(BASE_URL + path)
|
20
|
+
set_request_headers
|
21
|
+
@object_key = object_key
|
22
|
+
@options = options
|
23
|
+
end
|
24
|
+
|
25
|
+
def perform
|
26
|
+
options_key = @request_method == :get ? :params : :json
|
27
|
+
response = HTTP.with(headers).public_send(request_method, uri.to_s, options_key => @options)
|
28
|
+
response_body = symbolize_keys!(response.parse)
|
29
|
+
fail_or_return_response_body(response.code, response_body)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
|
35
|
+
def set_request_headers
|
36
|
+
@headers = {}
|
37
|
+
@headers[:user_agent] = client.user_agent
|
38
|
+
@headers[:authorization] = "Bearer #{client.api_key}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def symbolize_keys!(object)
|
42
|
+
if object.is_a?(Array)
|
43
|
+
object.each_with_index do |val, index|
|
44
|
+
object[index] = symbolize_keys!(val)
|
45
|
+
end
|
46
|
+
elsif object.is_a?(Hash)
|
47
|
+
object.keys.each do |key|
|
48
|
+
object[key.to_sym] = symbolize_keys!(object.delete(key))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
object
|
52
|
+
end
|
53
|
+
|
54
|
+
def fail_or_return_response_body(code, body)
|
55
|
+
e = extract_error(code, body)
|
56
|
+
fail(e) if e
|
57
|
+
body[object_key.to_sym]
|
58
|
+
end
|
59
|
+
|
60
|
+
def extract_error(code, body)
|
61
|
+
klass = Taxjar::Error::ERRORS[code]
|
62
|
+
if !klass.nil?
|
63
|
+
klass.from_response(body)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'taxjar/api/request'
|
2
|
+
|
3
|
+
module Taxjar
|
4
|
+
module API
|
5
|
+
module Utils
|
6
|
+
|
7
|
+
def perform_request(request_method, path, object_key, options = {})
|
8
|
+
Taxjar::API::Request.new(self, request_method, path, object_key, options).perform
|
9
|
+
end
|
10
|
+
|
11
|
+
def perform_get_with_object(path, object_key, options, klass)
|
12
|
+
perform_request_with_object(:get, path, object_key, options, klass)
|
13
|
+
end
|
14
|
+
|
15
|
+
def perform_get_with_objects(path, object_key, options, klass)
|
16
|
+
perform_request_with_objects(:get, path, object_key, options, klass)
|
17
|
+
end
|
18
|
+
|
19
|
+
def perform_post_with_object(path, object_key, options, klass)
|
20
|
+
perform_request_with_object(:post, path, object_key, options, klass)
|
21
|
+
end
|
22
|
+
|
23
|
+
def perform_put_with_object(path, object_key, options, klass)
|
24
|
+
perform_request_with_object(:put, path, object_key, options, klass)
|
25
|
+
end
|
26
|
+
|
27
|
+
def perform_request_with_object(request_method, path, object_key, options, klass)
|
28
|
+
response = perform_request(request_method, path, object_key, options)
|
29
|
+
klass.new(response)
|
30
|
+
end
|
31
|
+
|
32
|
+
def perform_request_with_objects(request_method, path, object_key, options, klass)
|
33
|
+
response_array = perform_request(request_method, path, object_key, options) || []
|
34
|
+
response_array.collect do |element|
|
35
|
+
klass.new(element)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/taxjar/base.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'memoizable'
|
4
|
+
|
5
|
+
module Taxjar
|
6
|
+
class Base
|
7
|
+
extend Forwardable
|
8
|
+
include Memoizable
|
9
|
+
|
10
|
+
attr_reader :attrs
|
11
|
+
alias_method :to_h, :attrs
|
12
|
+
alias_method :to_hash, :to_h
|
13
|
+
|
14
|
+
def map_collection(klass, key)
|
15
|
+
Array(@attrs[key.to_sym]).map do |entity|
|
16
|
+
klass.new(entity)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class << self
|
21
|
+
|
22
|
+
def attr_reader(*attrs)
|
23
|
+
attrs.each do |attr|
|
24
|
+
define_attribute_method(attr)
|
25
|
+
define_predicate_method(attr)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def object_attr_reader(klass, key1, key2 = nil)
|
30
|
+
define_attribute_method(key1, klass, key2)
|
31
|
+
define_predicate_method(key1)
|
32
|
+
end
|
33
|
+
|
34
|
+
def define_attribute_method(key1, klass = nil, key2 = nil)
|
35
|
+
define_method(key1) do
|
36
|
+
if attr_falsey_or_empty?(key1)
|
37
|
+
else
|
38
|
+
klass.nil? ? @attrs[key1] : klass.new(attrs_for_object(key1, key2))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
memoize(key1)
|
42
|
+
end
|
43
|
+
|
44
|
+
def define_predicate_method(key1, key2 = key1)
|
45
|
+
define_method(:"#{key1}?") do
|
46
|
+
!attr_falsey_or_empty?(key2)
|
47
|
+
end
|
48
|
+
memoize(:"#{key1}?")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(attrs = {})
|
53
|
+
attrs = values_as_floats_where_possible(attrs)
|
54
|
+
@attrs = attrs || {}
|
55
|
+
end
|
56
|
+
|
57
|
+
def [](method)
|
58
|
+
warn "#{Kernel.caller.first}: [DEPRECATION] #[#{method.inspect}] is deprecated. Use ##{method} to fetch the value."
|
59
|
+
send(method.to_sym)
|
60
|
+
rescue NoMethodError
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def attr_falsey_or_empty?(key)
|
67
|
+
!@attrs[key] || @attrs[key].respond_to?(:empty?) && @attrs[key].empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
def attrs_for_object(key1, key2 = nil)
|
71
|
+
if key2.nil?
|
72
|
+
@attrs[key1]
|
73
|
+
else
|
74
|
+
attrs = @attrs.dup
|
75
|
+
attrs.delete(key1).merge(key2 => attrs)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def values_as_floats_where_possible(attrs)
|
80
|
+
attrs.map{|k, v| [k, to_f_or_i_or_s(v)]}.to_h
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_f_or_i_or_s(v)
|
84
|
+
((float = Float(v)) && (float % 1.0 == 0) ? float.to_i : float) rescue v
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|