giact 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Wynn Netherland
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,45 @@
1
+ # Giact
2
+
3
+ If you have to use the Giact recurring eCheck POST API my heart goes out to you. Here's a Ruby wrapper to ease your burden.
4
+
5
+ ## Installation
6
+
7
+ sudo gem install giact
8
+
9
+ ## TODO
10
+
11
+ API calls left to implement:
12
+
13
+ * list installment checks by order ID
14
+ * list installment checks by transaction ID
15
+ * cancel future installment checks by order ID
16
+ * cancel future installment checks by transaction ID
17
+ * cancel a transaction by order ID that has not been batched
18
+ * cancel a transaction by transaction ID that has not been batched
19
+ * request a refund by order ID
20
+ * request a refund by transaction ID
21
+ * request a partial refund by order ID
22
+ * request a partial refund by transaction ID
23
+ * return a daily summary
24
+ * list daily deposits for a given date
25
+ * list checks returned for a given date
26
+ * list checks refunded for a given date
27
+ * list transactions scrubbed for a given date
28
+ * list errored transactions for a given date
29
+ * list checks returned for a given date range
30
+ * list checks refunded for a given date range
31
+
32
+ ## Note on Patches/Pull Requests
33
+
34
+ * Fork the project.
35
+ * Make your feature addition or bug fix.
36
+ * Add tests for it. This is important so I don't break it in a
37
+ future version unintentionally.
38
+ * Commit, do not mess with rakefile, version, or history.
39
+ (if you want to have your own version, that is fine but
40
+ bump version in a commit by itself I can ignore when I pull)
41
+ * Send me a pull request. Bonus points for topic branches.
42
+
43
+ ## Copyright
44
+
45
+ Copyright (c) 2010 [Wynn Netherland](http://wynnnetherland.com). See LICENSE for details.
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "giact"
8
+ gem.summary = %Q{Ruby wrapper for the Giact real time checks API}
9
+ gem.description = %Q{Ruby wrapper for the Giact real time checks API}
10
+ gem.email = "wynn.netherland@gmail.com"
11
+ gem.homepage = "http://github.com/pengwynn/giact"
12
+ gem.authors = ["Wynn Netherland"]
13
+ gem.add_dependency('hashie', '~> 0.1.3')
14
+ gem.add_dependency('httparty', '~> 0.4.5')
15
+ gem.add_dependency('activesupport', '>= 2.3')
16
+
17
+ gem.add_development_dependency('thoughtbot-shoulda', '>= 2.10.1')
18
+ gem.add_development_dependency('jnunemaker-matchy', '0.4.0')
19
+ gem.add_development_dependency('mocha', '0.9.4')
20
+ gem.add_development_dependency('fakeweb', '>= 1.2.5')
21
+ gem.add_development_dependency "yard", ">= 0"
22
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
23
+ end
24
+ Jeweler::GemcutterTasks.new
25
+ rescue LoadError
26
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
27
+ end
28
+
29
+ require 'rake/testtask'
30
+ Rake::TestTask.new(:test) do |test|
31
+ test.libs << 'lib' << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+
36
+ begin
37
+ require 'rcov/rcovtask'
38
+ Rcov::RcovTask.new do |test|
39
+ test.libs << 'test'
40
+ test.pattern = 'test/**/test_*.rb'
41
+ test.verbose = true
42
+ end
43
+ rescue LoadError
44
+ task :rcov do
45
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
46
+ end
47
+ end
48
+
49
+ task :test => :check_dependencies
50
+
51
+ task :default => :test
52
+
53
+ begin
54
+ require 'yard'
55
+ YARD::Rake::YardocTask.new
56
+ rescue LoadError
57
+ task :yardoc do
58
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
59
+ end
60
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,106 @@
1
+ require 'rubygems'
2
+ require 'time'
3
+ gem 'activesupport', '~> 2.3.2'
4
+ require 'activesupport'
5
+
6
+ gem 'hashie', '~> 0.1.3'
7
+ require 'hashie'
8
+
9
+ gem 'httparty', '>= 0.5.0'
10
+ require 'httparty'
11
+
12
+ gem 'validatable', '~> 1.6.7'
13
+ require 'validatable'
14
+
15
+
16
+ directory = File.expand_path(File.dirname(__FILE__))
17
+
18
+ Hash.send :include, Hashie::HashExtensions
19
+
20
+ class Hash
21
+
22
+ # Converts all of the keys to strings, optionally formatting key name
23
+ def camelize_keys!
24
+ keys.each{|k|
25
+ v = delete(k)
26
+ new_key = k.to_s.camelize.gsub(/Id/, "ID")
27
+ self[new_key] = v
28
+ v.camelize_keys! if v.is_a?(Hash)
29
+ v.each{|p| p.camelize_keys! if p.is_a?(Hash)} if v.is_a?(Array)
30
+ }
31
+ self
32
+ end
33
+
34
+ end
35
+
36
+ module Giact
37
+
38
+ class GiactError < StandardError
39
+ attr_reader :errors
40
+
41
+ def initialize(errors)
42
+ @errors = errors
43
+ super
44
+ end
45
+ end
46
+
47
+ class Unauthorized < StandardError; end
48
+ class InvalidRequest < GiactError; end
49
+
50
+ # config/initializers/giact.rb (for instance)
51
+ #
52
+ # Giact.configure do |config|
53
+ # config.company_id = 'your_company_id'
54
+ # config.username = 'your_api_username'
55
+ # config.password = 'your_api_password'
56
+ # end
57
+ def self.configure
58
+ yield self
59
+
60
+ Giact.company_id = company_id
61
+ Giact.username = username
62
+ Giact.password = password
63
+ true
64
+ end
65
+
66
+ def self.company_id
67
+ @company_id
68
+ end
69
+
70
+ def self.company_id=(company_id)
71
+ @company_id = company_id
72
+ end
73
+
74
+ def self.username
75
+ @username
76
+ end
77
+
78
+ def self.username=(username)
79
+ @username = username
80
+ end
81
+
82
+ def self.password
83
+ @password
84
+ end
85
+
86
+ def self.password=(password)
87
+ @password = password
88
+ end
89
+
90
+ def self.test_mode?
91
+ @test_mode || false
92
+ end
93
+
94
+ def self.test_mode=(test_mode)
95
+ @test_mode = test_mode
96
+ end
97
+ end
98
+
99
+ require File.join(directory, 'giact', 'cancel_recurring_check_list')
100
+ require File.join(directory, 'giact', 'recurring_check_list')
101
+ require File.join(directory, 'giact', 'transaction_result')
102
+ require File.join(directory, 'giact', 'payment_reply')
103
+ require File.join(directory, 'giact', 'payment_request')
104
+ require File.join(directory, 'giact', 'installment_payment_request')
105
+ require File.join(directory, 'giact', 'recurring_payment_request')
106
+ require File.join(directory, 'giact', 'client')
@@ -0,0 +1,17 @@
1
+ module Giact
2
+ class CancelRecurringCheckList
3
+ attr_accessor :cancelled, :details
4
+
5
+ def initialize(row="")
6
+ @cancelled, @details = row.split("|")
7
+ end
8
+
9
+ def cancelled?
10
+ @cancelled.downcase == 'true'
11
+ end
12
+
13
+ def self.from_response(response)
14
+ response.split(/\n/).map{|line| Giact::CancelRecurringCheckList.new(line)}
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,136 @@
1
+ module Giact
2
+ class Client
3
+ include HTTParty
4
+ format :xml
5
+
6
+ attr_reader :company_id
7
+
8
+ # @param [Hash] options method options
9
+ # @option options [Integer] :gateway Number (1-3) of gateway server to use
10
+ # @option options [String] :company_id Your Giact company ID
11
+ # @option options [String] :username Your Giact API username
12
+ # @option options [String] :password Your Giact API password
13
+ def initialize(options={})
14
+ options[:gateway] ||= 1
15
+ self.class.base_uri("https://gatewaydtx#{options[:gateway]}.giact.com/RealTime/POST/RealTimeChecks.asmx")
16
+
17
+ @company_id ||= Giact.company_id ||= options[:company_id]
18
+ @username ||= Giact.username ||= options[:username]
19
+ @password ||= Giact.password ||= options[:password]
20
+ end
21
+
22
+
23
+ # Return an authentication token
24
+ #
25
+ # @return [String] authentication token
26
+ def login
27
+ response = self.class.post("/Login", :body => {:companyID => @company_id, :un => @username, :pw => @password})
28
+ if auth_token = response['string']
29
+ @token = auth_token
30
+ else
31
+ raise Giact::Unauthorized.new(response.body)
32
+
33
+ end
34
+ end
35
+
36
+ # Returns current authentication token or calls login
37
+ #
38
+ # @return [String] authentication token
39
+ def token
40
+ @token ||= self.login
41
+ end
42
+
43
+ # Process a single payment
44
+ #
45
+ # @param [Giact::PaymentRequest] payment_request Customer and payment info to be processed
46
+ # @return [Giact::PaymentReply] Transaction information
47
+ def single_payment(payment_request)
48
+ payment_request.merge!(:company_id => self.company_id, :token => self.token)
49
+ raise Giact::InvalidRequest.new(payment_request.errors.full_messages) unless payment_request.valid?
50
+
51
+ response = self.class.post("/SinglePayment", :body => payment_request.to_request_hash)
52
+ if reply = response['string']
53
+ Giact::PaymentReply.new(reply)
54
+ else
55
+ raise response.body
56
+ end
57
+ end
58
+
59
+ # Process recurring payments
60
+ #
61
+ # @param [Giact::RecurringPaymentRequest] payment_request Customer and payment info to be processed
62
+ # @return [Giact::PaymentReply] Transaction information
63
+ def recurring_payments(payment_request)
64
+ payment_request.merge!(:company_id => self.company_id, :token => self.token)
65
+ raise Giact::InvalidRequest.new(payment_request.errors.full_messages) if not payment_request.is_a?(Giact::RecurringPaymentRequest) or not payment_request.valid?
66
+
67
+ response = self.class.post("/RecurringPayments", :body => payment_request.to_request_hash)
68
+ if reply = response['string']
69
+ Giact::PaymentReply.new(reply)
70
+ else
71
+
72
+ end
73
+ end
74
+
75
+ # Process installment payments
76
+ #
77
+ # @param [Giact::InstallmentPaymentRequest] payment_request Customer and payment info to be processed
78
+ # @return [Giact::PaymentReply] Transaction information
79
+ def installments_payments(payment_request)
80
+ payment_request.merge!(:company_id => self.company_id, :token => self.token)
81
+ raise Giact::InvalidRequest.new(payment_request.errors.full_messages) if not payment_request.is_a?(Giact::InstallmentPaymentRequest) or not payment_request.valid?
82
+
83
+ response = self.class.post("/InstallmentsPayments", :body => payment_request.to_request_hash)
84
+ if reply = response['string']
85
+ Giact::PaymentReply.new(reply)
86
+ else
87
+
88
+ end
89
+ end
90
+
91
+ # @param [Hash] options method options
92
+ # @option options [String] :transaction_id Transaction ID to list checks
93
+ # @option options [String] :order_id Order ID to list checks
94
+ def recurring_checks(options={})
95
+
96
+ path = "/RecurringChecksBy#{options.keys.first.to_s.camelize.gsub(/Id$/, "ID")}"
97
+ options.merge!(:company_id => self.company_id, :token => self.token)
98
+ options.camelize_keys!
99
+
100
+ response = self.class.post(path, :body => options)['string']
101
+ Giact::RecurringCheckList.from_response(response)
102
+ end
103
+
104
+ # @param [Hash] options method options
105
+ # @option options [String] :transaction_id Transaction ID for which to cancel checks
106
+ # @option options [String] :order_id Order ID for which to cancel checks
107
+ def cancel_recurring(options={})
108
+
109
+ path = "/CancelRecurringBy#{options.keys.first.to_s.camelize.gsub(/Id$/, "ID")}"
110
+ options.merge!(:company_id => self.company_id, :token => self.token)
111
+ options.camelize_keys!
112
+
113
+ response = self.class.post(path, :body => options)['string']
114
+ Giact::CancelRecurringCheckList.from_response(response)
115
+ end
116
+
117
+
118
+ # @param [Hash] options method options
119
+ # @option options [String] :name_on_check Name on check to search
120
+ # @option options [String] :customer_id Customer ID to search
121
+ # @option options [String] :order_id Order ID to search
122
+ def search_transactions(options={})
123
+
124
+ path = "/TransactionsBy#{options.keys.first.to_s.camelize.gsub(/Id$/, "ID")}"
125
+ options.merge!(:company_id => self.company_id, :token => self.token)
126
+ options.camelize_keys!
127
+
128
+ response = self.class.post(path, :body => options)['string']
129
+ Giact::TransactionResult.from_response(response)
130
+ end
131
+
132
+
133
+
134
+
135
+ end
136
+ end
@@ -0,0 +1,20 @@
1
+ module Giact
2
+ class InstallmentPaymentRequest < PaymentRequest
3
+
4
+ validates_each :installment_amount, :logic => lambda { errors.add(:installment_amount, "must be greater than zero") if installment_amount.to_f == 0.0 }
5
+
6
+
7
+ def installment_count
8
+ self[:installment_amount].to_i
9
+ end
10
+
11
+ def installments_start_date
12
+ if self[:installments_start_date].respond_to?(:year)
13
+ self[:installments_start_date].strftime("%m/%d/%Y")
14
+ else
15
+ self[:installments_start_date]
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ module Giact
2
+ class PaymentReply
3
+ attr_reader :transaction_id, :error, :pass, :details, :code
4
+
5
+ def initialize(response)
6
+ @transaction_id, @error, @pass, @details, @code = response.split("|")
7
+ end
8
+
9
+ def transaction_id
10
+ @transaction_id.to_i == 0 ? nil : @transaction_id.to_i
11
+ end
12
+
13
+ def error?
14
+ @error.to_s == 'true'
15
+ end
16
+
17
+ def pass?
18
+ @pass.to_s == 'true'
19
+ end
20
+
21
+ end
22
+ end