giact 0.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.
@@ -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