lolita-first-data 1.0.0 → 2.0.0

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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/{LICENSE.txt → MIT-LICENSE} +1 -1
  3. data/README.md +8 -89
  4. data/Rakefile +22 -45
  5. data/app/controllers/lolita_first_data/application_controller.rb +23 -0
  6. data/app/controllers/lolita_first_data/test_controller.rb +73 -0
  7. data/app/controllers/lolita_first_data/transactions_controller.rb +63 -0
  8. data/app/models/lolita_first_data/transaction.rb +45 -0
  9. data/config/locales/en.yml +109 -109
  10. data/config/locales/lv.yml +109 -109
  11. data/config/routes.rb +5 -5
  12. data/lib/generators/lolita_first_data/install_generator.rb +13 -14
  13. data/lib/generators/lolita_first_data/templates/migration.rb +21 -21
  14. data/lib/lolita-first-data.rb +25 -6
  15. data/lib/lolita_first_data/billing.rb +82 -0
  16. data/lib/lolita_first_data/custom_logger.rb +8 -0
  17. data/lib/lolita_first_data/engine.rb +5 -0
  18. data/lib/lolita_first_data/version.rb +3 -0
  19. data/lib/tasks/lolita_first_data_tasks.rake +4 -0
  20. metadata +100 -102
  21. data/.rspec +0 -2
  22. data/Gemfile +0 -15
  23. data/Gemfile.lock +0 -140
  24. data/VERSION +0 -1
  25. data/app/controllers/lolita/first_data/common_controller.rb +0 -25
  26. data/app/controllers/lolita/first_data/test_controller.rb +0 -78
  27. data/app/controllers/lolita/first_data/transaction_controller.rb +0 -49
  28. data/app/models/lolita/first_data/transaction.rb +0 -55
  29. data/lib/lolita-first-data/billing.rb +0 -37
  30. data/lib/lolita-first-data/custom_logger.rb +0 -10
  31. data/lib/lolita-first-data/gateway.rb +0 -160
  32. data/lib/lolita-first-data/rails.rb +0 -6
  33. data/lib/tasks/first_data_tasks.rake +0 -78
  34. data/lolita-first-data.gemspec +0 -95
  35. data/spec/cert.pem +0 -63
  36. data/spec/fabricators/reservation_fabricator.rb +0 -4
  37. data/spec/fabricators/transaction_fabricator.rb +0 -7
  38. data/spec/first_data_spec.rb +0 -67
  39. data/spec/spec_helper.rb +0 -101
  40. data/spec/support/rails.rb +0 -16
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --color
2
- --format d
data/Gemfile DELETED
@@ -1,15 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- gem 'rails', '>= 3.0.0'
4
- gem "activemerchant", "~> 1.17.0"
5
-
6
- group :development do
7
- gem 'sqlite3', '~> 1.3.4'
8
- gem 'rspec', '~> 2.6.0'
9
- gem 'webmock', '~> 1.7.6'
10
- gem 'ruby-debug19', '~> 0.11.6'
11
- gem "bundler", "~> 1.0.0"
12
- gem "jeweler", "~> 1.6.4"
13
- gem "rcov", ">= 0"
14
- gem 'fabrication', "~> 1.1.0"
15
- end
@@ -1,140 +0,0 @@
1
- GEM
2
- remote: http://rubygems.org/
3
- specs:
4
- actionmailer (3.1.0)
5
- actionpack (= 3.1.0)
6
- mail (~> 2.3.0)
7
- actionpack (3.1.0)
8
- activemodel (= 3.1.0)
9
- activesupport (= 3.1.0)
10
- builder (~> 3.0.0)
11
- erubis (~> 2.7.0)
12
- i18n (~> 0.6)
13
- rack (~> 1.3.2)
14
- rack-cache (~> 1.0.3)
15
- rack-mount (~> 0.8.2)
16
- rack-test (~> 0.6.1)
17
- sprockets (~> 2.0.0)
18
- activemerchant (1.17.0)
19
- activesupport (>= 2.3.11)
20
- braintree (>= 2.0.0)
21
- builder (>= 2.0.0)
22
- json (>= 1.5.1)
23
- activemodel (3.1.0)
24
- activesupport (= 3.1.0)
25
- bcrypt-ruby (~> 3.0.0)
26
- builder (~> 3.0.0)
27
- i18n (~> 0.6)
28
- activerecord (3.1.0)
29
- activemodel (= 3.1.0)
30
- activesupport (= 3.1.0)
31
- arel (~> 2.2.1)
32
- tzinfo (~> 0.3.29)
33
- activeresource (3.1.0)
34
- activemodel (= 3.1.0)
35
- activesupport (= 3.1.0)
36
- activesupport (3.1.0)
37
- multi_json (~> 1.0)
38
- addressable (2.2.6)
39
- archive-tar-minitar (0.5.2)
40
- arel (2.2.1)
41
- bcrypt-ruby (3.0.0)
42
- braintree (2.11.0)
43
- builder (>= 2.0.0)
44
- builder (3.0.0)
45
- columnize (0.3.4)
46
- crack (0.1.8)
47
- diff-lcs (1.1.3)
48
- erubis (2.7.0)
49
- fabrication (1.1.0)
50
- git (1.2.5)
51
- hike (1.2.1)
52
- i18n (0.6.0)
53
- jeweler (1.6.4)
54
- bundler (~> 1.0)
55
- git (>= 1.2.5)
56
- rake
57
- json (1.5.4)
58
- linecache19 (0.5.12)
59
- ruby_core_source (>= 0.1.4)
60
- mail (2.3.0)
61
- i18n (>= 0.4.0)
62
- mime-types (~> 1.16)
63
- treetop (~> 1.4.8)
64
- mime-types (1.16)
65
- multi_json (1.0.3)
66
- polyglot (0.3.2)
67
- rack (1.3.2)
68
- rack-cache (1.0.3)
69
- rack (>= 0.4)
70
- rack-mount (0.8.3)
71
- rack (>= 1.0.0)
72
- rack-ssl (1.3.2)
73
- rack
74
- rack-test (0.6.1)
75
- rack (>= 1.0)
76
- rails (3.1.0)
77
- actionmailer (= 3.1.0)
78
- actionpack (= 3.1.0)
79
- activerecord (= 3.1.0)
80
- activeresource (= 3.1.0)
81
- activesupport (= 3.1.0)
82
- bundler (~> 1.0)
83
- railties (= 3.1.0)
84
- railties (3.1.0)
85
- actionpack (= 3.1.0)
86
- activesupport (= 3.1.0)
87
- rack-ssl (~> 1.3.2)
88
- rake (>= 0.8.7)
89
- rdoc (~> 3.4)
90
- thor (~> 0.14.6)
91
- rake (0.9.2)
92
- rcov (0.9.10)
93
- rdoc (3.9.4)
94
- rspec (2.6.0)
95
- rspec-core (~> 2.6.0)
96
- rspec-expectations (~> 2.6.0)
97
- rspec-mocks (~> 2.6.0)
98
- rspec-core (2.6.4)
99
- rspec-expectations (2.6.0)
100
- diff-lcs (~> 1.1.2)
101
- rspec-mocks (2.6.0)
102
- ruby-debug-base19 (0.11.25)
103
- columnize (>= 0.3.1)
104
- linecache19 (>= 0.5.11)
105
- ruby_core_source (>= 0.1.4)
106
- ruby-debug19 (0.11.6)
107
- columnize (>= 0.3.1)
108
- linecache19 (>= 0.5.11)
109
- ruby-debug-base19 (>= 0.11.19)
110
- ruby_core_source (0.1.5)
111
- archive-tar-minitar (>= 0.5.2)
112
- sprockets (2.0.0)
113
- hike (~> 1.2)
114
- rack (~> 1.0)
115
- tilt (!= 1.3.0, ~> 1.1)
116
- sqlite3 (1.3.4)
117
- thor (0.14.6)
118
- tilt (1.3.3)
119
- treetop (1.4.10)
120
- polyglot
121
- polyglot (>= 0.3.1)
122
- tzinfo (0.3.29)
123
- webmock (1.7.6)
124
- addressable (> 2.2.5, ~> 2.2)
125
- crack (>= 0.1.7)
126
-
127
- PLATFORMS
128
- ruby
129
-
130
- DEPENDENCIES
131
- activemerchant (~> 1.17.0)
132
- bundler (~> 1.0.0)
133
- fabrication (~> 1.1.0)
134
- jeweler (~> 1.6.4)
135
- rails (>= 3.0.0)
136
- rcov
137
- rspec (~> 2.6.0)
138
- ruby-debug19 (~> 0.11.6)
139
- sqlite3 (~> 1.3.4)
140
- webmock (~> 1.7.6)
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 1.0.0
@@ -1,25 +0,0 @@
1
- module Lolita::FirstData
2
- class CommonController < ApplicationController
3
- include ActiveMerchant::Billing
4
- before_filter :set_gateway
5
- skip_before_filter :verify_authenticity_token
6
-
7
- private
8
-
9
- def set_gateway
10
- @gateway ||= ActiveMerchant::Billing::FirstDataGateway.new(
11
- :pem => File.open(FD_PEM).read,
12
- :pem_password => FD_PASS,
13
- :payment => set_active_payment
14
- )
15
- end
16
-
17
- # returns current payment instance from session
18
- def set_active_payment
19
- if session && session[:first_data] && params[:controller] == 'Lolita::FirstData::Transaction'
20
- @payment ||= session[:first_data][:billing_class].constantize.find(session[:first_data][:billing_id])
21
- end
22
- end
23
-
24
- end
25
- end
@@ -1,78 +0,0 @@
1
- module Lolita::FirstData
2
- class TestController < Lolita::FirstData::CommonController
3
- before_filter :render_nothing
4
-
5
- # renders nothing if not in development environment
6
- def render_nothing
7
- render :nothing => true unless Rails.env == 'development'
8
- end
9
-
10
- #FIXME: refactor
11
- # There are all 12 FirstData tests
12
- # you should pass them to get production certificates
13
- # use: http://localhost:3000/first_data_test/test?nr=1
14
- # then increment the "nr" until done
15
- def test
16
- ip = params[:nr] == '9' ? '192.168.1.2' : request.remote_ip
17
- # REQUEST
18
- case params[:nr]
19
- when /^(1|2|5|6|7|8|10)$/
20
- session[:first_data] = {}
21
- session[:first_data][:test] = params[:nr]
22
- rs = @gateway.purchase(100,428,ip,'tests')
23
- if rs.success?
24
- session[:first_data][:trans_id] = rs.params['TRANSACTION_ID']
25
- return redirect_to(@gateway.go_out)
26
- else
27
- return render(:text => "<pre>#{rs.message}</pre>", :satus => 400)
28
- end
29
- when /^(3|4|9|11)$/
30
- session[:first_data] = {}
31
- session[:first_data][:test] = params[:nr]
32
- rs = @gateway.authorize(100,428,ip,'tests')
33
- if rs.success?
34
- session[:first_data][:trans_id] = rs.params['TRANSACTION_ID']
35
- return redirect_to(@gateway.go_out)
36
- else
37
- return render(:text => "<pre>#{rs.message}</pre>", :satus => 400)
38
- end
39
- when /^(12)$/
40
- session[:first_data] = {}
41
- session[:first_data][:test] = params[:nr]
42
- rs = @gateway.close_day
43
- return render(:text => "<pre>#{rs.message}</pre>")
44
- end
45
-
46
- # RESPONSE
47
- case session[:first_data][:test]
48
- when /^(1|2|5|6|7|8)$/
49
- rs = @gateway.get_trans_result(ip,session[:first_data][:trans_id])
50
- msg = %^
51
- trans_id: #{session[:first_data][:trans_id]} <br />
52
- <pre>#{rs.message}</pre>
53
- ^
54
- return render(:text => msg)
55
- when /^3|4|9$/
56
- rs = @gateway.complete(session[:first_data][:trans_id],100,428,ip,'tests')
57
- msg = %^
58
- trans_id: #{session[:first_data][:trans_id]} <br />
59
- <pre>#{rs.message}</pre>
60
- ^
61
- return render(:text => msg)
62
- when /^(10|11)$/
63
- if session[:first_data][:test] == '11'
64
- @gateway.complete(session[:first_data][:trans_id],100,428,ip,'tests')
65
- @gateway.close_day
66
- end
67
- rs = @gateway.reverse(session[:first_data][:trans_id],100)
68
- msg = %^
69
- trans_id: #{session[:first_data][:trans_id]} <br />
70
- <pre>#{rs.message}</pre>
71
- ^
72
- return render(:text => msg)
73
- end if session[:first_data]
74
- render :text => "WRONG REQUEST", :status => 400
75
- end
76
-
77
- end
78
- end
@@ -1,49 +0,0 @@
1
- module Lolita::FirstData
2
- class TransactionController < Lolita::FirstData::CommonController
3
- before_filter :is_ssl_required
4
-
5
- # should exist
6
- # session[:first_data][:billing_class]
7
- # session[:first_data]:billing_id]
8
- # Class should respond to these methods:
9
- # => :price - cents
10
- # => :currency - according to http://en.wikipedia.org/wiki/ISO_4217
11
- # => :description - string that will show up in payment details
12
- def checkout
13
- if @payment && !@payment.paid?
14
- rs = @gateway.purchase(@payment.price,@payment.currency,request.remote_ip,@payment.description)
15
- if rs.success?
16
- Lolita::FirstData::Transaction.add(@payment, request, rs)
17
- redirect_to(@gateway.go_out)
18
- else
19
- if request.xhr? || !request.referer
20
- render :text => I18n.t('fd.purchase_failed'), :status => 400
21
- else
22
- redirect_to :back
23
- end
24
- end
25
- else
26
- render :text => I18n.t('fd.wrong_request'), :status => 400
27
- end
28
- end
29
-
30
- # there we land after returning from FirstData server
31
- # then we get transactions result and redirect to your given "finish" path
32
- def answer
33
- if trx = Lolita::FirstData::Transaction.where(transaction_id: params[:trans_id]).first
34
- rs = @gateway.get_trans_result(request.remote_ip,params[:trans_id])
35
- trx.process_answer(rs, @gateway, request)
36
- redirect_to "#{session[:first_data][:finish_path]}?merchant=fd&trans_id=#{CGI::escape(params[:trans_id])}"
37
- else
38
- render :text => "wrong transaction ID", :status => 400
39
- end
40
- end
41
-
42
- private
43
-
44
- # forces SSL in production mode if available
45
- def is_ssl_required
46
- ssl_required(:answer, :checkout) if defined?(ssl_required) && (Rails.env == 'production' || Rails.env == 'staging')
47
- end
48
- end
49
- end
@@ -1,55 +0,0 @@
1
- module Lolita::FirstData
2
- class Transaction < ActiveRecord::Base
3
- set_table_name :first_data_transactions
4
- belongs_to :paymentable, :polymorphic => true
5
- after_save :touch_paymentable
6
-
7
- def ip
8
- IPAddr.new(self[:ip], Socket::AF_INET).to_s
9
- end
10
-
11
- def ip=(x)
12
- self[:ip] = IPAddr.new(x).to_i
13
- end
14
-
15
- def process_answer rs, gateway, request
16
- self.status = (rs.success?) ? 'completed' : 'rejected'
17
- self.transaction_code = rs.params['RESULT_CODE']
18
- begin
19
- self.save!
20
- rescue Exception => e
21
- fdp_error = "#{e.to_s}\n\n#{$@.join("\n")}"
22
- if rs.success?
23
- begin
24
- gateway.reverse(fdp.transaction_id,fdp.paymentable.price)
25
- rescue Exception => reverse_exception
26
- reverse_error = "#{reverse_exception.to_s}\n\n#{$@.join("\n")}"
27
- ExceptionNotifier::Notifier.exception_notification(request.env, reverse_exception).deliver if defined?(ExceptionNotifier)
28
- gateway.log :error, reverse_error
29
- end
30
- end
31
- ExceptionNotifier::Notifier.exception_notification(request.env, e).deliver if defined?(ExceptionNotifier)
32
- gateway.log :error, fdp_error
33
- false
34
- end
35
- end
36
-
37
- # add new transaction in Checkout
38
- def self.add payment, request, rs
39
- Lolita::FirstData::Transaction.create!(
40
- :transaction_id => rs.params['TRANSACTION_ID'],
41
- :status => 'processing',
42
- :paymentable_id => payment.id,
43
- :paymentable_type => payment.class.to_s,
44
- :ip => request.remote_ip
45
- )
46
- end
47
-
48
- private
49
-
50
- # trigger "fd_trx_saved" on our paymentable model
51
- def touch_paymentable
52
- paymentable.fd_trx_saved(self) if paymentable
53
- end
54
- end
55
- end
@@ -1,37 +0,0 @@
1
- module Lolita
2
- module FirstData
3
- module Billing
4
- def self.included(base)
5
- base.has_many :fd_transactions, :as => :paymentable, :class_name => "Lolita::FirstData::Transaction", :dependent => :destroy
6
- base.extend ClassMethods
7
- base.class_eval do
8
- # returns true if exists transaction with status 'completed'
9
- # and updates status if needed
10
- def paid?
11
- self.fd_transactions.count(:conditions => {:status => 'completed', :transaction_code => '000'}) >= 1
12
- end
13
-
14
- def fd_error_message
15
- if !fd_transactions.empty? && fd_transactions.last.transaction_code
16
- I18n.t("fd.response.code_#{fd_transactions.last.transaction_code}", :default => I18n.t('fd.unknown_error'))
17
- end
18
- end
19
- end
20
- end
21
-
22
- module ClassMethods
23
- # Closes business day
24
- # should be executed every day ~midnight
25
- # Like "ruby script/runner <YourBillingModel>.close_business_day"
26
- def close_business_day
27
- gw = ActiveMerchant::Billing::FirstDataGateway.new(
28
- :pem => File.open(FD_PEM).read,
29
- :pem_password => FD_PASS
30
- )
31
- rs =gw.close_day
32
- rs.success? or raise("FirstData close day: #{rs.message}")
33
- end
34
- end
35
- end
36
- end
37
- end
@@ -1,10 +0,0 @@
1
- module Lolita
2
- module FirstData
3
- # custom log formatter for FirstData gateway
4
- class LogFormatter < Logger::Formatter
5
- def call(severity, time, program_name, message)
6
- "%5s [%s] (%s) %s :: %s\n" % [severity,I18n.l(time), $$, program_name, message]
7
- end
8
- end
9
- end
10
- end
@@ -1,160 +0,0 @@
1
- module ActiveMerchant #:nodoc:
2
- module Billing #:nodoc:
3
- class FirstDataGateway < Gateway
4
- TEST_DOMAIN = 'https://secureshop-test.firstdata.lv'
5
- LIVE_DOMAIN = 'https://secureshop.firstdata.lv'
6
-
7
- # The homepage URL of the gateway
8
- self.homepage_url = 'http://www.firstdata.lv/'
9
-
10
- # The name of the gateway
11
- self.display_name = 'FirstData'
12
-
13
- def initialize(options = {})
14
- requires!(options, :pem, :pem_password)
15
- @options = options
16
- @logger = Logger.new(defined?(Rails) ? "#{Rails.root}/log/first_data.log" : "/tmp/fd.log", 2, 1024**2)
17
- @logger.formatter = Lolita::FirstData::LogFormatter.new
18
- super
19
- end
20
-
21
- def authorize(money, currency ,ip, description)
22
- commit('authorize',{
23
- 'command' => 'a',
24
- 'msg_type' => 'DMS',
25
- 'amount' => money,
26
- 'currency' => currency,
27
- 'client_ip_addr'=> ip,
28
- 'description' => description[0,125],
29
- 'language' => language
30
- })
31
- end
32
-
33
- def complete(trans_id,amount,currency,ip,description)
34
- commit('complete',{
35
- 'command' => 't',
36
- 'msg_type' => 'DMS',
37
- 'trans_id' => trans_id,
38
- 'amount' => amount,
39
- 'currency' => currency,
40
- 'client_ip_addr'=> ip,
41
- 'description' => description[0,125]
42
- })
43
- end
44
-
45
- def purchase(money, currency ,ip, description)
46
- commit('purchase',{
47
- 'command' => 'v',
48
- 'amount' => money,
49
- 'currency' => currency,
50
- 'client_ip_addr'=> ip,
51
- 'description' => description[0,125],
52
- 'language' => language
53
- })
54
- end
55
-
56
- def get_trans_result(ip,trans_id)
57
- commit('result',{
58
- 'command' => 'c',
59
- 'trans_id' => trans_id,
60
- 'client_ip_addr'=> ip
61
- })
62
- end
63
-
64
- def reverse(trans_id, amount)
65
- commit('reverse',{
66
- 'command' => 'r',
67
- 'trans_id' => trans_id,
68
- 'amount' => amount
69
- })
70
- end
71
-
72
- def close_day
73
- commit('close',{
74
- 'command' => 'b'
75
- })
76
- end
77
-
78
- def go_out
79
- raise I18n.t('fd.no_trans_id') unless @trans_id
80
- url = get_domain + "/ecomm/ClientHandler?trans_id=#{CGI.escape @trans_id}"
81
- url
82
- end
83
-
84
- # log to default logger and if possible to payment logger
85
- def log severity, message
86
- @logger.send(severity,message)
87
- @options[:payment].log(severity,message) if @options[:payment].respond_to?(:log)
88
- end
89
-
90
- private
91
-
92
- def language
93
- I18n.locale.to_s.sub('-','_').downcase
94
- end
95
-
96
- def test?
97
- ActiveMerchant::Billing::Base.mode == :test
98
- end
99
-
100
- def get_domain
101
- test? ? TEST_DOMAIN : LIVE_DOMAIN
102
- end
103
-
104
- def parse(body)
105
- body.to_s.strip
106
- end
107
-
108
- # Return Response object
109
- # Use the response:
110
- # rs.success? - returns true|false
111
- # rs.message - returns String with message
112
- # rs.params - returns Hash with {'TRANSACTION_ID': 'jl2j4l2j423423=3-3423-4'} or {'RESULT_CODE' => '000'} ...
113
- def commit(action,parameters)
114
- url = get_domain + ":8443/ecomm/MerchantHandler"
115
- data = post_data(action, parameters)
116
- log :info, "#{url} + #{data}"
117
- rs = parse(post(url, data))
118
- log :info, rs
119
- data = {}
120
- rs.scan(/[0-9A-Z_]+:\s[^\s]+/){|item|
121
- item =~ /([0-9A-Z_]+):\s(.+)/
122
- data[$1] = $2
123
- }
124
- case action
125
- when /purchase|authorize/
126
- @trans_id = data['TRANSACTION_ID']
127
- Response.new(data['TRANSACTION_ID'],rs,data)
128
- when /result|reverse|complete|close/
129
- Response.new(data['RESULT'] == "OK",rs,data)
130
- end
131
- end
132
-
133
- # this posts data to FirstData server
134
- def post url, data, headers = {}
135
- begin
136
- ssl_post(url, data, headers)
137
- rescue Exception => e
138
- log :error, "#{e.to_s}\n\n#{$@.join("\n")}"
139
- "ERROR POSTING DATA"
140
- end
141
- end
142
-
143
- def pem_password
144
- @options[:pem_password]
145
- end
146
-
147
- def ssl_strict
148
- false
149
- end
150
-
151
- def post_data(action, parameters = {})
152
- parameters.to_query
153
- end
154
-
155
- def self.get_code_msg k
156
- I18n.t("fd.code._#{k}", :default => I18n.t('fd.unknown_error'))
157
- end
158
- end
159
- end
160
- end