charger 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,10 +2,30 @@ require "charger/version"
2
2
 
3
3
  module Charger
4
4
  class << self
5
- attr_accessor :api_key, :subdomain
5
+ # Your api key assigned to you buy them
6
+ @@api_key = 'notset'
7
+
8
+ # The chargify site
9
+ @@subdomain = 'notset'
6
10
 
7
11
  def configure
8
12
  yield self
9
13
  end
10
14
  end
15
+
16
+ def self.api_key
17
+ @@api_key
18
+ end
19
+
20
+ def self.api_key= api_key
21
+ @@api_key = api_key
22
+ end
23
+
24
+ def self.subdomain
25
+ @@subdomain
26
+ end
27
+
28
+ def self.subdomain= subdomain
29
+ @@subdomain = subdomain
30
+ end
11
31
  end
@@ -3,9 +3,9 @@ module Charger
3
3
  class Client
4
4
  attr_accessor :api_key, :subdomain
5
5
 
6
- def initialize api_key, subdomain
7
- @api_key = api_key
8
- @subdomain = subdomain
6
+ def initialize params={}
7
+ @api_key = params[:api_key] ? params[:api_key] : Charger.api_key
8
+ @subdomain = params[:subdomain] ? params[:subdomain] : Charger.subdomain
9
9
  end
10
10
 
11
11
  def get resource, headers={}
@@ -36,5 +36,4 @@ module Charger
36
36
  end
37
37
  end
38
38
 
39
-
40
39
  end
@@ -0,0 +1,69 @@
1
+ module Charger
2
+
3
+ class Component
4
+ include Virtus
5
+ extend ActiveModel::Naming
6
+ include ActiveModel::Conversion
7
+ include ActiveModel::Validations
8
+
9
+ attribute :id, Integer
10
+ attribute :name, String
11
+ attribute :unit_name, String
12
+ attribute :unit_price, Float
13
+ attribute :pricing_scheme, String
14
+ attribute :prices, Array[Component::Price]
15
+ attribute :product_family_id, Integer
16
+ attribute :kind, String
17
+ attribute :archived, Boolean
18
+
19
+ def total_for amount
20
+ prices.sort_by! { |k| k.starting_quantity }
21
+
22
+ total = 0.0
23
+
24
+ case pricing_scheme
25
+ when 'per_unit'
26
+ total = amount * unit_price
27
+ when 'volume'
28
+ total = amount * prices.select { |price| price.between_quantities?(amount) }.unit_price
29
+ when 'tiered'
30
+ prices.each do |price|
31
+ total += price.total(amount)
32
+ end
33
+ when 'stairstep'
34
+ total = prices.select { |price| price.between_quantities?(amount) }.unit_price
35
+ end
36
+
37
+ total
38
+ rescue => e
39
+ puts "[ERROR] #{e}"
40
+ puts "[ERROR] #{amount}"
41
+ puts "[ERROR] #{self.to_json}"
42
+ return 0.0
43
+ end
44
+
45
+ def self.find_by_product_family_id id
46
+ client = Client.new
47
+ components = []
48
+
49
+ client.get("product_families/#{id}/components").each do |data|
50
+ case data['component']['kind']
51
+ when 'quantity_based_component'
52
+ components << Component::QuantityBased.new(data['component'])
53
+ when 'metered_component'
54
+ components << Component::Metered.new(data['component'])
55
+ when 'on_off_component'
56
+ components << Component::OnOff.new(data['component'])
57
+ end
58
+ end
59
+
60
+ components
61
+ end
62
+
63
+ def self.find product_family_id, id
64
+ client = Client.new
65
+ new(client.get("product_families/#{product_family_id}/components/#{id}")['component'])
66
+ end
67
+ end
68
+
69
+ end
@@ -0,0 +1,6 @@
1
+ module Charger
2
+
3
+ class Component::Metered < Component
4
+ end
5
+
6
+ end
@@ -0,0 +1,9 @@
1
+ module Charger
2
+
3
+ class Component::OnOff < Component
4
+ def total_for val
5
+ val > 0 ? unit_price : 0.to_f
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,36 @@
1
+ module Charger
2
+
3
+ class Component::Price
4
+ include Virtus
5
+
6
+ attribute :ending_quantity, Integer
7
+ attribute :starting_quantity, Integer, default: 0
8
+ attribute :unit_price, Float
9
+
10
+ def between_quantities? amount
11
+ if ending_quantity
12
+ starting_quantity <= amount && ending_quantity >= amount
13
+ else
14
+ starting_quantity <= amount
15
+ end
16
+ end
17
+
18
+ def remaining_quantity amount
19
+ if ending_quantity
20
+ amount - ending_quantity
21
+ else
22
+ amount
23
+ end
24
+ end
25
+
26
+ def total amount
27
+ return 0.0 if amount < starting_quantity
28
+ if ending_quantity && amount > ending_quantity
29
+ ending_quantity * unit_price
30
+ else
31
+ (amount - (starting_quantity-1)) * unit_price
32
+ end
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,6 @@
1
+ module Charger
2
+
3
+ class Component::QuantityBased < Component
4
+ end
5
+
6
+ end
@@ -0,0 +1,35 @@
1
+ module Charger
2
+
3
+ class CreditCard
4
+ include Virtus
5
+ extend ActiveModel::Naming
6
+ include ActiveModel::Conversion
7
+ include ActiveModel::Validations
8
+
9
+ VAULTS = %w{cancellation_message trust_commerce payment_express beanstream braintree1}
10
+
11
+ attribute :id, Integer
12
+ attribute :payment_profile_id, Integer
13
+ attribute :card_type, String
14
+ attribute :expiration_month, Integer
15
+ attribute :expiration_year, Integer
16
+ attribute :first_name, String
17
+ attribute :last_name, String
18
+ attribute :masked_card_number, String
19
+ attribute :customer_id, Integer
20
+ attribute :customer_vault_token, String
21
+ attribute :vault_token, String
22
+ attribute :current_vault, String
23
+ attribute :billing_address, String
24
+ attribute :billing_address_2, String
25
+ attribute :billing_city, String
26
+ attribute :billing_state, String
27
+ attribute :billing_zip, String
28
+ attribute :billing_country, String
29
+
30
+ def persisted?
31
+ !!id
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,18 @@
1
+ module Charger
2
+
3
+ class Customer
4
+ include Virtus
5
+ extend ActiveModel::Naming
6
+ include ActiveModel::Conversion
7
+ include ActiveModel::Validations
8
+
9
+ attribute :id, Integer
10
+ attribute :first_name, String
11
+ attribute :last_name, String
12
+
13
+ def persisted?
14
+ !!id
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,75 @@
1
+ module Charger
2
+
3
+ class LineItem
4
+ include Virtus
5
+ extend ActiveModel::Naming
6
+ include ActiveModel::Conversion
7
+ include ActiveModel::Validations
8
+
9
+ attribute :subscription_id, Integer
10
+ attribute :component_id, Integer
11
+ attribute :name, String
12
+ attribute :unit_name, String
13
+ attribute :kind, String
14
+
15
+ # @param [Integer] the id of the subscription
16
+ # @return [Array<LineItem>]
17
+ def self.find_by_subscription_id id
18
+ client = Client.new
19
+ items = []
20
+
21
+ client.get("subscriptions/#{id}/components").each do |data|
22
+ case data['component']['kind']
23
+ when 'metered_component'
24
+ items << LineItem::Metered.new(data['component'])
25
+ when 'quantity_based_component'
26
+ items << LineItem::QuantityBased.new(data['component'])
27
+ when 'on_off_component'
28
+ items << LineItem::OnOff.new(data['component'])
29
+ end
30
+ end
31
+
32
+ items
33
+ end
34
+
35
+ # @return [Float]
36
+ def total
37
+ 0.0
38
+ end
39
+
40
+ def subscription= sub
41
+ @subscription = sub
42
+ end
43
+
44
+ # @param [Boolean] force uncache the results from chargify
45
+ # @return [Subscription]
46
+ def subscription force=false
47
+ @subscription = nil if force
48
+ @subscription ||= Subscription.find(subscription_id)
49
+ end
50
+
51
+ def component= comp
52
+ @component = comp
53
+ end
54
+
55
+ # @param [Boolean] force uncache the results from chargify
56
+ # @return [Component]
57
+ def component force=false
58
+ @component = nil if force
59
+
60
+ unless @component
61
+ product = subscription.product
62
+ family = product.product_family
63
+ @component = Component.find(family.id, component_id)
64
+ end
65
+
66
+ @component
67
+ end
68
+
69
+ # @return [Boolean]
70
+ def allocated?
71
+ false
72
+ end
73
+ end
74
+
75
+ end
@@ -0,0 +1,17 @@
1
+ module Charger
2
+
3
+ class LineItem::Metered < LineItem
4
+ attribute :unit_balance, Integer
5
+
6
+ # @return [Float]
7
+ def total
8
+ component.total_for(unit_balance)
9
+ end
10
+
11
+ # @return [Boolean]
12
+ def allocated?
13
+ unit_balance > 0
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,21 @@
1
+ module Charger
2
+
3
+ class LineItem::OnOff < LineItem
4
+ attribute :enabled, Boolean
5
+
6
+ # @return [Float]
7
+ def total
8
+ if enabled
9
+ component.total_for(1)
10
+ else
11
+ 0
12
+ end
13
+ end
14
+
15
+ # @return [Boolean]
16
+ def allocated?
17
+ enabled
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1,17 @@
1
+ module Charger
2
+
3
+ class LineItem::QuantityBased < LineItem
4
+ attribute :allocated_quantity, Integer
5
+
6
+ # @return [Float]
7
+ def total
8
+ component.total_for(allocated_quantity)
9
+ end
10
+
11
+ # @return [Boolean]
12
+ def allocated?
13
+ allocated_quantity > 0
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,46 @@
1
+ module Charger
2
+
3
+ class Product
4
+ include Virtus
5
+ extend ActiveModel::Naming
6
+ include ActiveModel::Conversion
7
+ include ActiveModel::Validations
8
+
9
+ attribute :id, Integer
10
+ attribute :price_in_cents, Integer
11
+ attribute :name, String
12
+ attribute :handle, String
13
+ attribute :description, String
14
+ attribute :product_family, ProductFamily
15
+ attribute :accounting_code, String
16
+ attribute :interval_unit, String
17
+ attribute :interval, Integer
18
+ attribute :initial_charge_in_cents, Integer
19
+ attribute :trial_price_in_cents, Integer
20
+ attribute :trial_interval, Integer
21
+ attribute :trial_interval_unit, String
22
+ attribute :expiration_interval, Integer
23
+ attribute :expiration_interval_unit, String
24
+ attribute :return_url, String
25
+ attribute :return_params, String
26
+ attribute :required_credit_card, Boolean
27
+ attribute :request_credit_card, Boolean
28
+ attribute :created_at, DateTime
29
+ attribute :updated_at, DateTime
30
+ attribute :archived_at, DateTime
31
+
32
+ def self.find_by_product_family_id id
33
+ client = Client.new
34
+ products = []
35
+ client.get("product_families/#{id}/products") do |data|
36
+ products << new(data['product'])
37
+ end
38
+ products
39
+ end
40
+
41
+ def persisted?
42
+ !!id
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,35 @@
1
+ module Charger
2
+
3
+ class ProductFamily
4
+ include Virtus
5
+ extend ActiveModel::Naming
6
+ include ActiveModel::Conversion
7
+ include ActiveModel::Validations
8
+
9
+ attribute :id, Integer
10
+ attribute :name, String
11
+ attribute :handle, String
12
+ attribute :accounting_code, String
13
+ attribute :description, String
14
+
15
+ # @param [Boolean] force will cause this to un-cache the results
16
+ def products force=false
17
+ @products = nil if force
18
+ @products ||= Product.find_by_product_family_id(id)
19
+ end
20
+
21
+ # Get all of the components descriptions associated to this product family
22
+ #
23
+ # @param [Boolean] force will cause this to un-cache the results
24
+ # @return [Array<Component>]
25
+ def components force=false
26
+ @components = nil if force
27
+ @components ||= Component.find_by_product_family_id(id)
28
+ end
29
+
30
+ def persisted?
31
+ !!id
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,167 @@
1
+ module Charger
2
+
3
+ class Subscription
4
+ include Virtus
5
+ extend ActiveModel::Naming
6
+ include ActiveModel::Conversion
7
+ include ActiveModel::Validations
8
+
9
+ STATES = %w{trialing trial_ended assessing active soft_failure past_due suspended canceled unpaid expired}
10
+
11
+ attribute :id, Integer
12
+ attribute :state, String, default: 'active'
13
+ attribute :balance_cents, Integer
14
+ attribute :current_period_started_at, DateTime
15
+ attribute :current_period_ends_at, DateTime
16
+ attribute :next_assessment_at, DateTime
17
+ attribute :trial_started_at, DateTime
18
+ attribute :trial_started_at, DateTime
19
+ attribute :trial_ended_at, DateTime
20
+ attribute :activated_at, DateTime
21
+ attribute :expires_at, DateTime
22
+ attribute :created_at, DateTime
23
+ attribute :updated_at, DateTime
24
+ attribute :customer, Customer
25
+ attribute :product, Product
26
+ attribute :credit_card, CreditCard
27
+ attribute :cancellation_message, String
28
+ attribute :canceled_at, DateTime
29
+ attribute :signup_revenue, Float
30
+ attribute :signup_payment_id, Integer
31
+ attribute :cancel_at_end_of_peroid, Boolean
32
+ attribute :delayed_cancel_at, DateTime
33
+ attribute :previous_state, String
34
+ attribute :coupon_code, String
35
+
36
+ validates :state, inclusion: {in: STATES, allow_blank: true, allow_nil: true}
37
+ validates :product, presence: true
38
+ validates :customer, presence: true
39
+
40
+ # Finds a [Subscription] in chargify. This can raise an exception.
41
+ #
42
+ # @raise [RestClient::Exception] if the id is invalid and the resource is
43
+ # not found.
44
+ # @return [Subscription]
45
+ def self.find id
46
+ client = Client.new
47
+ new(client.get("subscriptions/#{id}")['subscription'])
48
+ end
49
+
50
+ # This is a **VERY** long running task. It will scrape all of the
51
+ # subscriptions in the chargify account/
52
+ #
53
+ # @return [Array<Subscription>]
54
+ def self.all
55
+ client = Client.new
56
+ subscriptions = []
57
+
58
+ num = 1
59
+ loop do
60
+ subs = page(num, 200)
61
+ break if subs.empty?
62
+ num += 1
63
+ subscriptions += subs
64
+ end
65
+
66
+ subscriptions
67
+ end
68
+
69
+ # Get all of the subscriptions for a specific page
70
+ #
71
+ # @param [Integer] num default is 1
72
+ # @param [Integer] limit max is 200 and default is 20
73
+ # @return [Array<Subscription>]
74
+ def self.page num=1, limit=20
75
+ client = Client.new
76
+ subscriptions = []
77
+ client.get("subscriptions", params: {page: num, per_page: limit}).each do |data|
78
+ subscriptions << new(data['subscription'])
79
+ end
80
+ subscriptions
81
+ end
82
+
83
+ def live?
84
+ %w{trialing assessing active}.include?(state)
85
+ end
86
+
87
+ def trialing?
88
+ state == 'trialing'
89
+ end
90
+
91
+ def assessing?
92
+ state == 'assessing'
93
+ end
94
+
95
+ def active?
96
+ state == 'active'
97
+ end
98
+
99
+ def problem?
100
+ %w{soft_failure past_due unpaid}.include?(state)
101
+ end
102
+
103
+ def soft_failure?
104
+ state == 'soft_failure'
105
+ end
106
+
107
+ def past_due?
108
+ state == 'past_due'
109
+ end
110
+
111
+ def unpaid?
112
+ state == 'unpaid'
113
+ end
114
+
115
+ def end_of_life?
116
+ %w{canceled expired suspended trial_ended}.include?(state)
117
+ end
118
+
119
+ def canceled?
120
+ state == 'canceled'
121
+ end
122
+
123
+ def expired?
124
+ state == 'expired'
125
+ end
126
+
127
+ def suspended?
128
+ state == 'suspended'
129
+ end
130
+
131
+ def trial_ended?
132
+ state == 'trial_ended'
133
+ end
134
+
135
+ # @return [Boolean]
136
+ def persisted?
137
+ !!id
138
+ end
139
+
140
+ def total
141
+ sum = product.price_in_cents.to_f / 100.0
142
+ line_items.each do |item|
143
+ sum += item.total if item.allocated?
144
+ end
145
+ sum
146
+ end
147
+
148
+ def mrr
149
+ if product.interval_unit == 'day'
150
+ total / (product.interval / 30)
151
+ else
152
+ total / product.interval
153
+ end
154
+ end
155
+
156
+ # @param [Boolean] force will cause this to un-cache the results
157
+ def line_items force=false
158
+ @line_items = nil if force
159
+ @line_items ||= LineItem.find_by_subscription_id(id)
160
+ @line_items.each do |item|
161
+ item.subscription = self
162
+ end
163
+ @line_items
164
+ end
165
+ end
166
+
167
+ end
@@ -1,3 +1,3 @@
1
1
  module Charger
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: charger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -187,6 +187,20 @@ files:
187
187
  - charger.gemspec
188
188
  - lib/charger.rb
189
189
  - lib/charger/client.rb
190
+ - lib/charger/component.rb
191
+ - lib/charger/component/metered.rb
192
+ - lib/charger/component/on_off.rb
193
+ - lib/charger/component/price.rb
194
+ - lib/charger/component/quantity_based.rb
195
+ - lib/charger/credit_card.rb
196
+ - lib/charger/customer.rb
197
+ - lib/charger/line_item.rb
198
+ - lib/charger/line_item/metered.rb
199
+ - lib/charger/line_item/on_off.rb
200
+ - lib/charger/line_item/quantity_based.rb
201
+ - lib/charger/product.rb
202
+ - lib/charger/product_family.rb
203
+ - lib/charger/subscription.rb
190
204
  - lib/charger/version.rb
191
205
  homepage: https://github.com/warmwaffles/charger
192
206
  licenses: []
@@ -202,7 +216,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
202
216
  version: '0'
203
217
  segments:
204
218
  - 0
205
- hash: -4566746105116238615
219
+ hash: 484567601979014021
206
220
  required_rubygems_version: !ruby/object:Gem::Requirement
207
221
  none: false
208
222
  requirements:
@@ -211,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
211
225
  version: '0'
212
226
  segments:
213
227
  - 0
214
- hash: -4566746105116238615
228
+ hash: 484567601979014021
215
229
  requirements: []
216
230
  rubyforge_project:
217
231
  rubygems_version: 1.8.24