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.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ # Default directory to look in is `/specs`
5
+ # Run with `rake spec`
6
+ RSpec::Core::RakeTask.new(:spec) do |task|
7
+ task.rspec_opts = ['--color', '--format', 'nested']
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,40 @@
1
+ module ChargebeeRails
2
+ class WebhooksController < ActionController::Base
3
+ include WebhookHandler
4
+ before_filter :authenticate, if: "ChargebeeRails.configuration.secure_webhook_api"
5
+
6
+ # Handle ChargeBee webhook events
7
+ # From the post request received from chargebee, the event for which the
8
+ # webhook is triggered is found from the id parameter sent.
9
+ # The event is then handled by the WebhookHandler module.
10
+ # * *Raises* :
11
+ # - +ChargebeeRails::Error+ -> If event is not valid or if event unprocessable
12
+ def handle_event
13
+ event = ChargeBee::Event.retrieve(params[:id]).event
14
+ handle(event)
15
+ head :ok
16
+ rescue ChargebeeRails::Error => e
17
+ log_errors(e)
18
+ head :internal_server_error
19
+ end
20
+
21
+ private
22
+
23
+ def log_errors(e)
24
+ logger.error e.message
25
+ e.backtrace.each { |line| logger.error " #{line}" }
26
+ end
27
+
28
+ # Basic http authentication for ChargeBee webhook apis
29
+ # The username and password used to secure the webhook at chargebee
30
+ # is compared with the configured webhook_authentication user and
31
+ # secret in the application.
32
+ def authenticate
33
+ authenticate_or_request_with_http_basic do |user, password|
34
+ user == ChargebeeRails.configuration.webhook_authentication[:user] &&
35
+ password == ChargebeeRails.configuration.webhook_authentication[:secret]
36
+ end
37
+ end
38
+
39
+ end
40
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ Rails.application.routes.draw do #:nodoc:
2
+ # ChargeBee webhook route
3
+ # This is the default route that the chargebee's webhooks reach the application
4
+ # and can be configured.
5
+ # The default webhook_api_path is +chargebee_rails_event+ - can be configured;
6
+ # and the default webhook_handler (webhook handling controller) is
7
+ # +chargebee_rails/webhooks+ - can be configured as well.
8
+ post ChargebeeRails.configuration.webhook_api_path => "#{ChargebeeRails.configuration.webhook_handler}#handle_event"
9
+ end
@@ -0,0 +1,119 @@
1
+ require "chargebee_rails/version"
2
+ require "chargebee"
3
+ require "rails"
4
+ require "chargebee_rails/engine"
5
+ require "chargebee_rails/webhook_handler"
6
+ require "chargebee_rails/configuration"
7
+ require "chargebee_rails/errors"
8
+ require "generators/chargebee_rails/install_generator"
9
+ require "chargebee_rails/subscription"
10
+ require "chargebee_rails/subscription_builder"
11
+ require "chargebee_rails/hosted_page_subscription_manager"
12
+ require "chargebee_rails/customer"
13
+ require "chargebee_rails/metered_billing"
14
+
15
+ module ChargebeeRails
16
+ # This method is used to update the chargebee customer and also reflects the
17
+ # changes to the subscription owner in the application.
18
+ # * *Args* :
19
+ # - +customer+ -> the subscription owner
20
+ # - +options+ -> the options hash allowed for customer update in chargebee
21
+ # For more details on the options hash, refer the input parameters for
22
+ # https://apidocs.chargebee.com/docs/api/customers?lang=ruby#update_a_customer
23
+ # * *Returns* :
24
+ # - the updated subscription owner
25
+ # * *Raises* :
26
+ # - +ChargeBee::InvalidRequestError+ -> If customer or options is invalid
27
+ #
28
+ def self.update_customer(customer, options={})
29
+ chargebee_customer = ChargeBee::Customer.update(customer.chargebee_id, options).customer
30
+ customer.update(chargebee_id: chargebee_customer.id, chargebee_data: chargebee_customer_data(chargebee_customer))
31
+ customer
32
+ end
33
+
34
+ # Update the billing information of the chargebee customer. The changes in the
35
+ # billing details are also reflected in the subscription owner's
36
+ # +chargebee_customer_data+
37
+ # * *Args* :
38
+ # - +customer+ -> the subscription owner
39
+ # - +options+ -> the options hash allowed for updating customer billing info in chargebee
40
+ # For more details on the options hash, refer the input parameters for
41
+ # https://apidocs.chargebee.com/docs/api/customers?lang=ruby#update_billing_info_for_a_customer
42
+ # * *Returns* :
43
+ # - the updated subscription owner
44
+ # * *Raises* :
45
+ # - +ChargeBee::InvalidRequestError+ -> If customer or options is invalid
46
+ #
47
+ def self.update_billing_info(customer, options={})
48
+ chargebee_customer = ChargeBee::Customer.update_billing_info(customer.chargebee_id, options).customer
49
+ customer.update(chargebee_id: chargebee_customer.id, chargebee_data: chargebee_customer_data(chargebee_customer))
50
+ customer
51
+ end
52
+
53
+ # Add contacts to a chargebee customer, the subscription owner
54
+ # and the contact details hash is given as options.
55
+ # * *Args* :
56
+ # - +customer+ -> the subscription owner
57
+ # - +options+ -> the options hash allowed for adding contacts to customer in chargebee
58
+ # For more details on the options hash, refer the input parameters for
59
+ # https://apidocs.chargebee.com/docs/api/customers?lang=ruby#add_contacts_to_a_customer
60
+ # * *Returns* :
61
+ # - the updated chargebee customer
62
+ # * *Raises* :
63
+ # - +ChargeBee::InvalidRequestError+ -> If customer or options is invalid
64
+ #
65
+ def self.add_customer_contacts(customer, options={})
66
+ ChargeBee::Customer.add_contact(customer.chargebee_id, options).customer
67
+ end
68
+
69
+ # Update the contacts for a chargebee customer - the chargebee contact id
70
+ # must be passed in the options to update the existing contact for the
71
+ # subscription owner
72
+ # * *Args* :
73
+ # - +customer+ -> the subscription owner
74
+ # - +options+ -> the options hash allowed for updating customer contacts in chargebee
75
+ # For more details on the options hash, refer the input parameters for
76
+ # https://apidocs.chargebee.com/docs/api/customers?lang=ruby#update_contacts_for_a_customer
77
+ # * *Returns* :
78
+ # - the updated chargebee customer
79
+ # * *Raises* :
80
+ # - +ChargeBee::InvalidRequestError+ -> If customer or options is invalid
81
+ #
82
+ def self.update_customer_contacts(customer, options={})
83
+ ChargeBee::Customer.update_contact(customer.chargebee_id, options).customer
84
+ end
85
+
86
+ private
87
+
88
+ def chargebee_customer_data customer
89
+ {
90
+ customer_details: customer_details(customer),
91
+ billing_address: billing_address(customer.billing_address)
92
+ }
93
+ end
94
+
95
+ def customer_details customer
96
+ {
97
+ first_name: customer.first_name,
98
+ last_name: customer.last_name,
99
+ email: customer.email,
100
+ company: customer.company,
101
+ vat_number: customer.vat_number
102
+ }
103
+ end
104
+
105
+ def billing_address customer_billing_address
106
+ {
107
+ first_name: customer_billing_address.first_name,
108
+ last_name: customer_billing_address.last_name,
109
+ company: customer_billing_address.company,
110
+ address_line1: customer_billing_address.line1,
111
+ address_line2: customer_billing_address.line2,
112
+ address_line3: customer_billing_address.line3,
113
+ city: customer_billing_address.city,
114
+ state: customer_billing_address.state,
115
+ country: customer_billing_address.country,
116
+ zip: customer_billing_address.zip
117
+ } if customer_billing_address.present?
118
+ end
119
+ end
@@ -0,0 +1,47 @@
1
+ # :nodoc: all
2
+ module ChargebeeRails
3
+
4
+ def self.configuration
5
+ @configuration ||= Configuration.new
6
+ end
7
+
8
+ def self.setup
9
+ ::ChargeBee.configure(
10
+ site: configuration.chargebee_site,
11
+ api_key: configuration.chargebee_api_key
12
+ )
13
+ end
14
+
15
+ def self.configure
16
+ yield configuration
17
+ setup
18
+ end
19
+
20
+ class Configuration
21
+ attr_accessor :default_plan_id
22
+ attr_accessor :end_of_term
23
+ attr_accessor :proration
24
+ attr_accessor :include_delayed_charges
25
+ attr_accessor :chargebee_site
26
+ attr_accessor :chargebee_api_key
27
+ attr_accessor :currency
28
+ attr_accessor :webhook_handler
29
+ attr_accessor :webhook_api_path
30
+ attr_accessor :secure_webhook_api
31
+ attr_accessor :webhook_authentication
32
+
33
+ def initialize
34
+ @default_plan_id = nil
35
+ @end_of_term = false
36
+ @proration = true
37
+ @include_delayed_charges = {changes_estimate: false, renewal_estimate: true}
38
+ @webhook_handler = 'chargebee_rails/webhooks'
39
+ @webhook_api_path = 'chargebee_rails_event'
40
+ @secure_webhook_api = false
41
+ @webhook_authentication = {user: nil, secret: nil}
42
+ @chargebee_site = nil
43
+ @chargebee_api_key = nil
44
+ @currency = "US Dollars [USD]"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,85 @@
1
+ module ChargebeeRails
2
+ module Customer
3
+
4
+ # Subscribe customer to a new subscription in chargebee.
5
+ # * *Args* :
6
+ # - +options+ -> the options hash allowed for subscription update in chargebee
7
+ # For more details on the options hash, refer the input parameters for
8
+ # https://apidocs.chargebee.com/docs/api/subscriptions?lang=ruby#create_a_subscription
9
+ # * *Returns* :
10
+ # - the subscription
11
+ # * *Raises* :
12
+ # - +ChargeBee::InvalidRequestError+ -> If subscription options is invalid
13
+ #
14
+ def subscribe(options={})
15
+ SubscriptionBuilder.new(self, options).create
16
+ end
17
+
18
+ # Subscribe customer to a new subscription in chargebee via chargebee's hosted page.
19
+ # * *Args* :
20
+ # - +hosted_page+ -> the +hosted_page+ returned by chargebee
21
+ # The subscription for the customer is created from the +hosted_page+
22
+ # returned by chargebee. This +hosted_page+ object contains the details
23
+ # about the subscription in chargebee for the customer. For more on +hosted_page+
24
+ # https://apidocs.chargebee.com/docs/api/hosted_pages?lang=ruby#checkout_new_subscription
25
+ # * *Returns* :
26
+ # - the subscription
27
+ # * *Raises* :
28
+ # - +ChargeBee::InvalidRequestError+ -> If +hosted_page+ is invalid
29
+ #
30
+ def subscribe_via_hosted_page(hosted_page)
31
+ HostedPageSubscriptionManager.new(self, hosted_page).create
32
+ end
33
+
34
+ # Subscribe customer to a new subscription in chargebee via chargebee's hosted page.
35
+ # * *Args* :
36
+ # - +hosted_page+ -> the +hosted_page+ returned by chargebee
37
+ # The subscription for the customer is updated from the +hosted_page+
38
+ # returned by chargebee. This +hosted_page+ object contains the details
39
+ # about the updated subscription in chargebee for the customer.
40
+ # * *Returns* :
41
+ # - the subscription
42
+ # * *Raises* :
43
+ # - +ChargeBee::InvalidRequestError+ -> If +hosted_page+ is invalid
44
+ #
45
+ def update_subscription_via_hosted_page(hosted_page)
46
+ HostedPageSubscriptionManager.new(self, hosted_page).update
47
+ end
48
+
49
+ # Update the customer's subscription
50
+ # * *Args* :
51
+ # - +options+ -> the options hash allowed for subscription update in chargebee
52
+ # For more details on the options hash, refer the input parameters for
53
+ # https://apidocs.chargebee.com/docs/api/subscriptions?lang=ruby#update_a_subscription
54
+ # * *Returns* :
55
+ # - the updated subscription
56
+ # * *Raises* :
57
+ # - +ChargeBee::InvalidRequestError+ -> If subscription or options is invalid
58
+ #
59
+ def update_subscription(options={})
60
+ SubscriptionBuilder.new(self, options).update
61
+ end
62
+
63
+ # Retrieve the chargebee customer of the subscription owner -
64
+ # i.e subscription owner as chargebee customer
65
+ # * *Returns* :
66
+ # - the chargebee customer
67
+ # * *Raises* :
68
+ # - +ChargeBee::InvalidRequestError+ -> If subscription owner is invalid
69
+ #
70
+ def as_chargebee_customer
71
+ ChargeBee::Customer.retrieve(chargebee_id).customer
72
+ end
73
+
74
+ # List all invoices for the subscription owner (customer).
75
+ # * *Returns* :
76
+ # - the list of invoices for the customer in chargebee
77
+ # * *Raises* :
78
+ # - +ChargeBee::InvalidRequestError+ -> If customer is invalid
79
+ #
80
+ def invoices
81
+ ChargeBee::Invoice.invoices_for_customer(chargebee_id).map(&:invoice)
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,6 @@
1
+ # :nodoc: all
2
+ module ChargebeeRails
3
+ class EventEngine < ::Rails::Engine
4
+ # isolate_namespace ChargebeeRails
5
+ end
6
+ end
@@ -0,0 +1,26 @@
1
+ # :nodoc: all
2
+ module ChargebeeRails
3
+
4
+ class Error < StandardError
5
+ attr_reader :original_error
6
+
7
+ def initialize(message=nil, original_error=nil)
8
+ super message
9
+ @original_error = original_error
10
+ end
11
+ end
12
+
13
+ class PlanError < Error
14
+ # Raise this error when the plan is not present in active_record
15
+ def plan_not_found
16
+ "Plan Not Found"
17
+ end
18
+
19
+ # Raise this error when the plan is not setup in ChargeBee
20
+ def plan_not_configured
21
+ "Plan Not Configured"
22
+ end
23
+ end
24
+
25
+ class UnauthorizedError < Error; end
26
+ end
@@ -0,0 +1,134 @@
1
+ module ChargebeeRails
2
+ class HostedPageSubscriptionManager
3
+
4
+ def initialize(customer, hosted_page)
5
+ @customer = customer
6
+ @hosted_page = hosted_page
7
+ end
8
+
9
+ # Create a subscription for the customer in application,
10
+ # from the subscription details got from chargebee's hosted page
11
+ def create
12
+ @customer.update(
13
+ chargebee_id: hosted_customer.id,
14
+ chargebee_data: chargebee_customer_data
15
+ )
16
+ @subscription = @customer.create_subscription(subscription_attrs)
17
+ manage_payment_method if hosted_payment_method.present?
18
+ @subscription
19
+ end
20
+
21
+ # Update the subscription for the customer in application,
22
+ # from the subscription details got from chargebee's hosted page
23
+ def update
24
+ @subscription = @customer.subscription
25
+ @subscription.update(subscription_attrs)
26
+ manage_payment_method if hosted_payment_method.present?
27
+ @subscription
28
+ end
29
+
30
+ private
31
+
32
+ # Update payment method for subscrption if one exists or create new one
33
+ def manage_payment_method
34
+ @subscription.payment_method.present? &&
35
+ @subscription.payment_method.update(payment_method_attrs) ||
36
+ create_payment_method
37
+ end
38
+
39
+ # Create the payment method for the subscription
40
+ def create_payment_method
41
+ @subscription.create_payment_method(payment_method_attrs)
42
+ end
43
+
44
+ def hosted_subscription
45
+ @hosted_subscription ||= @hosted_page.content.subscription
46
+ end
47
+
48
+ def hosted_customer
49
+ @hosted_customer ||= @hosted_page.content.customer
50
+ end
51
+
52
+ def hosted_payment_method
53
+ @hosted_payment_method ||= hosted_customer.payment_method
54
+ end
55
+
56
+ def hosted_card
57
+ @hosted_card ||= @hosted_page.content.card
58
+ end
59
+
60
+ def hosted_billing_address
61
+ @hosted_billing_address ||= @hosted_customer.billing_address
62
+ end
63
+
64
+ def subscription_attrs
65
+ {
66
+ chargebee_id: hosted_subscription.id,
67
+ status: hosted_subscription.status,
68
+ plan_quantity: hosted_subscription.plan_quantity,
69
+ chargebee_data: chargebee_subscription_data,
70
+ plan: Plan.find_by(plan_id: hosted_subscription.plan_id)
71
+ }
72
+ end
73
+
74
+ def chargebee_subscription_data
75
+ {
76
+ trial_ends_at: hosted_subscription.trial_end,
77
+ next_renewal_at: hosted_subscription.current_term_end,
78
+ cancelled_at: hosted_subscription.cancelled_at,
79
+ is_scheduled_for_cancel: (hosted_subscription.status == 'non-renewing' ? true : false),
80
+ has_scheduled_changes: hosted_subscription.has_scheduled_changes
81
+ }
82
+ end
83
+
84
+ def chargebee_customer_data
85
+ {
86
+ customer_details: customer_details(hosted_customer),
87
+ billing_address: billing_address(hosted_customer.billing_address)
88
+ }
89
+ end
90
+
91
+ def customer_details customer
92
+ {
93
+ first_name: customer.first_name,
94
+ last_name: customer.last_name,
95
+ email: customer.email,
96
+ company: customer.company,
97
+ vat_number: customer.vat_number
98
+ }
99
+ end
100
+
101
+ def billing_address customer_billing_address
102
+ {
103
+ first_name: customer_billing_address.first_name,
104
+ last_name: customer_billing_address.last_name,
105
+ company: customer_billing_address.company,
106
+ address_line1: customer_billing_address.line1,
107
+ address_line2: customer_billing_address.line2,
108
+ address_line3: customer_billing_address.line3,
109
+ city: customer_billing_address.city,
110
+ state: customer_billing_address.state,
111
+ country: customer_billing_address.country,
112
+ zip: customer_billing_address.zip
113
+ } if customer_billing_address.present?
114
+ end
115
+
116
+ def payment_method_attrs
117
+ if hosted_payment_method.type == 'card'
118
+ card_last4, card_type = hosted_card.last4, hosted_card.card_type
119
+ else
120
+ card_last4, card_type = nil, nil
121
+ end
122
+ {
123
+ cb_customer_id: hosted_customer.id,
124
+ auto_collection: hosted_customer.auto_collection,
125
+ payment_type: hosted_payment_method.type,
126
+ reference_id: hosted_payment_method.reference_id,
127
+ card_last4: card_last4,
128
+ card_type: card_type,
129
+ status: hosted_payment_method.status
130
+ }
131
+ end
132
+
133
+ end
134
+ end