activemerchant-paymentech-orbital 0.1.0

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,82 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ module PaymentechOrbital
4
+ module Request
5
+ class ProfileManagement < PaymentechOrbital::Request::Base
6
+ attr_accessor :action, :credit_card
7
+
8
+ cattr_accessor :action_map
9
+ self.action_map = {
10
+ "create" => "C",
11
+ "retrieve" => "R",
12
+ "update" => "U",
13
+ "delete" => "D"
14
+ }
15
+
16
+ def initialize(action, credit_card=nil, options={})
17
+ @action = action.to_s
18
+ @credit_card = credit_card
19
+ super(options)
20
+ end
21
+
22
+ def request_type; "Profile"; end
23
+
24
+ private
25
+ def customer_profile_action
26
+ self.class.action_map[action.downcase.to_s]
27
+ end
28
+
29
+ def writing?
30
+ ["create", "update"].include?(action)
31
+ end
32
+
33
+ def request_body(xml)
34
+ add_meta_info(xml)
35
+ add_profile_info(xml)
36
+
37
+ xml.tag! "CustomerProfileAction", customer_profile_action
38
+
39
+ add_customer_profile_management_options(xml)
40
+ add_account_info(xml) if writing?
41
+ add_credit_card_info(xml) if writing? && credit_card
42
+ end
43
+
44
+ def add_meta_info(xml)
45
+ xml.tag! "CustomerBin", bin
46
+ xml.tag! "CustomerMerchantID", merchant_id
47
+ end
48
+
49
+ def add_profile_info(xml)
50
+ xml.tag! "CustomerName", address[:name]
51
+ xml.tag! "CustomerRefNum", customer_ref_num if customer_ref_num
52
+ xml.tag! "CustomerAddress1", address[:address1]
53
+ xml.tag! "CustomerAddress2", address[:address]
54
+ xml.tag! "CustomerCity", address[:city]
55
+ xml.tag! "CustomerState", address[:state]
56
+ xml.tag! "CustomerZIP", address[:zip]
57
+ xml.tag! "CustomerEmail", address[:email]
58
+ xml.tag! "CustomerPhone", address[:phone]
59
+ xml.tag! "CustomerCountryCode", address[:country]
60
+ end
61
+
62
+ def add_customer_profile_management_options(xml)
63
+ unless customer_ref_num
64
+ xml.tag! "CustomerProfileOrderOverrideInd", "NO"
65
+ xml.tag! "CustomerProfileFromOrderInd", "A"
66
+ end
67
+ end
68
+
69
+ def add_account_info(xml)
70
+ xml.tag! "CustomerAccountType", "CC"
71
+ xml.tag! "Status", options[:status] || "A"
72
+ end
73
+
74
+ def add_credit_card_info(xml)
75
+ xml.tag! "CCAccountNum", credit_card.number
76
+ xml.tag! "CCExpireDate", "#{credit_card.month}#{credit_card.year}"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,39 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ module PaymentechOrbital
4
+ module Request
5
+ class Void < PaymentechOrbital::Request::Base
6
+ attr_reader :tx_ref_num, :tx_ref_idx, :money
7
+
8
+ def initialize(tx_ref_num, tx_ref_idx, money=nil, options={})
9
+ @tx_ref_num = tx_ref_num
10
+ @tx_ref_idx = tx_ref_idx
11
+ @money = money
12
+ super(options)
13
+ end
14
+
15
+ def request_type; "Reversal"; end
16
+
17
+ private
18
+ def request_body(xml)
19
+ add_transaction_info(xml)
20
+ xml.tag! "AdjustedAmt", money if money
21
+ add_meta_info(xml)
22
+ end
23
+
24
+ def add_transaction_info(xml)
25
+ xml.tag! "TxRefNum", tx_ref_num
26
+ xml.tag! "TxRefIdx", tx_ref_idx
27
+ end
28
+
29
+ def add_meta_info(xml)
30
+ xml.tag! "OrderID", order_id
31
+ xml.tag! "BIN", bin
32
+ xml.tag! "MerchantID", merchant_id
33
+ xml.tag! "TerminalID", terminal_id
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,100 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ module PaymentechOrbital
4
+ class Response
5
+ cattr_accessor :elements
6
+ attr_reader :doc, :request_type
7
+
8
+ self.elements = [
9
+ :industry_type, :message_type, :merchant_id,
10
+ :terminal_id, :card_brand, :account_num,
11
+ :order_id, :tx_ref_num, :tx_ref_idx, :proc_status,
12
+ :approval_status, :resp_code, :avs_resp_code,
13
+ :cvv2_resp_code, :auth_code, :status_msg, :resp_msg,
14
+ :customer_ref_num, :customer_name, :profile_proc_status,
15
+ :customer_profile_message, :resp_time, :batch_seq_num,
16
+ :CCAccountNum, :customer_address_1, :customer_address_2,
17
+ :customer_city, :CustomerZIP, :customer_state,
18
+ :CCExpireDate
19
+ ]
20
+
21
+ def initialize(doc, request_type, options={})
22
+ @doc = REXML::Document.new(doc)
23
+ @request_type = request_type
24
+ @options = options
25
+ end
26
+
27
+ def success?
28
+ case request_type
29
+ when "NewOrder"
30
+ proc_success? && approved?
31
+ when "Profile"
32
+ profile_proc_success?
33
+ when "Reversal", "EndOfDay"
34
+ proc_success?
35
+ else
36
+ false
37
+ end
38
+ end
39
+
40
+ def proc_success?
41
+ proc_status == "0"
42
+ end
43
+
44
+ def profile_proc_success?
45
+ profile_proc_status == "0"
46
+ end
47
+
48
+ def approved?
49
+ approval_status == "1"
50
+ end
51
+
52
+ def authorization
53
+ approval_status
54
+ end
55
+
56
+ def test?
57
+ @options[:test] || false
58
+ end
59
+
60
+ def avs_result
61
+ @avs_result ||= AVSResult.new({:code => avs_resp_code})
62
+ end
63
+
64
+ def cvv_result
65
+ @cvv_result ||= CVVResult.new({:code => cvv2_result_code})
66
+ end
67
+
68
+ def to_xml
69
+ unless @_xml
70
+ @_xml = ""
71
+ doc.write(@_xml, 2)
72
+ end
73
+ @_xml
74
+ end
75
+
76
+ private
77
+ def tagify(s)
78
+ s.to_s.gsub(/\/(.?)/) {
79
+ "::#{$1.upcase}"
80
+ }.gsub(/(?:^|_)(.)/) {
81
+ $1.upcase
82
+ }
83
+ end
84
+
85
+ def value_at(sym)
86
+ node = REXML::XPath.first(@doc, "//#{tagify(sym)}")
87
+ node ? node.text : nil
88
+ end
89
+
90
+ def method_missing(sym, *args, &blk)
91
+ if self.class.elements.include?(sym)
92
+ value_at(sym)
93
+ else
94
+ super(sym, *args, &blk)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,9 @@
1
+ require 'active_merchant'
2
+
3
+ require 'active_merchant/billing/paymentech_orbital'
4
+ require 'active_merchant/billing/paymentech_orbital/request'
5
+ require 'active_merchant/billing/paymentech_orbital/request/end_of_day'
6
+ require 'active_merchant/billing/paymentech_orbital/request/new_order'
7
+ require 'active_merchant/billing/paymentech_orbital/request/profile_management'
8
+ require 'active_merchant/billing/paymentech_orbital/request/void'
9
+ require 'active_merchant/billing/paymentech_orbital/response'
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class ActivemerchantPaymentechOrbitalTest < Test::Unit::TestCase
4
+ def test_something_for_real
5
+ assert true
6
+ end
7
+ end
@@ -0,0 +1,170 @@
1
+ Factory.define :credit_card, {
2
+ :class => ActiveMerchant::Billing::CreditCard,
3
+ :default_strategy => :build
4
+ } do |f|
5
+ f.number "4242424242424242"
6
+ f.month 9
7
+ f.year Time.now.year + 1
8
+ f.first_name 'Joe'
9
+ f.last_name 'Smith'
10
+ f.verification_value '123'
11
+ f.add_attribute :type, 'visa'
12
+ end
13
+
14
+ # Option factories
15
+ Options.define(:billing_address, :defaults => {
16
+ :zip => "12345",
17
+ :address1 => "123 Happy Place",
18
+ :address2 => "Apt 1",
19
+ :city => "SuperVille",
20
+ :state => "NY",
21
+ :name => "Joe Smith",
22
+ :country => "US"
23
+ })
24
+ Options.define(:gateway_auth, :defaults => {
25
+ :login => "user",
26
+ :password => "mytestpass",
27
+ :merchant_id => "1",
28
+ :bin => "1",
29
+ :terminal_id => "1"
30
+ })
31
+ Options.define(:soft_descriptors, :defaults => {
32
+ :merchant_name => 'merchant',
33
+ :merchant_url => 'mmmerchant.com'
34
+ })
35
+
36
+ # Request options:
37
+ Options.define(:request_options, :parent => :gateway_auth)
38
+ Options.define(:end_of_day_options, :parent => :request_options)
39
+ Options.define(:new_order_options, :parent => [
40
+ :request_options, :billing_address, :soft_descriptors
41
+ ], :defaults => {
42
+ :currency_code => "840",
43
+ :currency_exponent => "2",
44
+ :order_id => "1",
45
+ :recurring_frequency => "#{Date.today.strftime("%d")} * ?"
46
+ })
47
+ Options.define(:profile_management_options, :parent => [
48
+ :request_options, :billing_address
49
+ ], :defaults => {
50
+ :customer_ref_num => "123456"
51
+ })
52
+ Options.define(:void_options, :parent => :request_options, :defaults => {
53
+ :tx_ref_num => "1",
54
+ :tx_ref_idx => "1",
55
+ :order_id => "1"
56
+ })
57
+
58
+ # Pseudo factories
59
+ def gateway(options={})
60
+ ActiveMerchant::Billing::PaymentechOrbital::Gateway.new(Options(:gateway_auth, options))
61
+ end
62
+
63
+ def base_request(options={})
64
+ ActiveMerchant::Billing::PaymentechOrbital::Request::Base.new(
65
+ Options(:request_options, options)
66
+ )
67
+ end
68
+
69
+ def end_of_day_request(options={})
70
+ ActiveMerchant::Billing::PaymentechOrbital::Request::EndOfDay.new(
71
+ Options(:end_of_day_options, options)
72
+ )
73
+ end
74
+
75
+ def new_order_request(options={})
76
+ ActiveMerchant::Billing::PaymentechOrbital::Request::NewOrder.new(
77
+ "AC", 100, Factory(:credit_card), Options(:new_order_options, options)
78
+ )
79
+ end
80
+
81
+ def profile_management_request(action=:create, credit_card=Factory(:credit_card), options={})
82
+ ActiveMerchant::Billing::PaymentechOrbital::Request::ProfileManagement.new(
83
+ action, credit_card, Options(:profile_management_options, options)
84
+ )
85
+ end
86
+
87
+ def void_request(tx_ref_num="12345", tx_ref_idx="1", money="100", options={})
88
+ ActiveMerchant::Billing::PaymentechOrbital::Request::Void.new(
89
+ tx_ref_num, tx_ref_idx, money, Options(:void_options, options)
90
+ )
91
+ end
92
+
93
+ def new_order_response(doc=new_order_response_success, request_type="NewOrder", options={})
94
+ ActiveMerchant::Billing::PaymentechOrbital::Response.new(doc, request_type, options)
95
+ end
96
+
97
+ def new_order_response_success
98
+ <<-RESPONSE
99
+ <?xml version="1.0" encoding="UTF-8"?>
100
+ <Response>
101
+ <NewOrderResp>
102
+ <IndustryType/>
103
+ <MessageType>AC</MessageType>
104
+ <MerchantID>000000</MerchantID>
105
+ <TerminalID>000</TerminalID>
106
+ <CardBrand>MC</CardBrand>
107
+ <AccountNum>5454545454545454</AccountNum>
108
+ <OrderID>1</OrderID>
109
+ <TxRefNum>4A785F5106CCDC41A936BFF628BF73036FEC5401</TxRefNum>
110
+ <TxRefIdx>1</TxRefIdx>
111
+ <ProcStatus>0</ProcStatus>
112
+ <ApprovalStatus>1</ApprovalStatus>
113
+ <RespCode>00</RespCode>
114
+ <AVSRespCode>B </AVSRespCode>
115
+ <CVV2RespCode>M</CVV2RespCode>
116
+ <AuthCode>tst554</AuthCode>
117
+ <RecurringAdviceCd/>
118
+ <CAVVRespCode/>
119
+ <StatusMsg>Approved</StatusMsg>
120
+ <RespMsg/>
121
+ <HostRespCode>100</HostRespCode>
122
+ <HostAVSRespCode>I3</HostAVSRespCode>
123
+ <HostCVV2RespCode>M</HostCVV2RespCode>
124
+ <CustomerRefNum>2145108</CustomerRefNum>
125
+ <CustomerName>JOE SMITH</CustomerName>
126
+ <ProfileProcStatus>0</ProfileProcStatus>
127
+ <CustomerProfileMessage>Profile Created</CustomerProfileMessage>
128
+ <RespTime>121825</RespTime>
129
+ </NewOrderResp>
130
+ </Response>
131
+ RESPONSE
132
+ end
133
+
134
+ def new_order_response_failure
135
+ <<-RESPONSE
136
+ <?xml version="1.0" encoding="UTF-8"?>
137
+ <Response>
138
+ <QuickResp>
139
+ <ProcStatus>841</ProcStatus>
140
+ <StatusMsg>Error validating card/account number range</StatusMsg>
141
+ <CustomerBin></CustomerBin>
142
+ <CustomerMerchantID></CustomerMerchantID>
143
+ <CustomerName></CustomerName>
144
+ <CustomerRefNum></CustomerRefNum>
145
+ <CustomerProfileAction></CustomerProfileAction>
146
+ <ProfileProcStatus>9576</ProfileProcStatus>
147
+ <CustomerProfileMessage>Profile: Unable to Perform Profile Transaction. The Associated Transaction Failed. </CustomerProfileMessage>
148
+ <CustomerAddress1></CustomerAddress1>
149
+ <CustomerAddress2></CustomerAddress2>
150
+ <CustomerCity></CustomerCity>
151
+ <CustomerState></CustomerState>
152
+ <CustomerZIP></CustomerZIP>
153
+ <CustomerEmail></CustomerEmail>
154
+ <CustomerPhone></CustomerPhone>
155
+ <CustomerProfileOrderOverrideInd></CustomerProfileOrderOverrideInd>
156
+ <OrderDefaultDescription></OrderDefaultDescription>
157
+ <OrderDefaultAmount></OrderDefaultAmount>
158
+ <CustomerAccountType></CustomerAccountType>
159
+ <CCAccountNum></CCAccountNum>
160
+ <CCExpireDate></CCExpireDate>
161
+ <ECPAccountDDA></ECPAccountDDA>
162
+ <ECPAccountType></ECPAccountType>
163
+ <ECPAccountRT></ECPAccountRT>
164
+ <ECPBankPmtDlv></ECPBankPmtDlv>
165
+ <SwitchSoloStartDate></SwitchSoloStartDate>
166
+ <SwitchSoloIssueNum></SwitchSoloIssueNum>
167
+ </QuickResp>
168
+ </Response>
169
+ RESPONSE
170
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing
3
+ class Gateway
4
+ def ssl_request(*args)
5
+ "<xml></xml>"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,85 @@
1
+ # Simple stupid factory girl inspired way to easily setup
2
+ # option hashes across the tests.
3
+ #
4
+ # Setup like:
5
+ #
6
+ # Options.define(:address, :defaults => {
7
+ # :street => "1234 somewhere lane",
8
+ # :city => "right here town",
9
+ # :zip => "12345"
10
+ # })
11
+ #
12
+ # Use like:
13
+ #
14
+ # Options(:address)
15
+ #
16
+ # Over ride defaults like:
17
+ #
18
+ # Options(:address, :street => "4321 somewhere else")
19
+ #
20
+ # Provide nil to remove an option:
21
+ #
22
+ # Options(:address, :street => nil) => {:city => "right here town", :zip => "12345"}
23
+ #
24
+ # Option hashes can have parent hashes:
25
+ #
26
+ # Options.define(:new_address, {
27
+ # :parent => :address,
28
+ # :defaults => {
29
+ # :street => "4321 somewhere else",
30
+ # :apartment => "3"
31
+ # }
32
+ # })
33
+ # Options(:new_address) => {
34
+ # :street => "4321 somewhere else", :apartment => "3", :city => "right here town", :zip => "12345"
35
+ # }
36
+ #
37
+ # Multiple parents can be combined:
38
+ #
39
+ # Options.define(:name, :defaults => {
40
+ # :first_name => "joe",
41
+ # :last_name => "smith"
42
+ # })
43
+ #
44
+ # Options.define(:full_address, :parent => [ :name, :address ], :defaults => {
45
+ # :phone_number => "5555555555"
46
+ # })
47
+ #
48
+ # Options(:full_address) => {
49
+ # :street=>"1234 somewhere lane", :zip=>"12345", :city=>"right here town",
50
+ # :first_name=>"joe", :phone_number=>"5555555555", :last_name=>"smith"
51
+ # }
52
+ #
53
+ class Options
54
+ class << self
55
+ def define(name, options={})
56
+ self[name] = (self[options[:parent]] || {}).merge(options[:defaults] || {})
57
+ end
58
+
59
+ def [](k)
60
+ return {} if k.nil?
61
+
62
+ if k.is_a?(Array)
63
+ Hash[*k.map { |default|
64
+ self[default].map { |k,v|
65
+ [k,v]
66
+ }
67
+ }.flatten]
68
+ else
69
+ factories[k.to_sym]
70
+ end
71
+ end
72
+
73
+ def []=(k,v)
74
+ factories[k.to_sym] = v
75
+ end
76
+
77
+ def factories
78
+ @@_factories ||= {}
79
+ end
80
+ end
81
+ end
82
+
83
+ def Options(name, options={})
84
+ Options[name].merge(options).delete_if { |k,v| v.nil? }
85
+ end