chargebee_rails 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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