diff options
Diffstat (limited to 'lib')
9 files changed, 200 insertions, 1 deletions
diff --git a/lib/generators/solidus_subscriptions/install/templates/initializer.rb b/lib/generators/solidus_subscriptions/install/templates/initializer.rb index 17a6f51..e1e9731 100644 --- a/lib/generators/solidus_subscriptions/install/templates/initializer.rb +++ b/lib/generators/solidus_subscriptions/install/templates/initializer.rb @@ -64,4 +64,18 @@ SolidusSubscriptions.configure do |config| # :interval_units, # :end_date, # ] + + # ========================================= Churn Buster ========================================= + # + # This extension can integrate with Churn Buster for churn mitigation and failed payment recovery. + # If you want to integrate with Churn Buster, simply configure your credentials below. + # + # NOTE: If you integrate with Churn Buster and override any of the handlers, make sure to call + # `super` or copy-paste the original integration code or things won't work! + + # Your Churn Buster account ID. + # config.churn_buster_account_id = 'YOUR_CHURN_BUSTER_ACCOUNT_ID' + + # Your Churn Buster API key. + # config.churn_buster_api_key = 'YOUR_CHURN_BUSTER_API_KEY' end diff --git a/lib/solidus_subscriptions.rb b/lib/solidus_subscriptions.rb index 039b172..e9dae93 100644 --- a/lib/solidus_subscriptions.rb +++ b/lib/solidus_subscriptions.rb @@ -4,6 +4,7 @@ require 'solidus_core' require 'solidus_support' require 'deface' +require 'httparty' require 'state_machines' require 'solidus_subscriptions/configuration' @@ -11,6 +12,12 @@ require 'solidus_subscriptions/permission_sets/default_customer' require 'solidus_subscriptions/permission_sets/subscription_management' require 'solidus_subscriptions/version' require 'solidus_subscriptions/engine' +require 'solidus_subscriptions/churn_buster/client' +require 'solidus_subscriptions/churn_buster/serializer' +require 'solidus_subscriptions/churn_buster/subscription_customer_serializer' +require 'solidus_subscriptions/churn_buster/subscription_payment_method_serializer' +require 'solidus_subscriptions/churn_buster/subscription_serializer' +require 'solidus_subscriptions/churn_buster/order_serializer' module SolidusSubscriptions class << self @@ -21,5 +28,14 @@ module SolidusSubscriptions def configuration @configuration ||= Configuration.new end + + def churn_buster + return unless configuration.churn_buster? + + @churn_buster ||= ChurnBuster::Client.new( + account_id: SolidusSubscriptions.configuration.churn_buster_account_id, + api_key: SolidusSubscriptions.configuration.churn_buster_api_key, + ) + end end end diff --git a/lib/solidus_subscriptions/churn_buster/client.rb b/lib/solidus_subscriptions/churn_buster/client.rb new file mode 100644 index 0000000..915bb40 --- /dev/null +++ b/lib/solidus_subscriptions/churn_buster/client.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module SolidusSubscriptions + module ChurnBuster + class Client + BASE_API_URL = 'https://api.churnbuster.io/v1' + + attr_reader :account_id, :api_key + + def initialize(account_id:, api_key:) + @account_id = account_id + @api_key = api_key + end + + def report_failed_payment(order) + post('/failed_payments', OrderSerializer.serialize(order)) + end + + def report_successful_payment(order) + post('/successful_payments', OrderSerializer.serialize(order)) + end + + def report_subscription_cancellation(subscription) + post('/cancellations', SubscriptionSerializer.serialize(subscription)) + end + + def report_payment_method_change(subscription) + post('/payment_methods', SubscriptionPaymentMethodSerializer.serialize(subscription)) + end + + private + + def post(path, body) + HTTParty.post( + "#{BASE_API_URL}#{path}", + body: body.to_json, + headers: { + 'Content-Type' => 'application/json', + }, + basic_auth: { + username: account_id, + password: api_key, + }, + ) + end + end + end +end diff --git a/lib/solidus_subscriptions/churn_buster/order_serializer.rb b/lib/solidus_subscriptions/churn_buster/order_serializer.rb new file mode 100644 index 0000000..5bc5dee --- /dev/null +++ b/lib/solidus_subscriptions/churn_buster/order_serializer.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module SolidusSubscriptions + module ChurnBuster + class OrderSerializer < Serializer + def to_h + { + payment: { + source: 'in_house', + source_id: object.number, + amount_in_cents: object.display_total.cents, + currency: object.currency, + }, + customer: SubscriptionCustomerSerializer.serialize(object.subscription), + } + end + end + end +end diff --git a/lib/solidus_subscriptions/churn_buster/serializer.rb b/lib/solidus_subscriptions/churn_buster/serializer.rb new file mode 100644 index 0000000..d8b83bb --- /dev/null +++ b/lib/solidus_subscriptions/churn_buster/serializer.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module SolidusSubscriptions + module ChurnBuster + class Serializer + attr_reader :object + + class << self + def serialize(object) + new(object).to_h + end + end + + def initialize(object) + @object = object + end + + def to_h + raise NotImplementedError + end + end + end +end diff --git a/lib/solidus_subscriptions/churn_buster/subscription_customer_serializer.rb b/lib/solidus_subscriptions/churn_buster/subscription_customer_serializer.rb new file mode 100644 index 0000000..700c1c6 --- /dev/null +++ b/lib/solidus_subscriptions/churn_buster/subscription_customer_serializer.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module SolidusSubscriptions + module ChurnBuster + class SubscriptionCustomerSerializer < Serializer + def to_h + { + source: 'in_house', + source_id: object.id, + email: object.user.email, + properties: { + first_name: object.shipping_address_to_use.firstname, + last_name: object.shipping_address_to_use.lastname, + }, + } + end + end + end +end diff --git a/lib/solidus_subscriptions/churn_buster/subscription_payment_method_serializer.rb b/lib/solidus_subscriptions/churn_buster/subscription_payment_method_serializer.rb new file mode 100644 index 0000000..984059d --- /dev/null +++ b/lib/solidus_subscriptions/churn_buster/subscription_payment_method_serializer.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module SolidusSubscriptions + module ChurnBuster + class SubscriptionPaymentMethodSerializer < Serializer + def to_h + { + payment_method: { + source: 'in_house', + source_id: [ + object.payment_method_to_use&.id, + object.payment_source_to_use&.id + ].compact.join('-'), + type: 'card', + properties: payment_source_properties, + }, + customer: SubscriptionCustomerSerializer.serialize(object), + } + end + + private + + def payment_source_properties + if object.payment_source.is_a?(::Spree::CreditCard) + { + brand: object.payment_source.cc_type, + last4: object.payment_source.last_digits, + exp_month: object.payment_source.month, + exp_year: object.payment_source.year, + } + else + {} + end + end + end + end +end diff --git a/lib/solidus_subscriptions/churn_buster/subscription_serializer.rb b/lib/solidus_subscriptions/churn_buster/subscription_serializer.rb new file mode 100644 index 0000000..06c08d1 --- /dev/null +++ b/lib/solidus_subscriptions/churn_buster/subscription_serializer.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module SolidusSubscriptions + module ChurnBuster + class SubscriptionSerializer < Serializer + def to_h + { + subscription: { + source: 'in_house', + source_id: object.id + }, + customer: SubscriptionCustomerSerializer.serialize(object), + } + end + end + end +end diff --git a/lib/solidus_subscriptions/configuration.rb b/lib/solidus_subscriptions/configuration.rb index d5dc80f..22b7d84 100644 --- a/lib/solidus_subscriptions/configuration.rb +++ b/lib/solidus_subscriptions/configuration.rb @@ -2,7 +2,9 @@ module SolidusSubscriptions class Configuration - attr_accessor :maximum_total_skips + attr_accessor( + :maximum_total_skips, :churn_buster_account_id, :churn_buster_api_key, + ) attr_writer( :success_dispatcher_class, :failure_dispatcher_class, :payment_failed_dispatcher_class, @@ -73,5 +75,9 @@ module SolidusSubscriptions @subscribable_class ||= 'Spree::Variant' @subscribable_class.constantize end + + def churn_buster? + churn_buster_account_id.present? && churn_buster_api_key.present? + end end end |