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,126 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
|
3
|
+
class InvalidOrder < RubyPsigateError; end
|
4
|
+
class InvalidAccount < RubyPsigateError; end
|
5
|
+
class InvalidGatewayMode < RubyPsigateError; end
|
6
|
+
class InvalidCredentials < RubyPsigateError; end
|
7
|
+
class InvalidCreditCard < RubyPsigateError; end
|
8
|
+
class IncompleteParameters < RubyPsigateError; end
|
9
|
+
|
10
|
+
class Gateway
|
11
|
+
|
12
|
+
# Class defaults to
|
13
|
+
@env = :test
|
14
|
+
|
15
|
+
def self.env
|
16
|
+
@env
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.live!
|
20
|
+
@env = :live
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.test!
|
24
|
+
@env = :test
|
25
|
+
end
|
26
|
+
|
27
|
+
include ::RubyPsigate::TransactionMethods
|
28
|
+
# include RubyPsigate::AccountManagerMethods
|
29
|
+
|
30
|
+
attr_reader :mode, :order, :options, :account
|
31
|
+
|
32
|
+
def initialize(options={})
|
33
|
+
@options = options
|
34
|
+
set_mode(@options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def mode=(type)
|
38
|
+
valid_modes = %w( transaction account_manager )
|
39
|
+
raise InvalidGatewayMode unless valid_modes.include?(type.to_s)
|
40
|
+
@mode = type.to_sym
|
41
|
+
end
|
42
|
+
|
43
|
+
def order=(order_object)
|
44
|
+
raise InvalidOrder unless order_object.is_a?(RubyPsigate::Order)
|
45
|
+
@order = order_object
|
46
|
+
end
|
47
|
+
|
48
|
+
def account=(account_object)
|
49
|
+
raise InvalidAccount unless account_object.is_a?(RubyPsigate::Account)
|
50
|
+
@account = account_object
|
51
|
+
end
|
52
|
+
|
53
|
+
TRANSACTION_LOOKUP = {
|
54
|
+
:sale => "0",
|
55
|
+
:preauth => "1",
|
56
|
+
:postauth => "2",
|
57
|
+
:credit => "3",
|
58
|
+
:force_postauth => "4",
|
59
|
+
:void => "9"
|
60
|
+
}
|
61
|
+
|
62
|
+
def commit!
|
63
|
+
# 1) Check the mode
|
64
|
+
# 2) Ensure credentials are set
|
65
|
+
# 3) Check which action user is wanting to take
|
66
|
+
# 4) Ensure adequate minimum parameters for the given action
|
67
|
+
# 5) Parameterize and ensure it is properly formatted
|
68
|
+
# 6) Send!
|
69
|
+
|
70
|
+
raise InvalidGatewayMode unless mode_set?
|
71
|
+
raise InvalidCredentials unless sufficient_login_credentials?
|
72
|
+
|
73
|
+
if mode == :transaction
|
74
|
+
raise InvalidOrder unless order.valid?
|
75
|
+
|
76
|
+
@params = {}
|
77
|
+
@params[:Order] = {}
|
78
|
+
@params[:Order] = { :StoreID => options[:store_id], :Passphrase => options[:passphrase] }
|
79
|
+
@params[:Order].merge!(order.to_hash(order.action))
|
80
|
+
|
81
|
+
@endpoint = "https://dev.psigate.com:7989/Messenger/XMLMessenger"
|
82
|
+
end
|
83
|
+
|
84
|
+
if mode == :account_manager
|
85
|
+
# raise InvalidAccount unless account.valid?
|
86
|
+
|
87
|
+
# Parameterize based on account manager
|
88
|
+
@params = {}
|
89
|
+
@params[:Request] = {}
|
90
|
+
@params[:Request] = { :CID => options[:cid], :UserID => options[:user_id], :Password => options[:password] }
|
91
|
+
@params[:Request].merge!(account.to_hash(account.action))
|
92
|
+
|
93
|
+
@endpoint = "https://dev.psigate.com:8645/Messenger/AMMessenger"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Serializes the parameters to xml
|
97
|
+
@params = Serializer.new(@params, :header => true).to_xml
|
98
|
+
|
99
|
+
@connection = Connection.new(@endpoint)
|
100
|
+
@response = @connection.post(@params)
|
101
|
+
Response.new(@response)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def set_mode(options)
|
107
|
+
self.mode = :account_manager if options[:cid] && options[:user_id] && options[:password]
|
108
|
+
self.mode = :transaction if options[:store_id] && options[:passphrase]
|
109
|
+
end
|
110
|
+
|
111
|
+
def mode_set?
|
112
|
+
(mode == :account_manager || mode == :transaction) ? true : false
|
113
|
+
end
|
114
|
+
|
115
|
+
def sufficient_login_credentials?
|
116
|
+
result = false
|
117
|
+
if mode == :account_manager
|
118
|
+
result = (options[:cid] && options[:user_id] && options[:password] ? true : false)
|
119
|
+
elsif mode == :transaction
|
120
|
+
result = (options[:store_id] && options[:passphrase] ? true : false)
|
121
|
+
end
|
122
|
+
result
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
# In your class, you can define the instance variables you want to wrap in a hash output
|
3
|
+
#
|
4
|
+
# Usage:
|
5
|
+
#
|
6
|
+
# hashable :billing, [:name, :address, :city, :state, :country, :zipcode]
|
7
|
+
#
|
8
|
+
# What this will do is create a hash like this when you call the class' instance #to_hash(:billing)
|
9
|
+
# { :name => "Bob", :address => "1234 West St", :city => "North York", :state => "NY", :country => "USA", zipcode => "12345"}
|
10
|
+
#
|
11
|
+
# If you don't specify a "name", you can simply call #to_hash:
|
12
|
+
#
|
13
|
+
# hashable [:name, :address, :city, :state, :country, :zipcode]
|
14
|
+
# instance#to_hash will yield the same output as above
|
15
|
+
#
|
16
|
+
module HashVariables
|
17
|
+
|
18
|
+
def self.included(klass)
|
19
|
+
klass.extend ClassMethods
|
20
|
+
klass.send(:include, InstanceMethods)
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def hashable(*args)
|
25
|
+
@hashable_attributes ||= {}
|
26
|
+
if args[0].is_a?(Symbol) && args[1].is_a?(Array)
|
27
|
+
@hashable_attributes[args[0]] = args[1]
|
28
|
+
elsif args[0].is_a?(Array)
|
29
|
+
@hashable_attributes[:default] = args[0]
|
30
|
+
else
|
31
|
+
raise ArgumentError
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def hashable_attributes
|
36
|
+
@hashable_attributes
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module InstanceMethods
|
41
|
+
def to_hash(name=nil)
|
42
|
+
return_hash = {}
|
43
|
+
if name.nil?
|
44
|
+
attributes = self.class.hashable_attributes[:default]
|
45
|
+
else
|
46
|
+
attributes = self.class.hashable_attributes[name.to_sym]
|
47
|
+
end
|
48
|
+
attributes.each do |a|
|
49
|
+
key = "#{a}"
|
50
|
+
value = self.send(a.downcase)
|
51
|
+
return_hash[key.to_sym] = value
|
52
|
+
end
|
53
|
+
return_hash
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
|
3
|
+
class InvalidItemOption < RubyPsigateError; end
|
4
|
+
|
5
|
+
class Item
|
6
|
+
include NumberValidationMethods
|
7
|
+
include HashVariables
|
8
|
+
|
9
|
+
hashable %w( ItemID ItemDescription ItemQty ItemPrice )
|
10
|
+
|
11
|
+
attr_reader :qty, :price
|
12
|
+
attr_accessor :unique_id, :desc
|
13
|
+
|
14
|
+
# For hashable
|
15
|
+
alias_method :itemid, :unique_id
|
16
|
+
alias_method :itemdescription, :desc
|
17
|
+
alias_method :itemqty, :qty
|
18
|
+
alias_method :itemprice, :price
|
19
|
+
|
20
|
+
def to_hash
|
21
|
+
@return_hash = super
|
22
|
+
unless options.count == 0
|
23
|
+
@return_hash[:Option] = {}
|
24
|
+
options.each do |opt|
|
25
|
+
opt = opt.first
|
26
|
+
key = opt[0].to_sym
|
27
|
+
value = opt[1]
|
28
|
+
@return_hash[:Option][key] = value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@return_hash
|
32
|
+
end
|
33
|
+
# End hashable
|
34
|
+
|
35
|
+
def initialize(parameters = {})
|
36
|
+
@options = []
|
37
|
+
end
|
38
|
+
|
39
|
+
def qty=(x)
|
40
|
+
x = x.to_i
|
41
|
+
@qty = validate(x)
|
42
|
+
end
|
43
|
+
|
44
|
+
def price=(x)
|
45
|
+
x = x.to_f
|
46
|
+
@price = Money.new(x*100)
|
47
|
+
end
|
48
|
+
|
49
|
+
def << (item_option)
|
50
|
+
raise InvalidItemOption unless item_option.is_a?(RubyPsigate::ItemOption)
|
51
|
+
@options << item_option
|
52
|
+
return self
|
53
|
+
end
|
54
|
+
|
55
|
+
def options
|
56
|
+
holder = []
|
57
|
+
@options.each do |opt|
|
58
|
+
holder << opt.instance_variable_get(:@option)
|
59
|
+
end
|
60
|
+
holder
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
|
3
|
+
class InvalidAction < RubyPsigateError; end
|
4
|
+
class InvalidItem < RubyPsigateError; end
|
5
|
+
class InvalidAmount < RubyPsigateError; end
|
6
|
+
class InvalidAddress < RubyPsigateError; end
|
7
|
+
|
8
|
+
class Order
|
9
|
+
|
10
|
+
include CreditCardMethods
|
11
|
+
include HashVariables
|
12
|
+
|
13
|
+
hashable :sale, %w( Subtotal Email CardAction Comments Tax1 Tax2 Tax3 Tax4 Tax5 )
|
14
|
+
hashable :preauth, %w( Subtotal Email CardAction Comments Tax1 Tax2 Tax3 Tax4 Tax5 )
|
15
|
+
hashable :postauth, %w( Subtotal Email CardAction OrderID )
|
16
|
+
hashable :credit, %w( Subtotal Email CardAction OrderID )
|
17
|
+
hashable :force_postauth, %w( Subtotal Email CardAction CardAuthNumber)
|
18
|
+
hashable :void, %w( CardAction OrderID TransRefNumber )
|
19
|
+
|
20
|
+
attr_reader :action, :items, :amount, :billing, :shipping, :tax1, :tax2, :tax3, :tax4, :tax5
|
21
|
+
attr_accessor :comment, :email, :order_id, :card_auth_number, :trans_ref_number
|
22
|
+
|
23
|
+
alias_method :orderid, :order_id
|
24
|
+
alias_method :cardauthnumber, :card_auth_number
|
25
|
+
alias_method :transrefnumber, :trans_ref_number
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@items = []
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_hash(type = nil)
|
32
|
+
result = super
|
33
|
+
result = result.merge(billing.to_hash(:billing)) unless billing.nil? # Add billing address if present
|
34
|
+
result = result.merge(shipping.to_hash(:shipping)) unless shipping.nil? # Add shipping address if present
|
35
|
+
|
36
|
+
# Add items if present
|
37
|
+
if items.count > 0
|
38
|
+
result[:Item] = []
|
39
|
+
items.each do |item|
|
40
|
+
result[:Item] << item.to_hash
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
result.merge!(cc.to_hash) if cc
|
45
|
+
|
46
|
+
result = result.delete_if { |key, value| value.nil? } # Delete empty hash values
|
47
|
+
result
|
48
|
+
end
|
49
|
+
|
50
|
+
def << (new_item)
|
51
|
+
raise InvalidItem unless new_item.is_a?(RubyPsigate::Item)
|
52
|
+
@items << new_item
|
53
|
+
return self
|
54
|
+
end
|
55
|
+
|
56
|
+
def action=(x)
|
57
|
+
x = x.downcase.to_sym
|
58
|
+
raise InvalidAction unless [:sale, :preauth, :postauth, :credit, :force_postauth, :void].include?(x)
|
59
|
+
@action = x
|
60
|
+
end
|
61
|
+
|
62
|
+
CARD_ACTION = {
|
63
|
+
:sale => 0,
|
64
|
+
:preauth => 1,
|
65
|
+
:postauth => 2,
|
66
|
+
:credit => 3,
|
67
|
+
:force_postauth => 4,
|
68
|
+
:void => 9
|
69
|
+
}
|
70
|
+
|
71
|
+
# Returns the Psigate format equivalent for the action method
|
72
|
+
def cardaction
|
73
|
+
CARD_ACTION[action]
|
74
|
+
end
|
75
|
+
|
76
|
+
alias_method :subtotal, :amount
|
77
|
+
|
78
|
+
def amount=(x)
|
79
|
+
raise InvalidAmount if x.to_f < 0
|
80
|
+
@amount = Money.new(x.to_f*100)
|
81
|
+
end
|
82
|
+
|
83
|
+
def billing=(billing_address)
|
84
|
+
raise InvalidAddress unless billing_address.is_a?(RubyPsigate::Address)
|
85
|
+
@billing = billing_address
|
86
|
+
end
|
87
|
+
|
88
|
+
def shipping=(shipping_address)
|
89
|
+
raise InvalidAddress unless shipping_address.is_a?(RubyPsigate::Address)
|
90
|
+
@shipping = shipping_address
|
91
|
+
end
|
92
|
+
|
93
|
+
alias_method :comments, :comment
|
94
|
+
|
95
|
+
%w( tax1 tax2 tax3 tax4 tax5 ).each do |tax|
|
96
|
+
self.class_eval <<-EOF
|
97
|
+
def #{tax}=(tax_amount)
|
98
|
+
raise InvalidAmount if tax_amount.to_f < 0
|
99
|
+
@#{tax} = Money.new(tax_amount*100)
|
100
|
+
end
|
101
|
+
EOF
|
102
|
+
end
|
103
|
+
|
104
|
+
# Used internally by the Gateway class to determine if minimal parameters are set to be processed
|
105
|
+
def valid?
|
106
|
+
errors = 0
|
107
|
+
|
108
|
+
if amount.zero?
|
109
|
+
errors += 1 unless action == :postauth || action == :void
|
110
|
+
end
|
111
|
+
errors += 1 if action.nil?
|
112
|
+
errors += 1 if email.nil?
|
113
|
+
errors += 1 if ( action == :postauth && order_id.nil? ) || ( action == :credit && order_id.nil? ) || ( action == :void && order_id.nil? )
|
114
|
+
errors += 1 if ( action == :force_postauth && card_auth_number.nil? )
|
115
|
+
errors += 1 if ( action == :void && trans_ref_number.nil? )
|
116
|
+
errors > 0 ? false : true
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
|
3
|
+
class InvalidRecurringItem < RubyPsigateError; end
|
4
|
+
|
5
|
+
# This class encapsulates the recurring charge elements for use in the Account Class
|
6
|
+
class RecurringCharge
|
7
|
+
|
8
|
+
include HashVariables
|
9
|
+
hashable %w( RBCID RBName Interval RBTrigger ProcessType Status StartTime EndTime )
|
10
|
+
|
11
|
+
attr_accessor :rbcid, :rbname, :interval, :rbtrigger, :status, :starttime, :endtime, :processtype
|
12
|
+
|
13
|
+
def initialize(attributes = {})
|
14
|
+
@rbcid = attributes[:rbcid]
|
15
|
+
@rbname = attributes[:rbname]
|
16
|
+
@interval = attributes[:interval]
|
17
|
+
@rbtrigger = attributes[:rbtrigger]
|
18
|
+
@status = attributes[:status]
|
19
|
+
@starttime = attributes[:starttime]
|
20
|
+
@endtime = attributes[:endtime]
|
21
|
+
@processtype = attributes[:processtype]
|
22
|
+
@items = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_hash
|
26
|
+
@return_hash = super
|
27
|
+
unless items.count == 0
|
28
|
+
@return_hash[:ItemInfo] = []
|
29
|
+
items.each do |item|
|
30
|
+
@return_hash[:ItemInfo] << item.to_hash
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@return_hash = @return_hash.delete_if { |key, value| value.nil? } # Delete empty hash values
|
35
|
+
@return_hash
|
36
|
+
end
|
37
|
+
|
38
|
+
def << (item)
|
39
|
+
raise InvalidRecurringItem unless item.is_a?(RecurringItem)
|
40
|
+
@items << item
|
41
|
+
return self
|
42
|
+
end
|
43
|
+
|
44
|
+
def items
|
45
|
+
holder = []
|
46
|
+
@items.each do |item|
|
47
|
+
holder << item.to_hash
|
48
|
+
end
|
49
|
+
holder
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
class RecurringItem
|
3
|
+
|
4
|
+
include HashVariables
|
5
|
+
hashable %w( ProductID Description Quantity Price Tax1 Tax2 )
|
6
|
+
|
7
|
+
attr_accessor :product_id, :description, :quantity, :price, :tax1, :tax2, :cost
|
8
|
+
alias_method :productid, :product_id
|
9
|
+
|
10
|
+
def initialize(attributes = {})
|
11
|
+
@product_id = attributes[:product_id]
|
12
|
+
@description = attributes[:description]
|
13
|
+
@quantity = attributes[:quantity]
|
14
|
+
@price = attributes[:price]
|
15
|
+
@tax1 = attributes[:tax1]
|
16
|
+
@tax2 = attributes[:tax2]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
result = super
|
21
|
+
result = result.delete_if { |key, value| value.nil? } # Delete empty hash values
|
22
|
+
result
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
|
3
|
+
# The Response class deals with the XML responses from Psigate's servers
|
4
|
+
#
|
5
|
+
class Response
|
6
|
+
|
7
|
+
ACCOUNT_SUCCESS_CODES = %w(
|
8
|
+
RPA-0000
|
9
|
+
RPA-0001
|
10
|
+
RPA-0010
|
11
|
+
RPA-0015
|
12
|
+
RPA-0020
|
13
|
+
RPA-0022
|
14
|
+
RPA-0025
|
15
|
+
RPA-0040
|
16
|
+
RPA-0042
|
17
|
+
RPA-0046
|
18
|
+
RPA-0048
|
19
|
+
RPA-0058
|
20
|
+
PSI-0000
|
21
|
+
RRC-0000
|
22
|
+
RRC-0005
|
23
|
+
RRC-0050
|
24
|
+
RRC-0060
|
25
|
+
RRC-0065
|
26
|
+
RRC-0072
|
27
|
+
RRC-0075
|
28
|
+
RRC-0082
|
29
|
+
RRC-0090
|
30
|
+
RRC-0092
|
31
|
+
RRC-0095
|
32
|
+
RRC-0098
|
33
|
+
RRC-0190
|
34
|
+
CTL-0000
|
35
|
+
CTL-0005
|
36
|
+
CTL-0050
|
37
|
+
CTL-0060
|
38
|
+
CTL-0065
|
39
|
+
CTL-0072
|
40
|
+
CTL-0075
|
41
|
+
CTL-0082
|
42
|
+
CTL-0090
|
43
|
+
CTL-0092
|
44
|
+
CTL-0098
|
45
|
+
CTL-0190
|
46
|
+
CTL-0192
|
47
|
+
RIV-0050
|
48
|
+
RIV-0060
|
49
|
+
RIV-0072
|
50
|
+
RIV-0090
|
51
|
+
RIV-0190
|
52
|
+
RIV-0197
|
53
|
+
RIV-0198
|
54
|
+
EMR-0000
|
55
|
+
EMR-0005
|
56
|
+
EMR-0050
|
57
|
+
EMR-0060
|
58
|
+
EMR-0072
|
59
|
+
EMR-0082
|
60
|
+
EMR-0090
|
61
|
+
EMR-0190
|
62
|
+
)
|
63
|
+
|
64
|
+
# The Response object is initialized with the response output from the response body of the HTTP request to Psigate's servers
|
65
|
+
# The expected format of the raw_response is a properly formed XML document
|
66
|
+
# Please refer to Psigate's technical documents (XML Interface PDF and Account Manager API PDF)
|
67
|
+
#
|
68
|
+
# Once the raw_resposne is received, we use nokogiri to parse and format the output to a usable hash
|
69
|
+
def initialize(raw_response)
|
70
|
+
@response = Crack::XML.parse(raw_response)
|
71
|
+
end
|
72
|
+
|
73
|
+
def hash
|
74
|
+
@response
|
75
|
+
end
|
76
|
+
|
77
|
+
def parsed
|
78
|
+
return @parsed if @parsed
|
79
|
+
@parsed = @response["Result"] || @response["Response"]
|
80
|
+
end
|
81
|
+
|
82
|
+
def success?
|
83
|
+
parsed["Approved"] == "APPROVED" || ACCOUNT_SUCCESS_CODES.include?(self.returncode)
|
84
|
+
end
|
85
|
+
|
86
|
+
def method_missing(name, *args, &block)
|
87
|
+
@result = nil
|
88
|
+
name = name.downcase.to_sym
|
89
|
+
@result = find_value_in_hash(name, parsed)
|
90
|
+
@result
|
91
|
+
end
|
92
|
+
|
93
|
+
def find_value_in_hash(input_key, hash)
|
94
|
+
result = nil
|
95
|
+
hash.each_pair do |key, value|
|
96
|
+
if value.is_a? Hash
|
97
|
+
result = find_value_in_hash(input_key, value)
|
98
|
+
else
|
99
|
+
key = key.downcase.to_sym
|
100
|
+
result = value if input_key == key
|
101
|
+
end
|
102
|
+
|
103
|
+
break unless result.nil?
|
104
|
+
end
|
105
|
+
result
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
|
3
|
+
class InvalidHashError < ArgumentError; end;
|
4
|
+
|
5
|
+
class Serializer
|
6
|
+
# Initializes a new object with an inputted hash
|
7
|
+
#
|
8
|
+
# my_hash = { :something => "special" }
|
9
|
+
# Serializer.new(my_hash)
|
10
|
+
def initialize(input_hash, options = { :header => false })
|
11
|
+
raise InvalidHashError, "Input parameters does not contain a hash!" unless input_hash.is_a?(Hash)
|
12
|
+
@input_hash = input_hash
|
13
|
+
@header = options[:header]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Converts from a hash to a hierarchical XML
|
17
|
+
#
|
18
|
+
# An example:
|
19
|
+
#
|
20
|
+
# Converts
|
21
|
+
# => { :Something => "Hello World" }
|
22
|
+
# to this:
|
23
|
+
# => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Something>Hello World</Something>"
|
24
|
+
#
|
25
|
+
# Can also handle nested hashes
|
26
|
+
def to_xml
|
27
|
+
@builder = Builder::XmlMarkup.new
|
28
|
+
@builder.instruct! if @header
|
29
|
+
build_level(@input_hash)
|
30
|
+
@builder.target!
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_level(hash_level)
|
34
|
+
raise InvalidHashError, "Need hash to build XML" unless hash_level.is_a?(Hash)
|
35
|
+
for key, value in hash_level
|
36
|
+
|
37
|
+
# If the element at this level is :Item, then we know that it will be an array
|
38
|
+
unless key == :Item
|
39
|
+
@builder.tag!(key) { |level| value.is_a?(Hash) ? build_level(value) : level.text!(value.to_s) }
|
40
|
+
else
|
41
|
+
build_level_from_array_element(value)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_level_from_array_element(input_array)
|
48
|
+
input_array.each do |element|
|
49
|
+
@builder.tag!(:Item) {
|
50
|
+
element.each_pair do |key, value|
|
51
|
+
# @builder.tag!(key) { |level| level.text!(value) }
|
52
|
+
@builder.tag!(key) { |level| value.is_a?(Hash) ? build_level(value) : level.text!(value) }
|
53
|
+
end
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
|
3
|
+
module TransactionMethods
|
4
|
+
|
5
|
+
# URL for test environment
|
6
|
+
TRANSACTION_TEST_URL = 'https://dev.psigate.com:7989/Messenger/XMLMessenger'
|
7
|
+
|
8
|
+
# URL for live environment is not specified
|
9
|
+
# It must be obtained from Psigate's tech support and be specified
|
10
|
+
|
11
|
+
def amount
|
12
|
+
@amount
|
13
|
+
end
|
14
|
+
|
15
|
+
def amount=(x)
|
16
|
+
raise InvalidAmount if x.to_f < 0
|
17
|
+
@amount = x
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RubyPsigate
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
def requires!(hash, *params)
|
5
|
+
params.each do |param|
|
6
|
+
if param.is_a?(Array)
|
7
|
+
raise ArgumentError.new("Missing required parameter: #{param.first}") unless hash.has_key?(param.first)
|
8
|
+
|
9
|
+
valid_options = param[1..-1]
|
10
|
+
raise ArgumentError.new("Parameter: #{param.first} must be one of #{valid_options.to_sentence(:words_connector => 'or')}") unless valid_options.include?(hash[param.first])
|
11
|
+
else
|
12
|
+
raise ArgumentError.new("Missing required parameter: #{param}") unless hash.has_key?(param)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|