bigcharger 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 John Grimes
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,149 @@
1
+ # BigCharger - eWAY Token Payments Client
2
+
3
+ The idea of this project is to build a lightweight Ruby library for
4
+ interfacing with the [eWAY Token Payments
5
+ API](http://www.eway.com.au/Developer/eway-api/token-payments.aspx).
6
+
7
+ ## Installation
8
+
9
+ ```ruby
10
+ gem install bigcharger
11
+ ```
12
+
13
+ ## Setup
14
+
15
+ ```ruby
16
+ client = BigCharger.new(
17
+ '87654321',
18
+ 'test@eway.com.au',
19
+ 'test123'
20
+ )
21
+ ```
22
+
23
+ ## Create a new customer
24
+
25
+ ```ruby
26
+ client.create_customer({
27
+ 'CustomerRef' => 'Test 123',
28
+ 'Title' => 'Mr.',
29
+ 'FirstName' => 'Jo',
30
+ 'LastName' => 'Smith',
31
+ 'Company' => 'company',
32
+ 'JobDesc' => 'Analyst',
33
+ 'Email' => 'test@eway.com.au',
34
+ 'Address' => '15 Dundas Court',
35
+ 'Suburb' => 'phillip',
36
+ 'State' => 'act',
37
+ 'PostCode' => '2606',
38
+ 'Country' => 'au',
39
+ 'Phone' => '02111111111',
40
+ 'Mobile' => '04111111111',
41
+ 'Fax' => '111122222',
42
+ 'URL' => 'http://eway.com.au',
43
+ 'Comments' => 'Comments',
44
+ 'CCNameOnCard' => 'Jo Smith',
45
+ 'CCNumber' => '444433XXXXXX1111',
46
+ 'CCExpiryMonth' => '08',
47
+ 'CCExpiryYear' => '15'
48
+ })
49
+ # => "9876543211000" (managedCustomerID of new customer)
50
+ ```
51
+
52
+ ## Process a payment
53
+
54
+ ```ruby
55
+ client.process_payment(
56
+ 9876543211000,
57
+ 1000,
58
+ 'INV-80251',
59
+ 'Pants alteration'
60
+ )
61
+ # => {
62
+ # 'ewayTrxnError' => '00,Transaction Approved(Test Gateway)',
63
+ # 'ewayTrxnStatus' => 'True',
64
+ # 'ewayTrxnNumber' => '10498',
65
+ # 'ewayReturnAmount' => '1000',
66
+ # 'ewayAuthCode' => '123456'
67
+ # }
68
+
69
+ # Alternatively, improve security by requiring a CVN/CVV
70
+ client.process_payment_with_cvn(
71
+ 9876543211000,
72
+ 1000,
73
+ '123',
74
+ 'INV-80251',
75
+ 'Pants alteration'
76
+ )
77
+ ```
78
+
79
+ ## Find a customer
80
+
81
+ ```ruby
82
+ # Find a customer using the managed customer ID
83
+ client.query_customer(9876543211000)
84
+ # => {
85
+ # 'ManagedCustomerID' => '9876543211000',
86
+ # 'CustomerRef' => 'Test 123',
87
+ # 'CustomerTitle' => 'Mr.',
88
+ # 'CustomerFirstName' => 'Jo',
89
+ # 'CustomerLastName' => 'Smith',
90
+ # 'CustomerCompany' => 'company',
91
+ # 'CustomerJobDesc' => nil,
92
+ # 'CustomerEmail' => 'test@eway.com.au',
93
+ # 'CustomerAddress' => '15 Dundas Court',
94
+ # 'CustomerSuburb' => 'phillip',
95
+ # 'CustomerState' => 'act',
96
+ # 'CustomerPostCode' => '2606',
97
+ # 'CustomerCountry' => 'au',
98
+ # 'CustomerPhone1' => '02111111111',
99
+ # 'CustomerPhone2' => '04111111111',
100
+ # 'CustomerFax' => '111122222',
101
+ # 'CustomerURL' => 'http://eway.com.au',
102
+ # 'CustomerComments' => 'Comments',
103
+ # 'CCName' => 'Jo Smith',
104
+ # 'CCNumber' => '444433XXXXXX1111',
105
+ # 'CCExpiryMonth' => '08',
106
+ # 'CCExpiryYear' => '15'
107
+ # }
108
+
109
+ # Find a customer using the reference you supplied when you created the
110
+ # customer
111
+ client.query_customer_by_reference('Test 123')
112
+ ```
113
+
114
+ ## Get list of payments for a customer
115
+
116
+ ```ruby
117
+ client.query_payment(9876543211000)
118
+ # => [
119
+ # {
120
+ # 'TotalAmount' => '1000',
121
+ # 'Result' => '1',
122
+ # 'ResponseText' => 'Approved',
123
+ # 'TransactionDate' => '2012-02-20T00:00:00+11:00',
124
+ # 'ewayTrxnNumber' => '1'
125
+ # },
126
+ # {
127
+ # 'TotalAmount' => '1008',
128
+ # 'Result' => '1',
129
+ # 'ResponseText' => 'Approved',
130
+ # 'TransactionDate' => '2012-02-20T00:00:00+11:00',
131
+ # 'ewayTrxnNumber' => '2'
132
+ # }
133
+ # ]
134
+ ```
135
+
136
+ ## Update a customer
137
+
138
+ ```ruby
139
+ client.update_customer(
140
+ 9876543211000,
141
+ {
142
+ 'CCNameOnCard' => 'Jo Smith',
143
+ 'CCNumber' => '444433XXXXXX2222',
144
+ 'CCExpiryMonth' => '06',
145
+ 'CCExpiryYear' => '22'
146
+ }
147
+ )
148
+ # => true
149
+ ```
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__) + '/bigcharger/config'
2
+ require File.dirname(__FILE__) + '/bigcharger/utils'
3
+ require File.dirname(__FILE__) + '/bigcharger/ops'
4
+
5
+ class BigCharger
6
+ attr_accessor :logger
7
+
8
+ def initialize(customer_id, username, password, test_mode = false, logger = Logger.new('/dev/null'))
9
+ @credentials = {
10
+ :customer_id => customer_id,
11
+ :username => username,
12
+ :password => password
13
+ }
14
+ @client = Curl::Easy.new
15
+ @endpoint = test_mode ? TEST_ENDPOINT : ENDPOINT
16
+ @logger = logger
17
+ set_request_defaults
18
+ end
19
+
20
+ class Error < Exception; end
21
+ end
@@ -0,0 +1,32 @@
1
+ class BigCharger
2
+ SOAP_NAMESPACE = 'http://schemas.xmlsoap.org/soap/envelope/'
3
+ SERVICE_NAMESPACE = 'https://www.eway.com.au/gateway/managedpayment'
4
+ ENDPOINT = 'https://www.eway.com.au/gateway/ManagedPaymentService/managedCreditCardPayment.asmx'
5
+ TEST_ENDPOINT = 'https://www.eway.com.au/gateway/ManagedPaymentService/test/managedCreditCardPayment.asmx'
6
+
7
+ # List of fields (with significant ordering as per WSDL) for customer
8
+ # requests
9
+ CUSTOMER_REQUEST_FIELDS = [
10
+ 'Title',
11
+ 'FirstName',
12
+ 'LastName',
13
+ 'Address',
14
+ 'Suburb',
15
+ 'State',
16
+ 'Company',
17
+ 'PostCode',
18
+ 'Country',
19
+ 'Email',
20
+ 'Fax',
21
+ 'Phone',
22
+ 'Mobile',
23
+ 'CustomerRef',
24
+ 'JobDesc',
25
+ 'Comments',
26
+ 'URL',
27
+ 'CCNumber',
28
+ 'CCNameOnCard',
29
+ 'CCExpiryMonth',
30
+ 'CCExpiryYear'
31
+ ]
32
+ end
@@ -0,0 +1,148 @@
1
+ require 'nokogiri'
2
+ require 'curb'
3
+
4
+ class BigCharger
5
+ # Creates a new managed customer on the eWAY server.
6
+ # @param customer_fields Title, FirstName, LastName, Address, Suburb,
7
+ # State, Company, PostCode, Country, Email, Fax, Phone, Mobile,
8
+ # CustomerRef, JobDesc, Comments, URL, CCNumber, CCNameOnCard,
9
+ # CCExpiryMonth, CCExpiryYear
10
+ # @return [String] Managed customer ID of new customer.
11
+ def create_customer(customer_fields = {})
12
+ log_operation(:create_customer, customer_fields)
13
+ raise Error, 'customer_fields is not a Hash' unless customer_fields.class == Hash
14
+ envelope = wrap_in_envelope do |xml|
15
+ xml['man'].CreateCustomer {
16
+ CUSTOMER_REQUEST_FIELDS.each do |field|
17
+ xml['man'].send(field, customer_fields[field]) if customer_fields[field]
18
+ end
19
+ }
20
+ end
21
+ response = post(envelope, 'CreateCustomer')
22
+ result = response.xpath('//man:CreateCustomerResult', { 'man' => SERVICE_NAMESPACE }).first
23
+ return result ? result.text : false
24
+ end
25
+
26
+ # Processes a payment of a given amount for the specified customer.
27
+ # @param [#to_s] managed_customer_id
28
+ # @param [Integer] amount The amount of the payment, in cents. (e.g.
29
+ # 1000 = $10)
30
+ # @param invoice_ref An optional field that you can use to identify
31
+ # the corresponding invoice within your system.
32
+ # @param invoice_desc An optional field to describe the subject of the
33
+ # payment.
34
+ # @return [Hash] ewayTrxnError, ewayTrxnStatus, ewayTrxnNumber,
35
+ # ewayReturnAmount, ewayAuthCode
36
+ def process_payment(managed_customer_id, amount, invoice_ref = nil, invoice_desc = nil)
37
+ log_operation(:process_payment, managed_customer_id, amount, invoice_ref, invoice_desc)
38
+ envelope = wrap_in_envelope do |xml|
39
+ xml['man'].ProcessPayment {
40
+ xml['man'].managedCustomerID managed_customer_id
41
+ xml['man'].amount amount
42
+ xml['man'].invoiceReference invoice_ref if invoice_ref
43
+ xml['man'].invoiceDescription invoice_desc if invoice_desc
44
+ }
45
+ end
46
+ response = post(envelope, 'ProcessPayment')
47
+ result = response.xpath('//man:ProcessPaymentResponse/man:ewayResponse', { 'man' => SERVICE_NAMESPACE }).first
48
+ return result ? node_to_hash(result) : false
49
+ end
50
+
51
+ # Identical to {#process_payment} except that it accepts a CVN/CVV for
52
+ # greater security.
53
+ def process_payment_with_cvn(managed_customer_id, amount, cvn = nil, invoice_ref = nil, invoice_desc = nil)
54
+ log_operation(:process_payment_with_cvn, managed_customer_id, amount, cvn, invoice_ref, invoice_desc)
55
+ envelope = wrap_in_envelope do |xml|
56
+ xml['man'].ProcessPaymentWithCVN {
57
+ xml['man'].managedCustomerID managed_customer_id
58
+ xml['man'].amount amount
59
+ xml['man'].invoiceReference invoice_ref if invoice_ref
60
+ xml['man'].invoiceDescription invoice_desc if invoice_desc
61
+ xml['man'].cvn cvn if cvn
62
+ }
63
+ end
64
+ response = post(envelope, 'ProcessPaymentWithCVN')
65
+ result = response.xpath('//man:ProcessPaymentWithCVNResponse/man:ewayResponse', { 'man' => SERVICE_NAMESPACE }).first
66
+ return result ? node_to_hash(result) : false
67
+ end
68
+
69
+ # Returns the eWAY record for a specific managed customer ID.
70
+ # @param [#to_s] managed_customer_id
71
+ # @return [Hash] ManagedCustomerID, CustomerRef, CustomerTitle,
72
+ # CustomerFirstName, CustomerLastName, CustomerCompany,
73
+ # CustomerJobDesc, CustomerEmail, CustomerAddress, CustomerSuburb,
74
+ # CustomerState, CustomerPostCode, CustomerCountry, CustomerPhone1,
75
+ # CustomerPhone2, CustomerFax, CustomerURL, CustomerComments
76
+ def query_customer(managed_customer_id)
77
+ log_operation(:query_customer, managed_customer_id)
78
+ envelope = wrap_in_envelope do |xml|
79
+ xml['man'].QueryCustomer {
80
+ xml['man'].managedCustomerID managed_customer_id
81
+ }
82
+ end
83
+ response = post(envelope, 'QueryCustomer')
84
+ result = response.xpath('//man:QueryCustomerResult', { 'man' => SERVICE_NAMESPACE }).first
85
+ return result ? node_to_hash(result) : false
86
+ end
87
+
88
+ # Returns the eWAY customer record with the specified customer
89
+ # reference.
90
+ # @param [#to_s] customer_ref
91
+ # @return [Hash] ManagedCustomerID, CustomerRef, CustomerTitle,
92
+ # CustomerFirstName, CustomerLastName, CustomerCompany,
93
+ # CustomerJobDesc, CustomerEmail, CustomerAddress, CustomerSuburb,
94
+ # CustomerState, CustomerPostCode, CustomerCountry, CustomerPhone1,
95
+ # CustomerPhone2, CustomerFax, CustomerURL, CustomerComments
96
+ def query_customer_by_reference(customer_ref)
97
+ log_operation(:query_customer_by_reference, customer_ref)
98
+ envelope = wrap_in_envelope do |xml|
99
+ xml['man'].QueryCustomerByReference {
100
+ xml['man'].CustomerReference customer_ref
101
+ }
102
+ end
103
+ response = post(envelope, 'QueryCustomerByReference')
104
+ result = response.xpath('//man:QueryCustomerByReferenceResult', { 'man' => SERVICE_NAMESPACE }).first
105
+ return result ? node_to_hash(result) : false
106
+ end
107
+
108
+
109
+ # Returns all the payment records associated with a specified managed
110
+ # customer ID.
111
+ # @param [#to_s] customer_ref
112
+ # @return [Array of Hashes] TotalAmount, Result, ResponseText,
113
+ # TransactionDate, ewayTrxnNumber
114
+ def query_payment(managed_customer_id)
115
+ log_operation(:query_payment, managed_customer_id)
116
+ envelope = wrap_in_envelope do |xml|
117
+ xml['man'].QueryPayment {
118
+ xml['man'].managedCustomerID managed_customer_id
119
+ }
120
+ end
121
+ response = post(envelope, 'QueryPayment')
122
+ result = response.xpath('//man:QueryPaymentResult', { 'man' => SERVICE_NAMESPACE }).first
123
+ return result ? node_collection_to_array(result) : false
124
+ end
125
+
126
+ # Update details of an eWAY customer record.
127
+ # @param [#to_s] managed_customer_id
128
+ # @param customer_fields Title, FirstName, LastName, Address, Suburb,
129
+ # State, Company, PostCode, Country, Email, Fax, Phone, Mobile,
130
+ # CustomerRef, JobDesc, Comments, URL, CCNumber, CCNameOnCard,
131
+ # CCExpiryMonth, CCExpiryYear
132
+ # @return [Boolean] Success or otherwise
133
+ def update_customer(managed_customer_id, customer_fields = {})
134
+ log_operation(:update_customer, managed_customer_id, customer_fields)
135
+ raise Error, 'customer_fields is not a Hash' unless customer_fields.class == Hash
136
+ envelope = wrap_in_envelope do |xml|
137
+ xml['man'].UpdateCustomer {
138
+ xml['man'].managedCustomerID managed_customer_id
139
+ CUSTOMER_REQUEST_FIELDS.each do |field|
140
+ xml['man'].send(field, customer_fields[field]) if customer_fields[field]
141
+ end
142
+ }
143
+ end
144
+ response = post(envelope, 'UpdateCustomer')
145
+ result = response.xpath('//man:UpdateCustomerResult', { 'man' => SERVICE_NAMESPACE }).first
146
+ return result ? result.text == 'true' : false
147
+ end
148
+ end
@@ -0,0 +1,115 @@
1
+ require 'logger'
2
+
3
+ class BigCharger
4
+ private
5
+
6
+ def set_request_defaults
7
+ @client.verbose = false
8
+ @client.url = @endpoint
9
+ @client.headers['Content-Type'] = 'text/xml'
10
+ end
11
+
12
+ def wrap_in_envelope(&block)
13
+ envelope = Nokogiri::XML::Builder.new do |xml|
14
+ xml.Envelope('xmlns:soap' => SOAP_NAMESPACE,
15
+ 'xmlns:man' => SERVICE_NAMESPACE) {
16
+ xml.parent.namespace = xml.parent.namespace_definitions.find { |ns| ns.prefix == 'soap' }
17
+ xml['soap'].Header {
18
+ xml['man'].eWAYHeader {
19
+ xml['man'].eWAYCustomerID @credentials[:customer_id]
20
+ xml['man'].Username @credentials[:username]
21
+ xml['man'].Password @credentials[:password]
22
+ }
23
+ }
24
+ xml['soap'].Body {
25
+ yield xml
26
+ }
27
+ }
28
+ end
29
+ end
30
+
31
+ def post(envelope, action_name)
32
+ @client.headers['SOAPAction'] = "#{SERVICE_NAMESPACE}/#{action_name}"
33
+ record_request(@client, envelope.to_xml)
34
+ @client.http_post @last_request[:body]
35
+ log_last_request
36
+ record_response(@client)
37
+ log_last_response
38
+ check_last_response_for_faults
39
+ check_last_response_for_errors
40
+ return @last_response[:body_document]
41
+ end
42
+
43
+ def record_request(request, body)
44
+ @last_request = {}
45
+ @last_request[:headers] = request.headers.clone
46
+ @last_request[:body] = body
47
+ end
48
+
49
+ def record_response(response)
50
+ @last_response = {}
51
+ @last_response[:header_string] = response.header_str.clone
52
+ @last_response[:body] = response.body_str.clone
53
+ @last_response[:body_document] = Nokogiri::XML(response.body_str)
54
+ end
55
+
56
+ def log_operation(name, *args)
57
+ log_string = "BigCharger##{name} called with: "
58
+ log_string << args.map { |x| x.inspect }.join(', ')
59
+ @logger.debug log_string
60
+ end
61
+
62
+ def log_last_request
63
+ header_output = @last_request[:headers].map { |k,v| "#{k}: #{v}" }.join("\n")
64
+ log_string = "BigCharger - Request sent to eWAY server\n"
65
+ log_string << "#{header_output}\n#{@last_request[:body]}"
66
+ @logger.debug log_string
67
+ end
68
+
69
+ def log_last_response
70
+ body_output = @last_response[:body_document].serialize(:encoding => 'UTF-8') do |config|
71
+ config.format.as_xml
72
+ end
73
+ log_string = "BigCharger - Response received from eWAY server\n"
74
+ log_string << "#{@last_response[:header_string]}\n#{body_output}"
75
+ @logger.debug log_string
76
+ end
77
+
78
+ def check_last_response_for_faults
79
+ faults = @last_response[:body_document].xpath('//soap:Fault', { 'soap' => SOAP_NAMESPACE })
80
+ unless faults.empty?
81
+ fault = faults.first
82
+ fault_code = fault.xpath('faultcode').first.text
83
+ fault_message = fault.xpath('faultstring').first.text
84
+ raise Error, "eWAY server responded with \"#{fault_message}\" (#{fault_code})"
85
+ end
86
+ end
87
+
88
+ def check_last_response_for_errors
89
+ status_info = @last_response[:header_string].match(/HTTP\/[\d\.]+ (\d{3}) ([\w\s]+)[\r\n]/)
90
+ status_code, status_reason = status_info[1].strip, status_info[2].strip
91
+ unless ['200', '100'].include? status_code
92
+ raise Error, "eWAY server responded with \"#{status_reason}\" (#{status_code})"
93
+ end
94
+ end
95
+
96
+ def node_to_hash(node)
97
+ hash = {}
98
+ node.children.each do |node|
99
+ if node.type == Nokogiri::XML::Node::ELEMENT_NODE
100
+ hash[node.name] = node.text.empty? ? nil : node.text
101
+ end
102
+ end
103
+ return hash
104
+ end
105
+
106
+ def node_collection_to_array(node)
107
+ array = []
108
+ node.children.each do |node|
109
+ if node.type == Nokogiri::XML::Node::ELEMENT_NODE
110
+ array << node_to_hash(node)
111
+ end
112
+ end
113
+ return array
114
+ end
115
+ end