charger 0.0.1 → 0.0.2

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.
@@ -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