killbill-litle 1.0.1

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/.gitignore ADDED
@@ -0,0 +1,36 @@
1
+ *.gem
2
+ *.rbc
3
+ *.swp
4
+ .bundle
5
+ .config
6
+ coverage
7
+ InstalledFiles
8
+ lib/bundler/man
9
+ pkg
10
+ rdoc
11
+ spec/reports
12
+ test/tmp
13
+ test/version_tmp
14
+ tmp
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
20
+
21
+ .jbundler
22
+ Jarfile.lock
23
+ Gemfile.lock
24
+
25
+ .DS_Store
26
+
27
+ # Build directory
28
+ killbill-litle/
29
+
30
+ # Config file
31
+ litle.yml
32
+
33
+ # Testing database
34
+ test.db
35
+
36
+ target
data/.travis.yml ADDED
@@ -0,0 +1,18 @@
1
+ language: ruby
2
+
3
+ notifications:
4
+ email:
5
+ - killbilling-dev@googlegroups.com
6
+
7
+ rvm:
8
+ - jruby-19mode
9
+ - jruby-head
10
+
11
+ jdk:
12
+ - openjdk7
13
+ - oraclejdk7
14
+ - openjdk6
15
+
16
+ matrix:
17
+ allow_failures:
18
+ - rvm: jruby-head
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/Jarfile ADDED
@@ -0,0 +1,3 @@
1
+ jar 'com.ning.billing:killbill-api', '0.1.63'
2
+ jar 'com.ning.billing:killbill-util:tests', '0.1.63'
3
+ jar 'javax.servlet:javax.servlet-api', '3.0.1'
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ [![Build Status](https://travis-ci.org/killbill/killbill-litle-plugin.png)](https://travis-ci.org/killbill/killbill-litle-plugin)
2
+ [![Code Climate](https://codeclimate.com/github/killbill/killbill-litle-plugin.png)](https://codeclimate.com/github/killbill/killbill-litle-plugin)
3
+
4
+ killbill-litle-plugin
5
+ =====================
6
+
7
+ Plugin to use Litle & Co. as a gateway
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env rake
2
+
3
+ # Install tasks to build and release the plugin
4
+ require 'bundler/setup'
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ # Install test tasks
8
+ require 'rspec/core/rake_task'
9
+ desc "Run RSpec"
10
+ RSpec::Core::RakeTask.new
11
+
12
+ # Install tasks to package the plugin for Killbill
13
+ require 'killbill/rake_task'
14
+ Killbill::PluginHelper.install_tasks
15
+
16
+ # Run tests by default
17
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.1
data/config.ru ADDED
@@ -0,0 +1,4 @@
1
+ require 'litle'
2
+ require 'litle/config/application'
3
+
4
+ run Sinatra::Application
data/db/schema.rb ADDED
@@ -0,0 +1,53 @@
1
+ require 'active_record'
2
+
3
+ ActiveRecord::Schema.define(:version => 20130311153635) do
4
+ create_table "litle_payment_methods", :force => true do |t|
5
+ t.string "kb_account_id", :null => false
6
+ t.string "kb_payment_method_id" # NULL before Killbill knows about it
7
+ t.string "litle_token", :null => false
8
+ t.boolean "is_deleted", :null => false, :default => false
9
+ t.datetime "created_at", :null => false
10
+ t.datetime "updated_at", :null => false
11
+ end
12
+
13
+ create_table "litle_transactions", :force => true do |t|
14
+ t.integer "litle_response_id", :null => false
15
+ t.string "api_call", :null => false
16
+ t.string "kb_payment_id", :null => false
17
+ t.string "litle_txn_id", :null => false
18
+ t.integer "amount_in_cents", :null => false
19
+ t.datetime "created_at", :null => false
20
+ t.datetime "updated_at", :null => false
21
+ end
22
+
23
+ create_table "litle_responses", :force => true do |t|
24
+ t.string "api_call", :null => false
25
+ t.string "kb_payment_id"
26
+ t.string "message"
27
+ t.string "authorization"
28
+ t.boolean "fraud_review"
29
+ t.boolean "test"
30
+ t.string "params_litleonelineresponse_message"
31
+ t.string "params_litleonelineresponse_response"
32
+ t.string "params_litleonelineresponse_version"
33
+ t.string "params_litleonelineresponse_xmlns"
34
+ t.string "params_litleonelineresponse_saleresponse_customer_id"
35
+ t.string "params_litleonelineresponse_saleresponse_id"
36
+ t.string "params_litleonelineresponse_saleresponse_report_group"
37
+ t.string "params_litleonelineresponse_saleresponse_litle_txn_id"
38
+ t.string "params_litleonelineresponse_saleresponse_order_id"
39
+ t.string "params_litleonelineresponse_saleresponse_response"
40
+ t.string "params_litleonelineresponse_saleresponse_response_time"
41
+ t.string "params_litleonelineresponse_saleresponse_message"
42
+ t.string "params_litleonelineresponse_saleresponse_auth_code"
43
+ t.string "avs_result_code"
44
+ t.string "avs_result_message"
45
+ t.string "avs_result_street_match"
46
+ t.string "avs_result_postal_match"
47
+ t.string "cvv_result_code"
48
+ t.string "cvv_result_message"
49
+ t.boolean "success"
50
+ t.datetime "created_at", :null => false
51
+ t.datetime "updated_at", :null => false
52
+ end
53
+ end
@@ -0,0 +1,45 @@
1
+ version = File.read(File.expand_path('../VERSION', __FILE__)).strip
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'killbill-litle'
5
+ s.version = version
6
+ s.summary = 'Plugin to use Litle & Co. as a gateway.'
7
+ s.description = 'Killbill payment plugin for Litle & Co.'
8
+
9
+ s.required_ruby_version = '>= 1.9.3'
10
+
11
+ s.license = 'Apache License (2.0)'
12
+
13
+ s.author = 'Killbill core team'
14
+ s.email = 'killbilling-users@googlegroups.com'
15
+ s.homepage = 'http://www.killbilling.org'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.bindir = 'bin'
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ s.rdoc_options << '--exclude' << '.'
24
+
25
+ s.add_dependency 'killbill', '~> 1.0.12'
26
+ s.add_dependency 'activemerchant', '~> 1.31.1'
27
+ s.add_dependency 'activerecord', '~> 3.2.1'
28
+ s.add_dependency 'sinatra', '~> 1.3.4'
29
+ # LitleOnline gem dependencies
30
+ s.add_dependency 'LitleOnline', '~> 8.16.0'
31
+ s.add_dependency 'xml-mapping', '~> 0.9.1'
32
+ s.add_dependency 'xml-object', '~> 0.9.93'
33
+ if defined?(JRUBY_VERSION)
34
+ s.add_dependency 'activerecord-jdbcmysql-adapter', '~> 1.2.9'
35
+ end
36
+
37
+ s.add_development_dependency 'jbundler', '~> 0.4.1'
38
+ s.add_development_dependency 'rake', '>= 10.0.0'
39
+ s.add_development_dependency 'rspec', '~> 2.12.0'
40
+ if defined?(JRUBY_VERSION)
41
+ s.add_development_dependency 'activerecord-jdbcsqlite3-adapter', '~> 1.2.6'
42
+ else
43
+ s.add_development_dependency 'sqlite3', '~> 1.3.7'
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ mainClass=Killbill::Litle::PaymentPlugin
2
+ require=litle
3
+ pluginType=PAYMENT
data/lib/litle/api.rb ADDED
@@ -0,0 +1,119 @@
1
+ module Killbill::Litle
2
+ class PaymentPlugin < Killbill::Plugin::Payment
3
+ def start_plugin
4
+ Killbill::Litle.initialize! "#{@root}/litle.yml", @logger
5
+ @gateway = Killbill::Litle.gateway
6
+
7
+ super
8
+
9
+ @logger.info "Killbill::Litle::PaymentPlugin started"
10
+ end
11
+
12
+ # return DB connections to the Pool if required
13
+ def after_request
14
+ ActiveRecord::Base.connection.close
15
+ end
16
+
17
+ def process_payment(kb_account_id, kb_payment_id, kb_payment_method_id, amount_in_cents, currency, options = {})
18
+ # Required argument
19
+ # Note! The field is limited to 25 chars, so we convert the UUID (in hex) to base64
20
+ options[:order_id] ||= Utils.compact_uuid kb_payment_id
21
+
22
+ # Set a default report group
23
+ options[:merchant] ||= report_group_for_currency(currency)
24
+ # Retrieve the Litle token
25
+ token = get_token(kb_payment_method_id)
26
+
27
+ # Go to Litle
28
+ litle_response = @gateway.purchase amount_in_cents, token, options
29
+ response = save_response_and_transaction litle_response, :charge, kb_payment_id, amount_in_cents
30
+
31
+ response.to_payment_response
32
+ end
33
+
34
+ def process_refund(kb_account_id, kb_payment_id, amount_in_cents, currency, options = {})
35
+ # Find one successful charge which amount is at least the amount we are trying to refund
36
+ litle_transaction = LitleTransaction.where("litle_transactions.amount_in_cents >= ?", amount_in_cents).find_last_by_api_call_and_kb_payment_id(:charge, kb_payment_id)
37
+ raise "Unable to find Litle transaction id for payment #{kb_payment_id}" if litle_transaction.nil?
38
+
39
+ # Set a default report group
40
+ options[:merchant] ||= report_group_for_currency(currency)
41
+
42
+ # Go to Litle
43
+ litle_response = @gateway.credit amount_in_cents, litle_transaction.litle_txn_id, options
44
+ response = save_response_and_transaction litle_response, :refund, kb_payment_id, amount_in_cents
45
+
46
+ response.to_refund_response
47
+ end
48
+
49
+ def get_payment_info(kb_account_id, kb_payment_id, options = {})
50
+ # We assume the payment is immutable in Litle and only look at our tables since there
51
+ # doesn't seem to be a Litle API to fetch details for a given transaction.
52
+ # TODO How can we support Authorization/Sale Recycling?
53
+ litle_transaction = LitleTransaction.from_kb_payment_id(kb_payment_id)
54
+
55
+ litle_transaction.litle_response.to_payment_response
56
+ end
57
+
58
+ def add_payment_method(kb_account_id, kb_payment_method_id, payment_method_props, set_default, options = {})
59
+ # Set a default report group
60
+ options[:merchant] ||= report_group_for_account(kb_account_id)
61
+
62
+ cc = ActiveMerchant::Billing::CreditCard.new(:number => payment_method_props.value_string('number'), :description => kb_payment_method_id)
63
+ litle_response = @gateway.store cc, options
64
+ response = save_response_and_transaction litle_response, :add_payment_method
65
+
66
+ LitlePaymentMethod.create :kb_account_id => kb_account_id, :kb_payment_method_id => kb_payment_method_id, :litle_token => response.litle_token
67
+ end
68
+
69
+ def delete_payment_method(kb_account_id, kb_payment_method_id, options = {})
70
+ LitlePaymentMethod.mark_as_deleted! kb_payment_method_id
71
+ end
72
+
73
+ def get_payment_method_detail(kb_account_id, kb_payment_method_id, options = {})
74
+ LitlePaymentMethod.from_kb_payment_method_id(kb_payment_method_id).to_payment_method_response
75
+ end
76
+
77
+ def get_payment_methods(kb_account_id, refresh_from_gateway = false, options = {})
78
+ LitlePaymentMethod.from_kb_account_id(kb_account_id).collect { |pm| pm.to_payment_method_response }
79
+ end
80
+
81
+ private
82
+
83
+ def report_group_for_account(kb_account_id)
84
+ =begin
85
+ account = account_user_api.get_account_by_id(kb_account_id)
86
+ currency = account.get_currency
87
+ report_group_for_currency(currency)
88
+ rescue APINotAvailableError
89
+ "Default Report Group"
90
+ =end
91
+ # STEPH hack until we support making API calls-- with context
92
+ report_group_for_currency('USD')
93
+ end
94
+
95
+ def report_group_for_currency(currency)
96
+ "Report Group for #{currency}"
97
+ end
98
+
99
+
100
+ def get_token(kb_payment_method_id)
101
+ LitlePaymentMethod.from_kb_payment_method_id(kb_payment_method_id).litle_token
102
+ end
103
+
104
+ def save_response_and_transaction(litle_response, api_call, kb_payment_id=nil, amount_in_cents=0)
105
+ @logger.warn "Unsuccessful #{api_call}: #{litle_response.message}" unless litle_response.success?
106
+
107
+ # Save the response to our logs
108
+ response = LitleResponse.from_response(api_call, kb_payment_id, litle_response)
109
+ response.save!
110
+
111
+ if response.success and !response.litle_txn_id.blank?
112
+ # Record the transaction
113
+ transaction = response.create_litle_transaction!(:amount_in_cents => amount_in_cents, :api_call => api_call, :kb_payment_id => kb_payment_id, :litle_txn_id => response.litle_txn_id)
114
+ @logger.debug "Recorded transaction: #{transaction.inspect}"
115
+ end
116
+ response
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,54 @@
1
+ configure do
2
+ # Usage: rackup -Ilib -E test
3
+ if development? or test?
4
+ Killbill::Litle.initialize! unless Killbill::Litle.initialized
5
+ end
6
+ end
7
+
8
+ helpers do
9
+ def plugin
10
+ Killbill::Litle::PrivatePaymentPlugin.instance
11
+ end
12
+ end
13
+
14
+ # http://127.0.0.1:9292/plugins/killbill-litle
15
+ get '/plugins/killbill-litle' do
16
+ locals = {
17
+ :secure_page_url => Killbill::Litle.config[:litle][:secure_page_url],
18
+ :paypage_id => Killbill::Litle.config[:litle][:paypage_id],
19
+ :kb_account_id => request.GET['kb_account_id'] || '1',
20
+ :merchant_txn_id => request.GET['merchant_txn_id'] || '1',
21
+ :order_id => request.GET['order_id'] || '1',
22
+ :report_group => request.GET['report_group'] || 'Default Report Group',
23
+ }
24
+ erb :paypage, :views => File.expand_path(File.dirname(__FILE__) + '/../views'), :locals => locals
25
+ end
26
+
27
+ post '/plugins/killbill-litle/checkout' do
28
+ data = request.POST
29
+
30
+ begin
31
+ pm = plugin.register_token! data['kb_account_id'], data['response_paypage_registration_id']
32
+ redirect "/plugins/killbill-litle/1.0/pms/#{pm.id}"
33
+ rescue => e
34
+ halt 500, {'Content-Type' => 'text/plain'}, "Error: #{e}"
35
+ end
36
+ end
37
+
38
+ # curl -v http://127.0.0.1:9292/plugins/killbill-litle/1.0/pms/1
39
+ get '/plugins/killbill-litle/1.0/pms/:id', :provides => 'json' do
40
+ if pm = Killbill::Litle::LitlePaymentMethod.find_by_id(params[:id].to_i)
41
+ pm.to_json
42
+ else
43
+ status 404
44
+ end
45
+ end
46
+
47
+ # curl -v http://127.0.0.1:9292/plugins/killbill-litle/1.0/transactions/1
48
+ get '/plugins/killbill-litle/1.0/transactions/:id', :provides => 'json' do
49
+ if transaction = Killbill::Litle::LitleTransaction.find_by_id(params[:id].to_i)
50
+ transaction.to_json
51
+ else
52
+ status 404
53
+ end
54
+ end
@@ -0,0 +1,30 @@
1
+ require 'logger'
2
+
3
+ module Killbill::Litle
4
+ mattr_reader :logger
5
+ mattr_reader :config
6
+ mattr_reader :gateway
7
+ mattr_reader :initialized
8
+ mattr_reader :test
9
+
10
+ def self.initialize!(config_file='litle.yml', logger=Logger.new(STDOUT))
11
+ @@logger = logger
12
+
13
+ @@config = Properties.new(config_file)
14
+ @@config.parse!
15
+ @@test = @@config[:litle][:test]
16
+
17
+ @@gateway = Killbill::Litle::Gateway.instance
18
+ @@gateway.configure(@@config[:litle])
19
+
20
+ if defined?(JRUBY_VERSION)
21
+ # See https://github.com/jruby/activerecord-jdbc-adapter/issues/302
22
+ require 'jdbc/mysql'
23
+ Jdbc::MySQL.load_driver(:require) if Jdbc::MySQL.respond_to?(:load_driver)
24
+ end
25
+
26
+ ActiveRecord::Base.establish_connection(@@config[:database])
27
+
28
+ @@initialized = true
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ module Killbill::Litle
2
+ class Properties
3
+ def initialize(file = 'litle.yml')
4
+ @config_file = Pathname.new(file).expand_path
5
+ end
6
+
7
+ def parse!
8
+ raise "#{@config_file} is not a valid file" unless @config_file.file?
9
+ @config = YAML.load_file(@config_file.to_s)
10
+ validate!
11
+ end
12
+
13
+ def [](key)
14
+ @config[key]
15
+ end
16
+
17
+ private
18
+
19
+ def validate!
20
+ raise "Bad configuration for Litle plugin. Config is #{@config.inspect}" if @config.blank? || !@config[:litle] || !@config[:litle][:merchant_id] || !@config[:litle][:password]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ module Killbill::Litle
2
+ class Gateway
3
+ include Singleton
4
+
5
+ def configure(config)
6
+ if config[:test]
7
+ ActiveMerchant::Billing::Base.mode = :test
8
+ end
9
+
10
+ if config[:log_file]
11
+ ActiveMerchant::Billing::LitleGateway.wiredump_device = File.open(config[:log_file], 'w')
12
+ ActiveMerchant::Billing::LitleGateway.wiredump_device.sync = true
13
+ end
14
+
15
+ @gateway = ActiveMerchant::Billing::LitleGateway.new({ :user => config[:username],
16
+ :merchant_id => config[:merchant_id],
17
+ :password => config[:password]
18
+ })
19
+ end
20
+
21
+ def method_missing(m, *args, &block)
22
+ @gateway.send(m, *args, &block)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ class Integer
2
+ def base(b)
3
+ self < b ? [self] : (self/b).base(b) + [self%b]
4
+ end
5
+ end
6
+
7
+ module Killbill::Litle
8
+ class Utils
9
+ # Use base 62 to be safe on the Litle side
10
+ BASE62 = ('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a
11
+
12
+ def self.compact_uuid(uuid)
13
+ uuid = uuid.gsub(/-/, '')
14
+ uuid.hex.base(62).map{ |i| BASE62[i].chr } * ''
15
+ end
16
+
17
+ def self.unpack_uuid(base62_uuid)
18
+ as_hex = base62_uuid.split(//).inject(0) { |i,e| i*62 + BASE62.index(e[0]) }
19
+ ("%x" % as_hex).insert(8, "-").insert(13, "-").insert(18, "-").insert(23, "-")
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ module Killbill::Litle
2
+ class LitlePaymentMethod < ActiveRecord::Base
3
+ attr_accessible :kb_account_id, :kb_payment_method_id, :litle_token
4
+
5
+ def self.from_kb_account_id(kb_account_id)
6
+ find_all_by_kb_account_id_and_is_deleted(kb_account_id, false)
7
+ end
8
+
9
+ def self.from_kb_payment_method_id(kb_payment_method_id)
10
+ payment_methods = find_all_by_kb_payment_method_id_and_is_deleted(kb_payment_method_id, false)
11
+ raise "No payment method found for payment method #{kb_payment_method_id}" if payment_methods.empty?
12
+ raise "Killbill payment method mapping to multiple active Litle tokens for payment method #{kb_payment_method_id}" if payment_methods.size > 1
13
+ payment_methods[0]
14
+ end
15
+
16
+ def self.mark_as_deleted!(kb_payment_method_id)
17
+ payment_method = from_kb_payment_method_id(kb_payment_method_id)
18
+ payment_method.is_deleted = true
19
+ payment_method.save!
20
+ end
21
+
22
+ def to_payment_method_response
23
+ external_payment_method_id = litle_token
24
+ # No concept of default payment method in Litle
25
+ is_default = false
26
+ # No extra information is stored in Litle
27
+ properties = []
28
+
29
+ Killbill::Plugin::PaymentMethodResponse.new external_payment_method_id, is_default, properties
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,122 @@
1
+
2
+
3
+ module Killbill::Litle
4
+ class LitleResponse < ActiveRecord::Base
5
+ has_one :litle_transaction
6
+ attr_accessible :api_call,
7
+ :kb_payment_id,
8
+ :message,
9
+ # Either litleToken (registerToken call) or litleTxnId
10
+ :authorization,
11
+ :fraud_review,
12
+ :test,
13
+ :params_litleonelineresponse_message,
14
+ :params_litleonelineresponse_response,
15
+ :params_litleonelineresponse_version,
16
+ :params_litleonelineresponse_xmlns,
17
+ :params_litleonelineresponse_saleresponse_customer_id,
18
+ :params_litleonelineresponse_saleresponse_id,
19
+ :params_litleonelineresponse_saleresponse_report_group,
20
+ :params_litleonelineresponse_saleresponse_litle_txn_id,
21
+ :params_litleonelineresponse_saleresponse_order_id,
22
+ :params_litleonelineresponse_saleresponse_response,
23
+ :params_litleonelineresponse_saleresponse_response_time,
24
+ :params_litleonelineresponse_saleresponse_message,
25
+ :params_litleonelineresponse_saleresponse_auth_code,
26
+ :avs_result_code,
27
+ :avs_result_message,
28
+ :avs_result_street_match,
29
+ :avs_result_postal_match,
30
+ :cvv_result_code,
31
+ :cvv_result_message,
32
+ :success
33
+
34
+ def litle_token
35
+ authorization
36
+ end
37
+
38
+ def litle_txn_id
39
+ potential_litle_txn_id = params_litleonelineresponse_saleresponse_litle_txn_id || authorization
40
+ if potential_litle_txn_id.blank?
41
+ nil
42
+ else
43
+ # Litle seems to return the precision sometimes along with the txnId (e.g. 053499651324799+19)
44
+ ("%f" % potential_litle_txn_id.split('+')[0]).to_i
45
+ end
46
+ end
47
+
48
+ def self.from_response(api_call, kb_payment_id, response)
49
+ LitleResponse.new({
50
+ :api_call => api_call,
51
+ :kb_payment_id => kb_payment_id,
52
+ :message => response.message,
53
+ :authorization => response.authorization,
54
+ :fraud_review => response.fraud_review?,
55
+ :test => response.test?,
56
+ :params_litleonelineresponse_message => extract(response, "litleOnlineResponse", "message"),
57
+ :params_litleonelineresponse_response => extract(response, "litleOnlineResponse", "response"),
58
+ :params_litleonelineresponse_version => extract(response, "litleOnlineResponse", "version"),
59
+ :params_litleonelineresponse_xmlns => extract(response, "litleOnlineResponse", "xmlns"),
60
+ :params_litleonelineresponse_saleresponse_customer_id => extract(response, "litleOnlineResponse", "saleResponse", "customerId"),
61
+ :params_litleonelineresponse_saleresponse_id => extract(response, "litleOnlineResponse", "saleResponse", "id"),
62
+ :params_litleonelineresponse_saleresponse_report_group => extract(response, "litleOnlineResponse", "saleResponse", "reportGroup"),
63
+ :params_litleonelineresponse_saleresponse_litle_txn_id => extract(response, "litleOnlineResponse", "saleResponse", "litleTxnId"),
64
+ :params_litleonelineresponse_saleresponse_order_id => extract(response, "litleOnlineResponse", "saleResponse", "orderId"),
65
+ :params_litleonelineresponse_saleresponse_response => extract(response, "litleOnlineResponse", "saleResponse", "response"),
66
+ :params_litleonelineresponse_saleresponse_response_time => extract(response, "litleOnlineResponse", "saleResponse", "responseTime"),
67
+ :params_litleonelineresponse_saleresponse_message => extract(response, "litleOnlineResponse", "saleResponse", "message"),
68
+ :params_litleonelineresponse_saleresponse_auth_code => extract(response, "litleOnlineResponse", "saleResponse", "authCode"),
69
+ :avs_result_code => response.avs_result.kind_of?(ActiveMerchant::Billing::AVSResult) ? response.avs_result.code : response.avs_result['code'],
70
+ :avs_result_message => response.avs_result.kind_of?(ActiveMerchant::Billing::AVSResult) ? response.avs_result.message : response.avs_result['message'],
71
+ :avs_result_street_match => response.avs_result.kind_of?(ActiveMerchant::Billing::AVSResult) ? response.avs_result.street_match : response.avs_result['street_match'],
72
+ :avs_result_postal_match => response.avs_result.kind_of?(ActiveMerchant::Billing::AVSResult) ? response.avs_result.postal_match : response.avs_result['postal_match'],
73
+ :cvv_result_code => response.cvv_result.kind_of?(ActiveMerchant::Billing::CVVResult) ? response.cvv_result.code : response.cvv_result['code'],
74
+ :cvv_result_message => response.cvv_result.kind_of?(ActiveMerchant::Billing::CVVResult) ? response.cvv_result.message : response.cvv_result['message'],
75
+ :success => response.success?
76
+ })
77
+ end
78
+
79
+ def to_payment_response
80
+ to_killbill_response Killbill::Plugin::PaymentResponse
81
+ end
82
+
83
+ def to_refund_response
84
+ to_killbill_response Killbill::Plugin::RefundResponse
85
+ end
86
+
87
+ private
88
+
89
+ def to_killbill_response(klass)
90
+ if litle_transaction.nil?
91
+ amount_in_cents = nil
92
+ created_date = created_at
93
+ else
94
+ amount_in_cents = litle_transaction.amount_in_cents
95
+ created_date = litle_transaction.created_at
96
+ end
97
+
98
+ effective_date = params_litleonelineresponse_saleresponse_response_time || created_date
99
+ status = message == 'Approved' ? Killbill::Plugin::PaymentStatus::SUCCESS : Killbill::Plugin::PaymentStatus::ERROR
100
+ gateway_error = params_litleonelineresponse_saleresponse_message
101
+ gateway_error_code = params_litleonelineresponse_saleresponse_response
102
+
103
+ klass.new(amount_in_cents, created_date, effective_date, status, gateway_error, gateway_error_code)
104
+ end
105
+
106
+ def self.extract(response, key1, key2=nil, key3=nil)
107
+ return nil if response.nil? || response.params.nil?
108
+ level1 = response.params[key1]
109
+
110
+ if level1.nil? or (key2.nil? and key3.nil?)
111
+ return level1
112
+ end
113
+ level2 = level1[key2]
114
+
115
+ if level2.nil? or key3.nil?
116
+ return level2
117
+ else
118
+ return level2[key3]
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,13 @@
1
+ module Killbill::Litle
2
+ class LitleTransaction < ActiveRecord::Base
3
+ belongs_to :litle_response
4
+ attr_accessible :amount_in_cents, :api_call, :kb_payment_id, :litle_txn_id
5
+
6
+ def self.from_kb_payment_id(kb_payment_id)
7
+ litle_transactions = find_all_by_api_call_and_kb_payment_id(:charge, kb_payment_id)
8
+ raise "Unable to find Litle transaction id for payment #{kb_payment_id}" if litle_transactions.empty?
9
+ raise "Killbill payment mapping to multiple Litle transactions for payment #{kb_payment_id}" if litle_transactions.size > 1
10
+ litle_transactions[0]
11
+ end
12
+ end
13
+ end