module Killbill #:nodoc: module Cybersource #:nodoc: class PaymentPlugin < ::Killbill::Plugin::ActiveMerchant::PaymentPlugin def initialize gateway_builder = Proc.new do |config| ::ActiveMerchant::Billing::CyberSourceGateway.new :login => config[:login], :password => config[:password] end super(gateway_builder, :cybersource, ::Killbill::Cybersource::CybersourcePaymentMethod, ::Killbill::Cybersource::CybersourceTransaction, ::Killbill::Cybersource::CybersourceResponse) end def on_event(event) # Require to deal with per tenant configuration invalidation super(event) # # Custom event logic could be added below... # end def start_plugin super gateway = lookup_gateway(:on_demand) @report_api = CyberSourceOnDemand.new(gateway, logger) rescue => e @report_api = nil end def authorize_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 = {} add_required_options(kb_account_id, properties, options, context) 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 = {} add_required_options(kb_account_id, properties, options, context) 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) # Pass extra parameters for the gateway here options = {} add_required_options(kb_account_id, properties, options, context) 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 = {} add_required_options(kb_account_id, properties, options, context) 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 = {} add_required_options(kb_account_id, properties, options, context) 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 = {} add_required_options(kb_account_id, properties, options, context) 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) # Pass extra parameters for the gateway here options = {} 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) # 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 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) # TODO 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[:cybersource][:token] } descriptor_fields = merge_properties(descriptor_fields, options) super(kb_account_id, descriptor_fields, properties, context) end def process_notification(notification, properties, context) # Pass extra parameters for the gateway here options = {} properties = merge_properties(properties, options) super(notification, properties, context) do |gw_notification, service| # Retrieve the payment # gw_notification.kb_payment_id = # # Set the response body # gw_notification.entity = end end # Make calls idempotent def before_gateway(gateway, kb_transaction, last_transaction, payment_source, amount_in_cents, currency, options) super return if @report_api.nil? || options[:skip_gw] merchant_reference_code = options[:order_id] report = @report_api.single_transaction_report(merchant_reference_code, kb_transaction.created_date.strftime('%Y%m%d')) if !report.nil? && !report['Report'].nil? && !report['Report']['Requests'].nil? && !report['Report']['Requests']['Request'].nil? && report['Report']['Requests']['Request']['MerchantReferenceNumber'] == merchant_reference_code logger.info "Skipping gateway call for existing transaction #{kb_transaction.id}, merchant reference code #{merchant_reference_code}" options[:skip_gw] = true end rescue => e logger.warn "Error checking for duplicate payment: #{e.message}" end def add_required_options(kb_account_id, properties, options, context) if options[:email].nil? email = find_value_from_properties(properties, 'email') if email.nil? # Note: we need to clone the context otherwise it will be transformed back to a Java one here kb_account = @kb_apis.account_user_api.get_account_by_id(kb_account_id, @kb_apis.create_context(context.tenant_id)) email = kb_account.email end options[:email] = email end end end end end