include Killbill::Plugin::ActiveMerchant module Killbill #:nodoc: module Stripe #:nodoc: class PaymentPlugin < ::Killbill::Plugin::ActiveMerchant::PaymentPlugin def initialize gateway_builder = Proc.new do |config| ::ActiveMerchant::Billing::StripeGateway.new :login => config[:api_secret_key] end super(gateway_builder, :stripe, ::Killbill::Stripe::StripePaymentMethod, ::Killbill::Stripe::StripeTransaction, ::Killbill::Stripe::StripeResponse) end def on_event(event) # Require to deal with per tenant configuration invalidation super(event) # # Custom event logic could be added below... # end def authorize_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context) pm = @payment_method_model.from_kb_payment_method_id(kb_payment_method_id, context.tenant_id) options = {} populate_defaults(pm, amount, properties, context, options) properties = merge_properties(properties, options) super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context) end def capture_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context) # Pass extra parameters for the gateway here options = {} properties = merge_properties(properties, options) super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context) end def purchase_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context) pm = @payment_method_model.from_kb_payment_method_id(kb_payment_method_id, context.tenant_id) options = {} populate_defaults(pm, amount, properties, context, options) properties = merge_properties(properties, options) super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context) end def void_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, properties, context) # Pass extra parameters for the gateway here options = {} properties = merge_properties(properties, options) super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, properties, context) end def credit_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context) # Pass extra parameters for the gateway here options = {} properties = merge_properties(properties, options) super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context) end def refund_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context) # Pass extra parameters for the gateway here options = {} reverse_transfer = find_value_from_properties(properties, :reverse_transfer) options[:reverse_transfer] = ::Killbill::Plugin::ActiveMerchant::Utils.normalize(reverse_transfer) refund_application_fee = find_value_from_properties(properties, :refund_application_fee) options[:refund_application_fee] = ::Killbill::Plugin::ActiveMerchant::Utils.normalize(refund_application_fee) properties = merge_properties(properties, options) super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context) end def get_payment_info(kb_account_id, kb_payment_id, properties, context) # Pass extra parameters for the gateway here options = {} properties = merge_properties(properties, options) super(kb_account_id, kb_payment_id, properties, context) end def search_payments(search_key, offset, limit, properties, context) # Pass extra parameters for the gateway here options = {} properties = merge_properties(properties, options) super(search_key, offset, limit, properties, context) end def add_payment_method(kb_account_id, kb_payment_method_id, payment_method_props, set_default, properties, context) # Do we have a customer for that account already? stripe_customer_id = find_value_from_properties(payment_method_props.properties, :customer) || StripePaymentMethod.stripe_customer_id_from_kb_account_id(kb_account_id, context.tenant_id) email = find_value_from_properties(payment_method_props.properties, :email) || @kb_apis.account_user_api.get_account_by_id(kb_account_id, @kb_apis.create_context(context.tenant_id)).email # Pass extra parameters for the gateway here options = { :email => email, # This will either update the current customer if present, or create a new one :customer => stripe_customer_id, # Magic field, see also private_api.rb (works only when creating an account) :description => kb_account_id } properties = merge_properties(properties, options) super(kb_account_id, kb_payment_method_id, payment_method_props, set_default, properties, context) end def delete_payment_method(kb_account_id, kb_payment_method_id, properties, context) pm = StripePaymentMethod.from_kb_payment_method_id(kb_payment_method_id, context.tenant_id) # Pass extra parameters for the gateway here options = { :customer_id => pm.stripe_customer_id } properties = merge_properties(properties, options) super(kb_account_id, kb_payment_method_id, properties, context) end def get_payment_method_detail(kb_account_id, kb_payment_method_id, properties, context) # Pass extra parameters for the gateway here options = {} properties = merge_properties(properties, options) super(kb_account_id, kb_payment_method_id, properties, context) end def set_default_payment_method(kb_account_id, kb_payment_method_id, properties, context) pm = StripePaymentMethod.from_kb_payment_method_id(kb_payment_method_id, context.tenant_id) # Update the default payment method on the customer object options = properties_to_hash(properties) payment_processor_account_id = options[:payment_processor_account_id] || :default gateway = lookup_gateway(payment_processor_account_id, context.tenant_id) stripe_response = gateway.update_customer(pm.stripe_customer_id, :default_card => pm.token) response, transaction = save_response_and_transaction(stripe_response, :set_default_payment_method, kb_account_id, context.tenant_id, payment_processor_account_id) if response.success # TODO Update our records else raise response.message end end def get_payment_methods(kb_account_id, refresh_from_gateway, properties, context) # Pass extra parameters for the gateway here options = {} properties = merge_properties(properties, options) super(kb_account_id, refresh_from_gateway, properties, context) end def search_payment_methods(search_key, offset, limit, properties, context) # Pass extra parameters for the gateway here options = {} properties = merge_properties(properties, options) super(search_key, offset, limit, properties, context) end def reset_payment_methods(kb_account_id, payment_methods, properties, context) super end def build_form_descriptor(kb_account_id, descriptor_fields, properties, context) # Pass extra parameters for the gateway here options = {} properties = merge_properties(properties, options) # Add your custom static hidden tags here options = { #:token => config[:stripe][:token] } descriptor_fields = merge_properties(descriptor_fields, options) super(kb_account_id, descriptor_fields, properties, context) end def process_notification(notification_json, properties, context) notification = JSON.parse(notification_json) gw_response = ::ActiveMerchant::Billing::Response.new(true, nil, notification, :test => !notification['livemode'], :authorization => notification['request'], :avs_result => nil, :cvv_result => nil, :emv_authorization => nil, :error_code => nil) save_response_and_transaction(gw_response, "webhook.#{notification['type']}".to_sym, nil, context.tenant_id, nil) gw_notification = ::Killbill::Plugin::Model::GatewayNotification.new gw_notification.kb_payment_id = nil gw_notification.status = 200 gw_notification.headers = {} gw_notification.properties = [] gw_notification end private def before_gateways(kb_transaction, last_transaction, payment_source, amount_in_cents, currency, options, context) super(kb_transaction, last_transaction, payment_source, amount_in_cents, currency, options, context) options[:idempotency_key] ||= kb_transaction.external_key end def get_payment_source(kb_payment_method_id, properties, options, context) return nil if options[:customer_id] super(kb_payment_method_id, properties, options, context) end def populate_defaults(pm, amount, properties, context, options) options[:customer] ||= pm.stripe_customer_id options[:destination] ||= get_destination(properties, context) options[:application_fee] ||= get_application_fee(amount, properties) unless options[:destination].nil? end def get_destination(properties, context) stripe_account_id = find_value_from_properties(properties, :destination) if stripe_account_id.nil? config(context.tenant_id)[:stripe][:stripe_destination] elsif stripe_account_id =~ /[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}/ # Stripe doesn't use UUIDs - assume the destination is a Kill Bill account id ::Killbill::Stripe::StripeResponse.managed_stripe_account_id_from_kb_account_id(stripe_account_id, context.tenant_id) else stripe_account_id end end def get_application_fee(amount, properties) fees_amount = find_value_from_properties(properties, :fees_amount) return fees_amount unless fees_amount.nil? fees_percent = find_value_from_properties(properties, :fees_percent) return (fees_percent * amount * 100).to_i unless fees_percent.nil? config(context.tenant_id)[:stripe][:fees_amount] || (config(context.tenant_id)[:stripe][:fees_percent].to_f * amount * 100) end end end end