lolita-first-data 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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