chargebee_rails 0.1.3

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.
@@ -0,0 +1,56 @@
1
+ module ChargebeeRails
2
+ class MeteredBilling
3
+ class << self
4
+
5
+ #
6
+ # Add charge to pending invoice
7
+ # * *Args* :
8
+ # - +invoice_id+ -> the pending invoice id
9
+ # - +amount+ -> the charge on item to be added
10
+ # - +description+ -> the description of the added item
11
+ # * *Returns* :
12
+ # - the chargebee pending invoice
13
+ # For more details on the +add_charge+ for pending invoice, refer
14
+ # {Add charge item to pending invoice}[https://apidocs.chargebee.com/docs/api/invoices?lang=ruby#add_charge_item_to_pending_invoice]
15
+ #
16
+ def add_charge(invoice_id, amount, description)
17
+ ChargeBee::Invoice.add_charge(invoice_id, {
18
+ amount: amount,
19
+ description: description
20
+ }).invoice
21
+ end
22
+
23
+ #
24
+ # Add addon charge to pending invoice
25
+ # * *Args* :
26
+ # - +invoice_id+ -> the pending invoice id
27
+ # - +addon_id+ -> the id of the addon in chargebee
28
+ # - +addon_quantity+ -> the quantity of addon, defaults to 1
29
+ # * *Returns* :
30
+ # - the chargebee pending invoice
31
+ # For more details on the +addon_charge+ for pending invoice, refer
32
+ # {Add addon item to pending invoice}[https://apidocs.chargebee.com/docs/api/invoices?lang=ruby#add_addon_item_to_pending_invoice]
33
+ #
34
+ def add_addon_charge(invoice_id, addon_id, addon_quantity=1)
35
+ ChargeBee::Invoice.add_addon_charge(invoice_id, {
36
+ addon_id: addon_id,
37
+ addon_quantity: addon_quantity
38
+ }).invoice
39
+ end
40
+
41
+ #
42
+ # Close pending invoice
43
+ # * *Args* :
44
+ # - +invoice_id+ -> the pending invoice id
45
+ # * *Returns* :
46
+ # - the chargebee invoice
47
+ # For more details on closing pending invoice, refer
48
+ # {Close a pending invoice}[https://apidocs.chargebee.com/docs/api/invoices?lang=ruby#close_a_pending_invoice]
49
+ #
50
+ def close_invoice(invoice_id)
51
+ ChargeBee::Invoice.close(invoice_id).invoice
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,163 @@
1
+ module ChargebeeRails
2
+ module Subscription
3
+
4
+ # Extend the class methods of including class
5
+ def self.included(base) #:nodoc:
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ # Retrieve subscription as chargebee subscription
10
+ def as_chargebee_subscription
11
+ ChargeBee::Subscription.retrieve(chargebee_id).subscription
12
+ end
13
+
14
+ #
15
+ # Update the plan for a subscription
16
+ # * *Args* :
17
+ # - +plan+ -> the plan to be updated
18
+ # - +end_of_term+ -> this boolean option specifies if the update takes
19
+ # effect after term end, the default behavior will be as per configuration
20
+ # - +prorate+ -> this boolean option specifies if the updates to subscription
21
+ # are prorated, the default behavior will be as per configuration
22
+ # For more details on the *updating plan for subscription*, refer
23
+ # {Update a subscription}[https://apidocs.chargebee.com/docs/api/subscriptions?lang=ruby#update_a_subscription]
24
+ #
25
+ def change_plan(plan, end_of_term=nil, prorate=nil)
26
+ end_of_term ||= ChargebeeRails.configuration.end_of_term
27
+ prorate ||= ChargebeeRails.configuration.proration
28
+ chargebee_subscription = ChargeBee::Subscription.update(
29
+ chargebee_id, { plan_id: plan.plan_id, end_of_term: end_of_term, prorate: prorate }
30
+ ).subscription
31
+ update(subscription_attrs(chargebee_subscription, plan))
32
+ end
33
+
34
+ #
35
+ # Update plan quantity for subscription
36
+ # * *Args* :
37
+ # - +quantity+ -> the plan quantity to be updated (integer)
38
+ # - +end_of_term+ -> this boolean option specifies if the update takes
39
+ # effect after term end, the default behavior will be as per configuration
40
+ # - +prorate+ -> this boolean option specifies if the updates to subscription
41
+ # are prorated, the default behavior will be as per configuration
42
+ # For more details on the *updating plan quantity for subscription*, refer
43
+ # {Set plan quantity for subscription}[https://apidocs.chargebee.com/docs/api/subscriptions?lang=ruby#update_a_subscription]
44
+ #
45
+ def set_plan_quantity(quantity, end_of_term=nil, prorate=nil)
46
+ end_of_term ||= ChargebeeRails.configuration.end_of_term
47
+ prorate ||= ChargebeeRails.configuration.proration
48
+ chargebee_subscription = ChargeBee::Subscription.update(
49
+ chargebee_id, { plan_quantity: quantity, end_of_term: end_of_term, prorate: prorate }
50
+ ).subscription
51
+ update(subscription_attrs(chargebee_subscription, self.plan))
52
+ end
53
+
54
+ #
55
+ # Add or remove addons for the subscription
56
+ # * *Args* :
57
+ # - +addon_id+ -> the id of addon in chargebee
58
+ # - +quantity+ -> the quantity of addon, defaults to 1
59
+ # - +replace_addon_list+ -> this boolean option specifies if the current
60
+ # addon list me be replaced with the updated one, defaults to false
61
+ # * *Returns* :
62
+ # - the chargebee subscription
63
+ # For more details on the *updating addon for subscription*, refer
64
+ # {Manage addons for subscription}[https://apidocs.chargebee.com/docs/api/subscriptions?lang=ruby#update_a_subscription]
65
+ #
66
+ def manage_addons(addon_id, quantity=1, replace_addon_list=false)
67
+ chargebee_subscription = ChargeBee::Subscription.update(
68
+ chargebee_id, { replace_addon_list: replace_addon_list, addons: [{ id: addon_id, quantity: quantity }] }
69
+ ).subscription
70
+ end
71
+
72
+ # Cancel a subscription - it will be scheduled for cancellation at term end
73
+ # when end_of_term is passed as true. If no options are passed the
74
+ # default configured value for end_of_term is taken
75
+ # * *Args* :
76
+ # - +options+ -> the options hash allowed for subscription cancellation in chargebee
77
+ # For more details on the *updating addon for subscription*, refer
78
+ # {Manage addons for subscription}[https://apidocs.chargebee.com/docs/api/subscriptions?lang=ruby#update_a_subscription]
79
+ #
80
+ def cancel(options={})
81
+ options[:end_of_term] ||= ChargebeeRails.configuration.end_of_term
82
+ chargebee_subscription = ChargeBee::Subscription.cancel(chargebee_id, options).subscription
83
+ update(status: chargebee_subscription.status)
84
+ end
85
+
86
+ # Stop a scheduled cancellation of a subscription
87
+ def stop_cancellation
88
+ chargebee_subscription = ChargeBee::Subscription.remove_scheduled_cancellation(chargebee_id).subscription
89
+ update(status: chargebee_subscription.status)
90
+ end
91
+
92
+ #
93
+ # Estimates the subscription's renewal
94
+ # * *Args* :
95
+ # - +options+ -> the options hash allowed for renewal estimate in chargebee
96
+ # * *Returns* :
97
+ # - the chargebee estimate
98
+ # For more details on the *options for renewal of estimate*, refer
99
+ # {Subscription renewal estimate}[https://apidocs.chargebee.com/docs/api/estimates?lang=ruby#subscription_renewal_estimate]
100
+ #
101
+ def estimate_renewal(options={})
102
+ options[:include_delayed_charges] ||= ChargebeeRails.configuration.include_delayed_charges[:renewal_estimate]
103
+ ChargeBee::Estimate.renewal_estimate(chargebee_id, options).estimate
104
+ end
105
+
106
+ module ClassMethods
107
+ #
108
+ # Estimates the cost of subscribing to a new subscription
109
+ # * *Args* :
110
+ # - +estimation_params+ -> the estimation options permitted for estimating
111
+ # new subscription in chargebee
112
+ # * *Returns* :
113
+ # - the chargebee estimate
114
+ # For more details on the *estimation params for new subscription estimate*, refer
115
+ # {Create subscription estimate}[https://apidocs.chargebee.com/docs/api/estimates?lang=ruby#create_subscription_estimate]
116
+ #
117
+ def estimate(estimation_params)
118
+ estimation_params[:trial_end] ||= 0
119
+ ::ChargeBee::Estimate.create_subscription(estimation_params).estimate
120
+ end
121
+
122
+ # Estimates the cost of changes to an existing subscription
123
+ # estimates the upgrade/downgrade or other changes
124
+ # * *Args* :
125
+ # - +estimation_params+ -> the estimation options permitted for estimating
126
+ # subscription update in chargebee
127
+ # * *Returns* :
128
+ # - the chargebee estimate
129
+ # For more details on the *estimation params for subscription update estimate*, refer
130
+ # {Update subscription estimate}[https://apidocs.chargebee.com/docs/api/estimates?lang=ruby#update_subscription_estimate]
131
+ #
132
+ def estimate_changes(estimation_params)
133
+ estimation_params[:include_delayed_charges] ||= ChargebeeRails.configuration.include_delayed_charges[:changes_estimate]
134
+ estimation_params[:prorate] ||= ChargebeeRails.configuration.proration
135
+ ::ChargeBee::Estimate.update_subscription(estimation_params).estimate
136
+ end
137
+
138
+ end
139
+
140
+ private
141
+
142
+ def subscription_attrs(subscription, plan)
143
+ {
144
+ chargebee_id: subscription.id,
145
+ plan_id: plan.id,
146
+ plan_quantity: subscription.plan_quantity,
147
+ status: subscription.status,
148
+ chargebee_data: chargebee_subscription_data(subscription)
149
+ }
150
+ end
151
+
152
+ def chargebee_subscription_data subscription
153
+ {
154
+ trial_ends_at: subscription.trial_end,
155
+ next_renewal_at: subscription.current_term_end,
156
+ cancelled_at: subscription.cancelled_at,
157
+ is_scheduled_for_cancel: (subscription.status == 'non-renewing' ? true : false),
158
+ has_scheduled_changes: subscription.has_scheduled_changes
159
+ }
160
+ end
161
+
162
+ end
163
+ end
@@ -0,0 +1,162 @@
1
+ module ChargebeeRails
2
+ class SubscriptionBuilder
3
+
4
+ def initialize(customer, options)
5
+ @customer = customer
6
+ @options = options
7
+ end
8
+
9
+ # Create a subscription in Chargebee,
10
+ # update the resulting subscription details for the customer in the
11
+ # application and finally return the subscription
12
+ def create
13
+ build_subscription_payload
14
+ create_subscriptions
15
+ manage_payment_method if chargebee_payment_method.present?
16
+ @subscription
17
+ end
18
+
19
+ # Update existing subscription in Chargebee,
20
+ # the resulting subscription details are then updated in application
21
+ def update
22
+ update_subscriptions
23
+ manage_payment_method if chargebee_payment_method.present?
24
+ @subscription
25
+ end
26
+
27
+ private
28
+
29
+ # Create a subscription in Chargebee with the passed options payload
30
+ def create_subscriptions
31
+ @result = ChargeBee::Subscription.create(@options)
32
+ @customer.update(
33
+ chargebee_id: chargebee_customer.id,
34
+ chargebee_data: chargebee_customer_data
35
+ ) # Update the chargebee customer id for the subscription owner
36
+ @subscription = @customer.create_subscription(subscription_attrs) # Create an active record subscription of the chargebee subscription object for the customer
37
+ end
38
+
39
+ # Update subscription in ChargeBee and the application model
40
+ def update_subscriptions
41
+ @subscription = @customer.subscription
42
+ @options[:prorate] ||= ChargebeeRails.configuration.proration
43
+ @result = ChargeBee::Subscription.update(@subscription.chargebee_id, @options)
44
+ @plan = Plan.find_by(plan_id: @result.subscription.plan_id)
45
+ @subscription.update(subscription_attrs)
46
+ end
47
+
48
+ # Update payment method for subscrption if one exists or create new one
49
+ def manage_payment_method
50
+ @subscription.payment_method.present? &&
51
+ @subscription.payment_method.update(payment_method_attrs) ||
52
+ create_payment_method
53
+ end
54
+
55
+ # Create the payment method for the subscription
56
+ def create_payment_method
57
+ @subscription.create_payment_method(payment_method_attrs)
58
+ end
59
+
60
+ # Check for the default plan if one is not passed in the options payload
61
+ # raise plan not configured error incase plan is not passed and a default
62
+ # plan is not set in the ChargebeeRails configuration.
63
+ # Raise plan not found if the plan passed is not found in active record
64
+ def build_subscription_payload
65
+ @options[:trial_end] = 0 if @options[:skip_trial]
66
+ @options[:plan_id] ||= ChargebeeRails.configuration.default_plan_id
67
+ raise PlanError.new.plan_not_configureed unless @options[:plan_id]
68
+ @plan = Plan.find_by(plan_id: @options[:plan_id])
69
+ raise PlanError.new.plan_not_found unless @plan
70
+ end
71
+
72
+ def chargebee_subscription
73
+ @chargebee_subscription ||= @result.subscription
74
+ end
75
+
76
+ def chargebee_customer
77
+ @chargebee_customer ||= @result.customer
78
+ end
79
+
80
+ def chargebee_payment_method
81
+ @chargebee_payment_method ||= chargebee_customer.payment_method
82
+ end
83
+
84
+ def chargebee_card
85
+ @chargebee_card ||= @result.card
86
+ end
87
+
88
+ def chargebee_billing_address
89
+ @chargebee_billing_address ||= chargebee_customer.billing_address
90
+ end
91
+
92
+ def subscription_attrs
93
+ {
94
+ chargebee_id: chargebee_subscription.id,
95
+ status: chargebee_subscription.status,
96
+ plan_quantity: chargebee_subscription.plan_quantity,
97
+ chargebee_data: chargebee_subscription_data,
98
+ plan: @plan
99
+ }
100
+ end
101
+
102
+ def chargebee_subscription_data
103
+ {
104
+ trial_ends_at: chargebee_subscription.trial_end,
105
+ next_renewal_at: chargebee_subscription.current_term_end,
106
+ cancelled_at: chargebee_subscription.cancelled_at,
107
+ is_scheduled_for_cancel: (chargebee_subscription.status == 'non-renewing' ? true : false),
108
+ has_scheduled_changes: chargebee_subscription.has_scheduled_changes
109
+ }
110
+ end
111
+
112
+ def chargebee_customer_data
113
+ {
114
+ customer_details: customer_details(chargebee_customer),
115
+ billing_address: billing_address(chargebee_customer.billing_address)
116
+ }
117
+ end
118
+
119
+ def customer_details customer
120
+ {
121
+ first_name: customer.first_name,
122
+ last_name: customer.last_name,
123
+ email: customer.email,
124
+ company: customer.company,
125
+ vat_number: customer.vat_number
126
+ }
127
+ end
128
+
129
+ def billing_address customer_billing_address
130
+ {
131
+ first_name: customer_billing_address.first_name,
132
+ last_name: customer_billing_address.last_name,
133
+ company: customer_billing_address.company,
134
+ address_line1: customer_billing_address.line1,
135
+ address_line2: customer_billing_address.line2,
136
+ address_line3: customer_billing_address.line3,
137
+ city: customer_billing_address.city,
138
+ state: customer_billing_address.state,
139
+ country: customer_billing_address.country,
140
+ zip: customer_billing_address.zip
141
+ } if customer_billing_address.present?
142
+ end
143
+
144
+ def payment_method_attrs
145
+ if chargebee_payment_method.type == 'card'
146
+ card_last4, card_type = chargebee_card.last4, chargebee_card.card_type
147
+ else
148
+ card_last4, card_type = nil, nil
149
+ end
150
+ {
151
+ cb_customer_id: chargebee_customer.id,
152
+ auto_collection: chargebee_customer.auto_collection,
153
+ payment_type: chargebee_payment_method.type,
154
+ reference_id: chargebee_payment_method.reference_id,
155
+ card_last4: card_last4,
156
+ card_type: card_type,
157
+ status: chargebee_payment_method.status
158
+ }
159
+ end
160
+
161
+ end
162
+ end
@@ -0,0 +1,4 @@
1
+ # :nodoc:all
2
+ module ChargebeeRails
3
+ VERSION = "0.1.3"
4
+ end
@@ -0,0 +1,175 @@
1
+ # :nodoc:all
2
+ module ChargebeeRails
3
+ module WebhookHandler
4
+
5
+ # Handle the ChargeBee event retrieved from webhook and call the
6
+ # corresponding event type handler for the event
7
+ def handle(chargebee_event)
8
+ @chargebee_event = chargebee_event
9
+ sync_events_list.include?(event.event_type) ? sync_events : send(event.event_type)
10
+ end
11
+
12
+ # Set event as ChargeBee event
13
+ def event
14
+ @event ||= @chargebee_event
15
+ end
16
+
17
+ # All the event types in ChargeBee
18
+
19
+ def customer_created; end
20
+
21
+ def customer_changed; end
22
+
23
+ def customer_deleted; end
24
+
25
+ def subscription_created; end
26
+
27
+ def subscription_started; end
28
+
29
+ def subscription_trial_end_reminder; end
30
+
31
+ def subscription_activated; end
32
+
33
+ def subscription_changed; end
34
+
35
+ def subscription_cancellation_scheduled; end
36
+
37
+ def subscription_cancellation_reminder; end
38
+
39
+ def subscription_cancelled; end
40
+
41
+ def subscription_reactivated; end
42
+
43
+ def subscription_renewed; end
44
+
45
+ def subscription_scheduled_cancellation_removed; end
46
+
47
+ def subscription_shipping_address_updated; end
48
+
49
+ def subscription_deleted; end
50
+
51
+ def pending_invoice_created
52
+ ::ChargebeeRails::MeteredBilling.close_invoice(@event.content.invoice.id)
53
+ end
54
+
55
+ def invoice_generated; end
56
+
57
+ def invoice_updated; end
58
+
59
+ def invoice_deleted; end
60
+
61
+ def credit_note_created; end
62
+
63
+ def credit_note_updated; end
64
+
65
+ def credit_note_deleted; end
66
+
67
+ def subscription_renewal_reminder; end
68
+
69
+ def transaction_created; end
70
+
71
+ def transaction_updated; end
72
+
73
+ def transaction_deleted; end
74
+
75
+ def payment_succeeded; end
76
+
77
+ def payment_failed; end
78
+
79
+ def payment_refunded; end
80
+
81
+ def payment_initiated; end
82
+
83
+ def refund_initiated; end
84
+
85
+ def card_added; end
86
+
87
+ def card_updated; end
88
+
89
+ def card_expiry_reminder; end
90
+
91
+ def card_expired; end
92
+
93
+ def card_deleted; end
94
+
95
+ private
96
+
97
+ def sync_events_list
98
+ %w(
99
+ card_expired
100
+ card_updated
101
+ card_expiry_reminder
102
+ subscription_started
103
+ subscription_trial_end_reminder
104
+ subscription_activated
105
+ subscription_changed
106
+ subscription_cancellation_scheduled
107
+ subscription_cancellation_reminder
108
+ subscription_cancelled
109
+ subscription_reactivated
110
+ subscription_renewed
111
+ subscription_scheduled_cancellation_removed
112
+ subscription_renewal_reminder
113
+ )
114
+ end
115
+
116
+ def sync_events
117
+ sync(existing_subscription, subscription_attrs(event.content.subscription)) if event.event_type.include?('subscription') && can_sync?(existing_subscription)
118
+ sync(existing_payment_method, payment_method_attrs(event.content.customer, event.content.card)) if event.event_type.include?('card') && event.content.customer.payment_method.present? && can_sync?(existing_payment_method)
119
+ end
120
+
121
+ def sync obj, attrs
122
+ obj.update_all(attrs)
123
+ send(event.event_type)
124
+ end
125
+
126
+ def existing_subscription
127
+ @existing_subscription ||= ::Subscription.where(chargebee_id: event.content.subscription.id)
128
+ end
129
+
130
+ def existing_payment_method
131
+ @existing_payment_method ||= ::PaymentMethod.where(cb_customer_id: event.content.customer.id)
132
+ end
133
+
134
+ def can_sync? obj
135
+ obj.first && (obj.first.event_last_modified_at.to_i < event.occurred_at)
136
+ end
137
+
138
+ def subscription_attrs subscription
139
+ {
140
+ chargebee_id: subscription.id,
141
+ plan_id: ::Plan.find_by(plan_id: subscription.plan_id).id,
142
+ plan_quantity: subscription.plan_quantity,
143
+ status: subscription.status,
144
+ event_last_modified_at: event.occurred_at,
145
+ updated_at: Time.now,
146
+ chargebee_data: chargebee_subscription_data(subscription)
147
+ }
148
+ end
149
+
150
+ def chargebee_subscription_data subscription
151
+ {
152
+ trial_ends_at: subscription.trial_end,
153
+ next_renewal_at: subscription.current_term_end,
154
+ cancelled_at: subscription.cancelled_at,
155
+ is_scheduled_for_cancel: (subscription.status == 'non-renewing' ? true : false),
156
+ has_scheduled_changes: subscription.has_scheduled_changes
157
+ }
158
+ end
159
+
160
+ def payment_method_attrs customer, card
161
+ {
162
+ cb_customer_id: customer.id,
163
+ auto_collection: customer.auto_collection,
164
+ payment_type: customer.payment_method.type,
165
+ reference_id: customer.payment_method.reference_id,
166
+ card_last4: card.last4,
167
+ card_type: card.card_type,
168
+ status: customer.payment_method.status,
169
+ event_last_modified_at: event.occurred_at,
170
+ updated_at: Time.now
171
+ }
172
+ end
173
+
174
+ end
175
+ end