apruve 0.9.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.
- data/.gitignore +42 -0
- data/.rspec +2 -0
- data/Gemfile +29 -0
- data/Gemfile.lock +98 -0
- data/LICENSE.txt +22 -0
- data/README.md +44 -0
- data/Rakefile +13 -0
- data/apruve.gemspec +27 -0
- data/lib/apruve.rb +101 -0
- data/lib/apruve/client.rb +102 -0
- data/lib/apruve/error.rb +116 -0
- data/lib/apruve/faraday_error_handler.rb +16 -0
- data/lib/apruve/resources.rb +10 -0
- data/lib/apruve/resources/apruve_object.rb +44 -0
- data/lib/apruve/resources/line_item.rb +13 -0
- data/lib/apruve/resources/payment.rb +43 -0
- data/lib/apruve/resources/payment_item.rb +7 -0
- data/lib/apruve/resources/payment_request.rb +60 -0
- data/lib/apruve/resources/subscription.rb +26 -0
- data/lib/apruve/resources/subscription_adjustment.rb +33 -0
- data/lib/apruve/resources/validation_error.rb +12 -0
- data/lib/apruve/response/apruve_exception_middleware.rb +39 -0
- data/lib/apruve/response/apruve_parse_json.rb +11 -0
- data/lib/apruve/utils.rb +108 -0
- data/lib/apruve/version.rb +3 -0
- data/spec/apruve/apruve_spec.rb +96 -0
- data/spec/apruve/client_spec.rb +17 -0
- data/spec/apruve/config_spec.rb +7 -0
- data/spec/apruve/resources/line_item_spec.rb +39 -0
- data/spec/apruve/resources/payment_item_spec.rb +17 -0
- data/spec/apruve/resources/payment_request_spec.rb +133 -0
- data/spec/apruve/resources/payment_spec.rb +129 -0
- data/spec/apruve/resources/subscription_spec.rb +141 -0
- data/spec/apruve/response/apruve_exception_middleware_spec.rb +43 -0
- data/spec/spec_helper.rb +34 -0
- metadata +180 -0
data/lib/apruve/error.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
module Apruve
|
2
|
+
|
3
|
+
# Custom error class for rescuing from all API response-related Apruve errors
|
4
|
+
class Error < ::StandardError
|
5
|
+
attr_reader :response
|
6
|
+
|
7
|
+
# @param [Hash] response the decoded json response body
|
8
|
+
def initialize(response=nil)
|
9
|
+
@response = response
|
10
|
+
unless response.nil?
|
11
|
+
super error_message
|
12
|
+
# super 'Error!'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Hash]
|
17
|
+
def body
|
18
|
+
@body ||= begin
|
19
|
+
return {} unless response[:body]
|
20
|
+
Utils.indifferent_read_access(response[:body])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def error_message
|
25
|
+
# set_attrs
|
26
|
+
errors = body.fetch('errors', nil)
|
27
|
+
unless errors.nil?
|
28
|
+
error = errors[0][:error]
|
29
|
+
extra = error[:message] ? " -- #{error[:message]}" : ""
|
30
|
+
"#{self.class.name}(#{response[:status]}):: "\
|
31
|
+
"#{response[:method].to_s.upcase} #{response[:url].to_s}: "\
|
32
|
+
"#{error[:description]} #{extra}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# General error class for non API response exceptions
|
38
|
+
class StandardError < Error
|
39
|
+
attr_reader :message
|
40
|
+
alias :error_message :message
|
41
|
+
|
42
|
+
# @param [String, nil] message a description of the exception
|
43
|
+
def initialize(message = nil)
|
44
|
+
@message = message
|
45
|
+
super(message)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Raised when Apruve returns a 400 HTTP status code
|
50
|
+
class BadRequest < Error;
|
51
|
+
end
|
52
|
+
|
53
|
+
# Raised when Apruve returns a 401 HTTP status code
|
54
|
+
class Unauthorized < Error;
|
55
|
+
end
|
56
|
+
|
57
|
+
# Raised when Apruve returns a 403 HTTP status code
|
58
|
+
class Forbidden < Error;
|
59
|
+
end
|
60
|
+
|
61
|
+
# Raised when Apruve returns a 404 HTTP status code
|
62
|
+
class NotFound < Error;
|
63
|
+
end
|
64
|
+
|
65
|
+
# Raised when Apruve returns a 405 HTTP status code
|
66
|
+
class MethodNotAllowed < Error;
|
67
|
+
end
|
68
|
+
|
69
|
+
# Raised when Apruve returns a 406 HTTP status code
|
70
|
+
class AccessDenied < Error;
|
71
|
+
end
|
72
|
+
|
73
|
+
# Raised when Apruve returns a 409 HTTP status code
|
74
|
+
class Conflict < Error;
|
75
|
+
end
|
76
|
+
|
77
|
+
# Raised when Apruve returns a 410 HTTP status code
|
78
|
+
class Gone < Error;
|
79
|
+
end
|
80
|
+
|
81
|
+
# Raised when Apruve returns a 500 HTTP status code
|
82
|
+
class InternalServerError < Error;
|
83
|
+
end
|
84
|
+
|
85
|
+
# Raised when Apruve returns a 501 HTTP status code
|
86
|
+
class NotImplemented < Error;
|
87
|
+
end
|
88
|
+
|
89
|
+
# Raised when Apruve returns a 502 HTTP status code
|
90
|
+
class BadGateway < Error;
|
91
|
+
end
|
92
|
+
|
93
|
+
# Raised when Apruve returns a 503 HTTP status code
|
94
|
+
class ServiceUnavailable < Error;
|
95
|
+
end
|
96
|
+
|
97
|
+
# Raised when Apruve returns a 504 HTTP status code
|
98
|
+
class GatewayTimeout < Error;
|
99
|
+
end
|
100
|
+
|
101
|
+
# Raised when cannot connect to Apruve
|
102
|
+
class ServiceUnreachable < Error;
|
103
|
+
end
|
104
|
+
|
105
|
+
# Raised when we get a Faraday::ParseError
|
106
|
+
class ResponseUnreadable < Error;
|
107
|
+
end
|
108
|
+
|
109
|
+
# Raised when we haven't a clue
|
110
|
+
class UnknownError < Error;
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
# custom mapped exceptions
|
115
|
+
# class ValidationError < Error; end
|
116
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Apruve
|
2
|
+
# @api private
|
3
|
+
class FaradayErrorHandler < Faraday::Middleware
|
4
|
+
def call(env)
|
5
|
+
begin
|
6
|
+
@app.call(env)
|
7
|
+
rescue Faraday::ConnectionFailed
|
8
|
+
raise Apruve::ServiceUnreachable.new
|
9
|
+
rescue Faraday::ParsingError
|
10
|
+
raise Apruve::ResponseUnreadable.new
|
11
|
+
rescue Faraday::ClientError
|
12
|
+
raise Apruve::UnknownError.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# $:.unshift(File.join(File.dirname(__FILE__), 'apruve', 'resources'))
|
2
|
+
|
3
|
+
require_relative 'resources/validation_error'
|
4
|
+
require_relative 'resources/apruve_object'
|
5
|
+
require_relative 'resources/payment_request'
|
6
|
+
require_relative 'resources/line_item'
|
7
|
+
require_relative 'resources/payment'
|
8
|
+
require_relative 'resources/payment_item'
|
9
|
+
require_relative 'resources/subscription'
|
10
|
+
require_relative 'resources/subscription_adjustment'
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Apruve
|
2
|
+
class ApruveObject
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
def initialize(args = {})
|
6
|
+
args.each do |k, v|
|
7
|
+
instance_variable_set("@#{k}".to_sym, v) unless v.nil?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate
|
12
|
+
# default implementation.
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_hash
|
16
|
+
validate
|
17
|
+
hash = {}
|
18
|
+
instance_variables.each do |var|
|
19
|
+
if instance_variable_get(var).kind_of?(Array)
|
20
|
+
array = []
|
21
|
+
instance_variable_get(var).each { |aryvar| array.push(aryvar.to_hash) }
|
22
|
+
hash[var.to_s.delete('@')] = array
|
23
|
+
else
|
24
|
+
hash[var.to_s.delete('@')] = instance_variable_get(var)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
hash.reject! { |k, v| v.nil? }
|
28
|
+
hash.reject! { |k, v| k == 'api_key' }
|
29
|
+
hash
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_json(*a)
|
33
|
+
to_hash.to_json
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.logger
|
37
|
+
Apruve.client.config[:logger]
|
38
|
+
end
|
39
|
+
|
40
|
+
def logger
|
41
|
+
Apruve.client.config[:logger]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Apruve
|
2
|
+
class LineItem < Apruve::ApruveObject
|
3
|
+
attr_accessor :id, :title, :amount_cents, :price_ea_cents, :quantity, :description,
|
4
|
+
:variant_info, :sku, :vendor, :view_product_url, :plan_code, :line_item_api_url,
|
5
|
+
:subscription_url
|
6
|
+
|
7
|
+
def validate
|
8
|
+
errors = []
|
9
|
+
errors << 'title must be set on line items' if title.nil?
|
10
|
+
raise Apruve::ValidationError.new(errors) if errors.length > 0
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Apruve
|
2
|
+
class Payment < Apruve::ApruveObject
|
3
|
+
attr_accessor :id, :payment_request_id, :status, :status, :amount_cents, :currency, :merchant_notes,
|
4
|
+
:payment_items, :api_url, :view_url, :created_at, :updated_at
|
5
|
+
|
6
|
+
def self.find(payment_request_id, id)
|
7
|
+
response = Apruve.get("payment_requests/#{payment_request_id}/payments/#{id}")
|
8
|
+
Payment.new(response.body)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(params)
|
12
|
+
super
|
13
|
+
# hydrate payment items if appropriate
|
14
|
+
if @payment_items.nil?
|
15
|
+
@payment_items = []
|
16
|
+
elsif @payment_items.is_a?(Array) && @payment_items.first.is_a?(Hash)
|
17
|
+
hydrated_items = []
|
18
|
+
@payment_items.each do |item|
|
19
|
+
hydrated_items << Apruve::LineItem.new(item)
|
20
|
+
end
|
21
|
+
@payment_items = hydrated_items
|
22
|
+
end
|
23
|
+
@currency = Apruve.default_currency if currency.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate
|
27
|
+
errors = []
|
28
|
+
errors << 'payment_request_id must be set' if payment_request_id.nil?
|
29
|
+
errors << 'amount_cents must be set' if amount_cents.nil?
|
30
|
+
raise Apruve::ValidationError.new(errors) if errors.length > 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def save!
|
34
|
+
validate
|
35
|
+
response = Apruve.post("payment_requests/#{self.payment_request_id}/payments", self.to_json)
|
36
|
+
self.id = response.body['id']
|
37
|
+
self.status = response.body['status']
|
38
|
+
self.api_url = response.body['api_url']
|
39
|
+
self.view_url = response.body['view_url']
|
40
|
+
self.status
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Apruve
|
2
|
+
class PaymentRequest < Apruve::ApruveObject
|
3
|
+
attr_accessor :id, :merchant_id, :merchant_order_id, :status, :amount_cents, :tax_cents,
|
4
|
+
:shipping_cents, :line_items, :api_url, :view_url, :created_at, :updated_at
|
5
|
+
|
6
|
+
def self.find(id)
|
7
|
+
response = Apruve.get("payment_requests/#{id}")
|
8
|
+
logger.debug response.body
|
9
|
+
PaymentRequest.new(response.body)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.finalize!(id)
|
13
|
+
response = Apruve.post("payment_requests/#{id}/finalize")
|
14
|
+
logger.debug response.body
|
15
|
+
response.body
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(params)
|
19
|
+
super
|
20
|
+
# hydrate line items if appropriate
|
21
|
+
if @line_items.nil?
|
22
|
+
@line_items = []
|
23
|
+
elsif @line_items.is_a?(Array) && @line_items.first.is_a?(Hash)
|
24
|
+
hydrated_items = []
|
25
|
+
@line_items.each do |item|
|
26
|
+
hydrated_items << Apruve::LineItem.new(item)
|
27
|
+
end
|
28
|
+
@line_items = hydrated_items
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate
|
33
|
+
errors = []
|
34
|
+
errors << 'merchant_id must be set' if merchant_id.nil?
|
35
|
+
raise Apruve::ValidationError.new(errors) if errors.length > 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def value_string
|
39
|
+
token_string = to_hash.map do |k, v|
|
40
|
+
str = ''
|
41
|
+
if v.kind_of?(Array)
|
42
|
+
v.each do |item|
|
43
|
+
str = str + item.map{|q,r| r}.join
|
44
|
+
end
|
45
|
+
else
|
46
|
+
str = v
|
47
|
+
end
|
48
|
+
str
|
49
|
+
end
|
50
|
+
token_string.join
|
51
|
+
end
|
52
|
+
|
53
|
+
def secure_hash
|
54
|
+
if Apruve.client.api_key.nil?
|
55
|
+
raise 'api_key has not been set. Set it with Apruve.configure(api_key, environment, options)'
|
56
|
+
end
|
57
|
+
Digest::SHA256.hexdigest(Apruve.client.api_key+value_string)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Apruve
|
2
|
+
class Subscription < Apruve::LineItem
|
3
|
+
attr_accessor :id, :start_at, :next_charge_at, :last_charge_at, :end_at, :canceled_at
|
4
|
+
|
5
|
+
def self.find(id)
|
6
|
+
response = Apruve.get("subscriptions/#{id}")
|
7
|
+
logger.debug response.body
|
8
|
+
Subscription.new(response.body)
|
9
|
+
end
|
10
|
+
|
11
|
+
def update!
|
12
|
+
validate
|
13
|
+
response = Apruve.put("subscriptions/#{self.id}", self.to_json)
|
14
|
+
logger.debug response.body
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def cancel!
|
19
|
+
response = Apruve.post("subscriptions/#{self.id}/cancel")
|
20
|
+
logger.debug response.body
|
21
|
+
self.canceled_at = Time.parse(response.body['canceled_at']) unless response.body['canceled_at'].nil?
|
22
|
+
self.end_at = Time.parse(response.body['end_at']) unless response.body['end_at'].nil?
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Apruve
|
2
|
+
class SubscriptionAdjustment < Apruve::ApruveObject
|
3
|
+
attr_accessor :id, :subscription_id, :status, :title, :amount_cents, :price_ea_cents, :quantity, :description,
|
4
|
+
:variant_info, :sku, :vendor, :view_product_url
|
5
|
+
|
6
|
+
def self.find(subscription_id, id)
|
7
|
+
response = Apruve.get("subscriptions/#{subscription_id}/adjustments/#{id}")
|
8
|
+
logger.debug response.body
|
9
|
+
SubscriptionAdjustment.new(response.body)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.find_all(subscription_id)
|
13
|
+
response = Apruve.get("subscriptions/#{subscription_id}/adjustments")
|
14
|
+
logger.debug response.body
|
15
|
+
SubscriptionAdjustment.new(response.body)
|
16
|
+
end
|
17
|
+
|
18
|
+
def save!
|
19
|
+
validate
|
20
|
+
response = Apruve.post("subscriptions/#{self.subscription_id}/adjustments", self.to_json)
|
21
|
+
self.id = response.body['id']
|
22
|
+
self.status = response.body['status']
|
23
|
+
self.api_url = response.body['api_url']
|
24
|
+
self.status
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete!
|
28
|
+
response = Apruve.delete("subscriptions/#{self.subscription_id}/adjustments/#{id}")
|
29
|
+
logger.debug response.body
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require_relative '../error'
|
3
|
+
|
4
|
+
# @api private
|
5
|
+
module Faraday
|
6
|
+
|
7
|
+
class Response::RaiseApruveError < Response::Middleware
|
8
|
+
|
9
|
+
HTTP_STATUS_CODES = {
|
10
|
+
400 => Apruve::BadRequest,
|
11
|
+
401 => Apruve::Unauthorized,
|
12
|
+
403 => Apruve::Forbidden,
|
13
|
+
404 => Apruve::NotFound,
|
14
|
+
405 => Apruve::MethodNotAllowed,
|
15
|
+
406 => Apruve::AccessDenied,
|
16
|
+
409 => Apruve::Conflict,
|
17
|
+
410 => Apruve::Gone,
|
18
|
+
500 => Apruve::InternalServerError,
|
19
|
+
501 => Apruve::NotImplemented,
|
20
|
+
502 => Apruve::BadGateway,
|
21
|
+
503 => Apruve::ServiceUnavailable,
|
22
|
+
504 => Apruve::GatewayTimeout,
|
23
|
+
}
|
24
|
+
|
25
|
+
def on_complete(response)
|
26
|
+
status_code = response[:status].to_i
|
27
|
+
# if response.key? :body and response[:body] != nil and response[:body]['errors']
|
28
|
+
# category_code = response[:body]['errors'][0]['category_code']
|
29
|
+
# else
|
30
|
+
# category_code = nil
|
31
|
+
# end
|
32
|
+
error_class = HTTP_STATUS_CODES[status_code]
|
33
|
+
# error_class = CATEGORY_CODE_MAP[category_code] || HTTP_STATUS_CODES[status_code]
|
34
|
+
raise error_class.new(response) if error_class
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|