ruby_psigate 0.7
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/CHANGELOG +2 -0
- data/Gemfile +12 -0
- data/LICENSE +0 -0
- data/Manifest +45 -0
- data/README.markdown +99 -0
- data/Rakefile +28 -0
- data/lib/certs/cacert.pem +2633 -0
- data/lib/ruby_psigate/account.rb +152 -0
- data/lib/ruby_psigate/account_manager_api.rb +137 -0
- data/lib/ruby_psigate/account_methods.rb +5 -0
- data/lib/ruby_psigate/address.rb +54 -0
- data/lib/ruby_psigate/connection.rb +96 -0
- data/lib/ruby_psigate/credit_card.rb +104 -0
- data/lib/ruby_psigate/credit_card_methods.rb +12 -0
- data/lib/ruby_psigate/error.rb +33 -0
- data/lib/ruby_psigate/gateway.rb +126 -0
- data/lib/ruby_psigate/hash_variables.rb +58 -0
- data/lib/ruby_psigate/item.rb +65 -0
- data/lib/ruby_psigate/item_option.rb +10 -0
- data/lib/ruby_psigate/number_validation_methods.rb +12 -0
- data/lib/ruby_psigate/order.rb +120 -0
- data/lib/ruby_psigate/recurring_charge.rb +53 -0
- data/lib/ruby_psigate/recurring_item.rb +26 -0
- data/lib/ruby_psigate/response.rb +109 -0
- data/lib/ruby_psigate/serializer.rb +59 -0
- data/lib/ruby_psigate/transaction_methods.rb +21 -0
- data/lib/ruby_psigate/utils.rb +18 -0
- data/lib/ruby_psigate.rb +57 -0
- data/ruby_psigate.gemspec +31 -0
- data/test/remote/remote_account_test.rb +33 -0
- data/test/remote/remote_gateway_test.rb +32 -0
- data/test/test_helper.rb +144 -0
- data/test/unit/account_manager_api_test.rb +96 -0
- data/test/unit/account_test.rb +388 -0
- data/test/unit/address_test.rb +99 -0
- data/test/unit/connection_test.rb +153 -0
- data/test/unit/credit_card_test.rb +152 -0
- data/test/unit/gateway_test.rb +112 -0
- data/test/unit/item_option_test.rb +19 -0
- data/test/unit/item_test.rb +106 -0
- data/test/unit/order_test.rb +491 -0
- data/test/unit/recurring_charge_test.rb +89 -0
- data/test/unit/recurring_item_test.rb +62 -0
- data/test/unit/response_test.rb +110 -0
- data/test/unit/serializer_test.rb +89 -0
- data/test/unit/xml_api_test.rb +25 -0
- metadata +154 -0
@@ -0,0 +1,152 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
|
3
|
+
class InvalidRecurringCharge < RubyPsigateError; end
|
4
|
+
|
5
|
+
class Account
|
6
|
+
|
7
|
+
include AccountMethods
|
8
|
+
include CreditCardMethods
|
9
|
+
|
10
|
+
# List of actions you can perform on an account and their associated constant codes
|
11
|
+
ACTIONS = {
|
12
|
+
:account => {
|
13
|
+
:summary => "AMA00",
|
14
|
+
:details => "AMA05",
|
15
|
+
:register => "AMA01",
|
16
|
+
:update => "AMA02",
|
17
|
+
:enable => "AMA08",
|
18
|
+
:disable => "AMA09"
|
19
|
+
},
|
20
|
+
:credit_card => {
|
21
|
+
:add => "AMA11",
|
22
|
+
:delete => "AMA14",
|
23
|
+
:enable => "AMA18",
|
24
|
+
:disable => "AMA19"
|
25
|
+
},
|
26
|
+
:charges => {
|
27
|
+
:summary => "RBC00",
|
28
|
+
:add => "RBC01",
|
29
|
+
:update => "RBC02",
|
30
|
+
:delete => "RBC04",
|
31
|
+
:details => "RBC05",
|
32
|
+
:enable => "RBC08",
|
33
|
+
:disable => "RBC09",
|
34
|
+
:immediate => "RBC99"
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
def self.action_reverse_lookup(code)
|
39
|
+
result = nil
|
40
|
+
result_first = nil
|
41
|
+
result_second = nil
|
42
|
+
ACTIONS.each_pair do |section, actions|
|
43
|
+
break if result_second
|
44
|
+
result_second = actions.select { |k,v| v == code }
|
45
|
+
if result_second
|
46
|
+
result_first = section
|
47
|
+
result_second = result_second.keys[0]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
result = "#{result_first}_#{result_second}".downcase.to_sym
|
51
|
+
end
|
52
|
+
|
53
|
+
# include RubyPsigate::HashVariables
|
54
|
+
# hashable :account_register, %w( AccountID )
|
55
|
+
# hashable :account_update, %w( Condition Update )
|
56
|
+
|
57
|
+
attr_writer :action
|
58
|
+
attr_reader :address, :rbcharge
|
59
|
+
attr_accessor :account_id, :comments, :email, :serial_no, :store_id
|
60
|
+
|
61
|
+
# For hasahable
|
62
|
+
alias_method :accountid, :account_id
|
63
|
+
|
64
|
+
def to_hash(type=nil)
|
65
|
+
type = self.class.action_reverse_lookup(type)
|
66
|
+
result = {}
|
67
|
+
result = process_hash(result, type)
|
68
|
+
result
|
69
|
+
end
|
70
|
+
|
71
|
+
def action
|
72
|
+
ACTIONS[@action.first[0]][@action.first[1]]
|
73
|
+
end
|
74
|
+
|
75
|
+
def process_hash(result, type)
|
76
|
+
result.merge!({:Action => action})
|
77
|
+
|
78
|
+
if type == :account_register
|
79
|
+
result[:Account] = Hash.new
|
80
|
+
result[:Account].merge!({:AccountID => nil})
|
81
|
+
result[:Account].merge!(address.to_hash(:billing)) unless address.nil?
|
82
|
+
result[:Account].merge!({ :CardInfo => cc.to_hash(:card_info) })
|
83
|
+
end
|
84
|
+
|
85
|
+
if type == :account_update
|
86
|
+
result.merge!({:Condition => { :AccountID => account_id }})
|
87
|
+
result[:Update] = {}
|
88
|
+
result[:Update].merge!(address.to_hash(:billing)) unless address.nil?
|
89
|
+
result[:Update].merge!(:Email => email) unless email.nil?
|
90
|
+
result[:Update].merge!(:Comments => comments) unless comments.nil?
|
91
|
+
end
|
92
|
+
|
93
|
+
if type == :account_details || type == :account_enable || type == :account_disable
|
94
|
+
result.merge!({:Condition => { :AccountID => account_id }})
|
95
|
+
end
|
96
|
+
|
97
|
+
if type == :credit_card_add
|
98
|
+
result[:Account] = Hash.new
|
99
|
+
result[:Account].merge!({ :AccountID => account_id })
|
100
|
+
result[:Account].merge!({:CardInfo => cc.to_hash(:card_info)})
|
101
|
+
end
|
102
|
+
|
103
|
+
if type == :credit_card_delete || type == :credit_card_enable || type == :credit_card_disable
|
104
|
+
result.merge!({:Condition => { :AccountID => account_id, :SerialNo => serial_no }})
|
105
|
+
end
|
106
|
+
|
107
|
+
if type == :charges_summary
|
108
|
+
result[:Condition] = Hash.new
|
109
|
+
result.merge!({:Condition => { :AccountID => account_id, :StoreID => store_id }})
|
110
|
+
result[:Condition].merge!(rbcharge.to_hash)
|
111
|
+
end
|
112
|
+
|
113
|
+
if type == :charges_add || type == :charges_immediate
|
114
|
+
result[:Charge] = Hash.new
|
115
|
+
result[:Charge].merge!({:AccountID => account_id, :SerialNo => serial_no})
|
116
|
+
result[:Charge].merge!(rbcharge.to_hash)
|
117
|
+
end
|
118
|
+
|
119
|
+
if type == :charges_update
|
120
|
+
result.merge!({:Condition => { :RBCID => rbcharge.rbcid }})
|
121
|
+
result.merge!({:Update => { :RBTrigger => rbcharge.rbtrigger }})
|
122
|
+
end
|
123
|
+
|
124
|
+
if type == :charges_delete || type == :charges_details || type == :charges_enable || type == :charges_disable
|
125
|
+
result.merge!({:Condition => { :RBCID => rbcharge.rbcid }})
|
126
|
+
end
|
127
|
+
|
128
|
+
result
|
129
|
+
end
|
130
|
+
|
131
|
+
# Method used to describe the query to cross reference the account on local and Psigate's servers
|
132
|
+
def condition
|
133
|
+
# AccountID, RBCID, RBTrigger, Type, InvoiceNo, Status, SerialNo
|
134
|
+
{ :AccountID => "123" }
|
135
|
+
end
|
136
|
+
|
137
|
+
def update
|
138
|
+
{ :Shit => "Fuck" }
|
139
|
+
end
|
140
|
+
|
141
|
+
def address=(input_address)
|
142
|
+
raise InvalidAddress unless input_address.is_a?(Address)
|
143
|
+
@address = input_address
|
144
|
+
end
|
145
|
+
|
146
|
+
def rbcharge=(charge)
|
147
|
+
raise InvalidRecurringCharge unless charge.is_a?(RecurringCharge)
|
148
|
+
@rbcharge = charge
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
class AccountManagerApi
|
3
|
+
|
4
|
+
include RubyPsigate::Utils
|
5
|
+
|
6
|
+
TEST_URL = 'https://dev.psigate.com:8645/Messenger/AMMessenger'
|
7
|
+
LIVE_URL = nil # TODO
|
8
|
+
|
9
|
+
attr_reader :action
|
10
|
+
attr_accessor :test
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
requires!(options, :cid, :login, :password)
|
14
|
+
@options = options
|
15
|
+
@live_url = @options[:live_url]
|
16
|
+
end
|
17
|
+
|
18
|
+
def test?
|
19
|
+
@live_url.blank?
|
20
|
+
end
|
21
|
+
|
22
|
+
# Registers a new account
|
23
|
+
#
|
24
|
+
# Options (* asterisk denotes required)
|
25
|
+
# => :name*
|
26
|
+
# => :company
|
27
|
+
# => :address1*
|
28
|
+
# => :address2
|
29
|
+
# => :city*
|
30
|
+
# => :province*
|
31
|
+
# => :postal_code*
|
32
|
+
# => :country*
|
33
|
+
# => :phone*
|
34
|
+
# => :fax
|
35
|
+
# => :email*
|
36
|
+
# => :comments
|
37
|
+
# => :card_holder*
|
38
|
+
# => :card_number*
|
39
|
+
# => :card_exp_month*
|
40
|
+
# => :card_exp_year*
|
41
|
+
#
|
42
|
+
def register(options = {})
|
43
|
+
requires!(options,
|
44
|
+
:name,
|
45
|
+
:address1,
|
46
|
+
:city,
|
47
|
+
:province,
|
48
|
+
:postal_code,
|
49
|
+
:country,
|
50
|
+
:phone,
|
51
|
+
:email,
|
52
|
+
:card_holder,
|
53
|
+
:card_number,
|
54
|
+
:card_exp_month,
|
55
|
+
:card_exp_year
|
56
|
+
)
|
57
|
+
@register_options = options
|
58
|
+
@action = "AMA01"
|
59
|
+
end
|
60
|
+
|
61
|
+
# Updates an already registered account
|
62
|
+
def update(options = {})
|
63
|
+
@action = "AMA02"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Retrieves a summary of a registered account
|
67
|
+
def retrieve_summary(options = {})
|
68
|
+
requires!(options, :account_id)
|
69
|
+
@options.update(options)
|
70
|
+
@action = "AMA00"
|
71
|
+
|
72
|
+
@params = {
|
73
|
+
:Request => {
|
74
|
+
:CID => @options[:cid],
|
75
|
+
:UserID => @options[:login],
|
76
|
+
:Password => @options[:password],
|
77
|
+
:Action => @action,
|
78
|
+
:Condition => {
|
79
|
+
:AccountID => @options[:account_id]
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
@data = data_to_be_posted(@params)
|
85
|
+
raw_response = post_to_server(@data)
|
86
|
+
response = Response.new(successful?(raw_response), message_from(raw_response), raw_response, :test => test?)
|
87
|
+
end
|
88
|
+
|
89
|
+
def enable(options = {})
|
90
|
+
@action = "AMA08"
|
91
|
+
end
|
92
|
+
|
93
|
+
def disable(options = {})
|
94
|
+
@action = "AMA09"
|
95
|
+
end
|
96
|
+
|
97
|
+
def charge(options = {})
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def data_to_be_posted(params)
|
104
|
+
data = Serializer.new(params)
|
105
|
+
data.to_xml
|
106
|
+
end
|
107
|
+
|
108
|
+
def post_to_server(data)
|
109
|
+
endpoint = test? ? TEST_URL : @live_url
|
110
|
+
psigate = ActiveMerchant::Connection.new(endpoint)
|
111
|
+
|
112
|
+
# Some configurations for ActiveMerchant::Connection instance object
|
113
|
+
psigate.verify_peer = true
|
114
|
+
psigate.retry_safe = false
|
115
|
+
psigate.open_timeout = 60
|
116
|
+
psigate.read_timeout = 60
|
117
|
+
|
118
|
+
# Since all of Psigate's requests are POST requests, we will use only the following
|
119
|
+
psigate.request(:post, data)
|
120
|
+
end
|
121
|
+
|
122
|
+
def successful?(response)
|
123
|
+
response[:approved] == "APPROVED"
|
124
|
+
end
|
125
|
+
|
126
|
+
def message_from(response)
|
127
|
+
if response[:approved] == "APPROVED"
|
128
|
+
return SUCCESS_MESSAGE
|
129
|
+
else
|
130
|
+
return FAILURE_MESSAGE if response[:errmsg].blank?
|
131
|
+
return response[:errmsg].gsub(/[^\w]/, ' ').split.join(" ").capitalize
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
# DOC - TODO
|
3
|
+
class Address
|
4
|
+
|
5
|
+
include RubyPsigate::HashVariables
|
6
|
+
|
7
|
+
hashable :billing, %w( Bname Bcompany Baddress1 Baddress2 Bcity Bprovince Bpostalcode Bcountry Phone Fax )
|
8
|
+
hashable :shipping, %w( Sname Scompany Saddress1 Saddress2 Scity Sprovince Spostalcode Scountry )
|
9
|
+
|
10
|
+
attr_accessor :firstname, :lastname, :line1, :line2, :city, :state, :country, :zipcode, :telephone, :fax, :company
|
11
|
+
|
12
|
+
alias_method :province, :state
|
13
|
+
alias_method :province=, :state=
|
14
|
+
|
15
|
+
alias_method :postalcode, :zipcode
|
16
|
+
alias_method :postalcode=, :zipcode=
|
17
|
+
|
18
|
+
alias_method :address1, :line1
|
19
|
+
alias_method :address2, :line2
|
20
|
+
|
21
|
+
alias_method :phone, :telephone
|
22
|
+
|
23
|
+
def name
|
24
|
+
"#{firstname} #{lastname}".strip
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_hash(type = nil)
|
28
|
+
result = super
|
29
|
+
result = result.delete_if { |key, value| value.nil? } # Delete empty hash values
|
30
|
+
result
|
31
|
+
end
|
32
|
+
|
33
|
+
# For billing
|
34
|
+
alias_method :bname, :name
|
35
|
+
alias_method :bcompany, :company
|
36
|
+
alias_method :baddress1, :address1
|
37
|
+
alias_method :baddress2, :address2
|
38
|
+
alias_method :bcity, :city
|
39
|
+
alias_method :bprovince, :province
|
40
|
+
alias_method :bpostalcode, :postalcode
|
41
|
+
alias_method :bcountry, :country
|
42
|
+
|
43
|
+
# For shipping
|
44
|
+
alias_method :sname, :name
|
45
|
+
alias_method :scompany, :company
|
46
|
+
alias_method :saddress1, :address1
|
47
|
+
alias_method :saddress2, :address2
|
48
|
+
alias_method :scity, :city
|
49
|
+
alias_method :sprovince, :province
|
50
|
+
alias_method :spostalcode, :postalcode
|
51
|
+
alias_method :scountry, :country
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
|
3
|
+
class Connection
|
4
|
+
|
5
|
+
MAX_RETRIES = 3
|
6
|
+
RETRY_SAFE = true
|
7
|
+
|
8
|
+
# => Values in seconds
|
9
|
+
READ_TIMEOUT = 60
|
10
|
+
OPEN_TIMEOUT = 30
|
11
|
+
VERIFY_PEER = true
|
12
|
+
|
13
|
+
def initialize(endpoint)
|
14
|
+
@endpoint = endpoint.is_a?(URI) ? endpoint : URI.parse(endpoint)
|
15
|
+
@retry_safe = RETRY_SAFE
|
16
|
+
@read_timeout = READ_TIMEOUT
|
17
|
+
@open_timeout = OPEN_TIMEOUT
|
18
|
+
@verify_peer = VERIFY_PEER
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :endpoint
|
22
|
+
attr_accessor :retry_safe
|
23
|
+
attr_accessor :read_timeout
|
24
|
+
attr_accessor :open_timeout
|
25
|
+
attr_accessor :verify_peer
|
26
|
+
|
27
|
+
def post(params)
|
28
|
+
retry_exceptions do
|
29
|
+
begin
|
30
|
+
|
31
|
+
uri = @endpoint
|
32
|
+
psigate = Net::HTTP.new(uri.host, uri.port)
|
33
|
+
|
34
|
+
# Configure Timeouts
|
35
|
+
psigate.open_timeout = open_timeout
|
36
|
+
psigate.read_timeout = read_timeout
|
37
|
+
|
38
|
+
# Configure SSL
|
39
|
+
psigate.use_ssl = true
|
40
|
+
if verify_peer
|
41
|
+
psigate.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
42
|
+
psigate.ca_file = ca_file
|
43
|
+
else
|
44
|
+
psigate.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
45
|
+
end
|
46
|
+
|
47
|
+
raw_response = psigate.post(uri.path, params)
|
48
|
+
response = handle_response(raw_response)
|
49
|
+
response
|
50
|
+
rescue EOFError => e
|
51
|
+
raise ConnectionError, "The remote server dropped the connection"
|
52
|
+
rescue Errno::ECONNRESET => e
|
53
|
+
raise ConnectionError, "The remote server reset the connection"
|
54
|
+
rescue Errno::ECONNREFUSED => e
|
55
|
+
raise RetriableConnectionError, "The remote server refused the connection"
|
56
|
+
rescue Timeout::Error, Errno::ETIMEDOUT => e
|
57
|
+
raise ConnectionError, "The connection to the remote server timed out"
|
58
|
+
|
59
|
+
end # begin
|
60
|
+
end # retry_exceptions
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def ca_file
|
66
|
+
ca_file = File.dirname(__FILE__) + '/../certs/cacert.pem'
|
67
|
+
raise CertVerificationFileMissingError unless File.exists?(ca_file)
|
68
|
+
ca_file
|
69
|
+
end
|
70
|
+
|
71
|
+
def handle_response(raw_response)
|
72
|
+
case raw_response.code.to_i
|
73
|
+
when 200..300
|
74
|
+
raw_response.body
|
75
|
+
else
|
76
|
+
raise ResponseError.new(raw_response)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def retry_exceptions(&block)
|
81
|
+
retries = MAX_RETRIES
|
82
|
+
begin
|
83
|
+
yield
|
84
|
+
rescue RetriableConnectionError => e
|
85
|
+
retries -= 1
|
86
|
+
retry unless retries.zero?
|
87
|
+
raise ConnectionError, e.message
|
88
|
+
rescue ConnectionError
|
89
|
+
retries -= 1
|
90
|
+
retry if retry_safe && !retries.zero?
|
91
|
+
raise
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
# This class acts as a standalone object to verify that the credit card number is valid
|
3
|
+
# If the credit card number is not valid, RubyPsigate::CreditCardInvalidError will be raised
|
4
|
+
#
|
5
|
+
#
|
6
|
+
|
7
|
+
class CreditCardNumberInvalid < RubyPsigateError; end
|
8
|
+
class CreditCardExpired < RubyPsigateError; end
|
9
|
+
class CreditCardExpiryInvalid < RubyPsigateError; end
|
10
|
+
class CreditCardOwnerNameMissing < RubyPsigateError; end
|
11
|
+
class CreditCardNotSupported < RubyPsigateError; end
|
12
|
+
|
13
|
+
class CreditCard
|
14
|
+
|
15
|
+
include RubyPsigate::Utils
|
16
|
+
include RubyPsigate::HashVariables
|
17
|
+
|
18
|
+
hashable %w( PaymentType CardNumber CardExpMonth CardExpYear CardIDNumber )
|
19
|
+
hashable :card_info, %w( CardHolder CardNumber CardExpMonth CardExpYear )
|
20
|
+
|
21
|
+
attr_accessor :number, :month, :year, :verification_value, :name
|
22
|
+
attr_reader :type
|
23
|
+
|
24
|
+
# Psigate only supports these three types of cards
|
25
|
+
VALID_TYPES = %w( visa mastercard american_express )
|
26
|
+
|
27
|
+
def initialize(options={})
|
28
|
+
requires!(options, :number, :month, :year, :name)
|
29
|
+
@number = options[:number]
|
30
|
+
@month = options[:month]
|
31
|
+
@year = options[:year]
|
32
|
+
@verification_value = options[:verification_value]
|
33
|
+
@name = options[:name]
|
34
|
+
|
35
|
+
validate_credit_card!
|
36
|
+
end
|
37
|
+
|
38
|
+
# For hashable
|
39
|
+
def paymenttype
|
40
|
+
"CC"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the last 2 digits of the expiry year
|
44
|
+
def cardexpyear
|
45
|
+
year[2..3]
|
46
|
+
end
|
47
|
+
|
48
|
+
alias_method :cardnumber, :number
|
49
|
+
alias_method :cardexpmonth, :month
|
50
|
+
alias_method :cardidnumber, :verification_value
|
51
|
+
alias_method :cardholder, :name
|
52
|
+
# End for hashable
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def validate_credit_card!
|
57
|
+
validate_number
|
58
|
+
validate_type
|
59
|
+
validate_name
|
60
|
+
validate_date
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_number
|
64
|
+
raise CreditCardNumberInvalid unless @number.to_s.creditcard?
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate_type
|
68
|
+
type = @number.to_s.creditcard_type
|
69
|
+
raise CreditCardNotSupported unless VALID_TYPES.include?(type)
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate_name
|
73
|
+
raise CreditCardOwnerNameMissing if @name.nil?
|
74
|
+
end
|
75
|
+
|
76
|
+
def validate_date
|
77
|
+
raise CreditCardExpiryInvalid unless (valid_month?(@month) && valid_expiry_year?(@year))
|
78
|
+
raise CreditCardExpired if expired?
|
79
|
+
end
|
80
|
+
|
81
|
+
def valid_month?(month)
|
82
|
+
(1..12).include?(month.to_i)
|
83
|
+
end
|
84
|
+
|
85
|
+
def valid_expiry_year?(year)
|
86
|
+
(Time.now.year..Time.now.year + 20).include?(year.to_i)
|
87
|
+
end
|
88
|
+
|
89
|
+
def expired?
|
90
|
+
today = Time.now
|
91
|
+
expiry = Time.new(@year, @month, days_in_a_month(@year, @month))
|
92
|
+
today > expiry
|
93
|
+
end
|
94
|
+
|
95
|
+
def days_in_a_month(year, month)
|
96
|
+
if month == 12
|
97
|
+
year += 1
|
98
|
+
next_month = 1
|
99
|
+
end
|
100
|
+
days = (Time.new(year, next_month)-1).day
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
|
3
|
+
class RubyPsigateError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class ConnectionError < RubyPsigateError
|
7
|
+
end
|
8
|
+
|
9
|
+
class RetriableConnectionError < RubyPsigateError
|
10
|
+
end
|
11
|
+
|
12
|
+
class CertVerificationFileMissingError < RubyPsigateError
|
13
|
+
end
|
14
|
+
|
15
|
+
class ResponseError < RubyPsigateError
|
16
|
+
attr_reader :response
|
17
|
+
|
18
|
+
def initialize(response, message = nil)
|
19
|
+
@response = response
|
20
|
+
@message = message
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
"Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class LiveURLMissingError < RubyPsigateError
|
29
|
+
end
|
30
|
+
|
31
|
+
class InvalidHashType < RubyPsigateError; end
|
32
|
+
|
33
|
+
end
|