safecharge 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ff76ed515bb382871847edfff75a3338adb50cbe
4
+ data.tar.gz: 1abce3e38d9783c534ae44b8a511768f4a44ba0b
5
+ SHA512:
6
+ metadata.gz: 207c9507b761fd53c213796c29bef229582d769c283bb162952c8798dde2707a0308fd8386f2f31637bfd1bb49ae33d34dc46355b0fda5f2d1f3770b1a296b68
7
+ data.tar.gz: d1204ea21290be3e93365297a2bc23f251884e52e3274f2024c3c9899d10341be942b8cadf0495d7b7b45a327df6e30a5a95536a58c8b4e5592c16f8b6f65e65
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in safecharge.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Interactive Gaming Entertainment Pty Ltd
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # Safecharge
2
+
3
+ A simple Ruby wrapper for the SafeCharge PPP Payment API v 3.0.0 (Revised July 2011), as well as
4
+ the SafeCharge Web Cashier API v1.0 (Revised July 2013).
5
+
6
+ ## Status
7
+
8
+ This project is under active development right now, and not suitable for use.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'safecharge'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install safecharge
23
+
24
+ ## Usage
25
+
26
+ ### Public Payment Page
27
+
28
+ The SafeCharge PPP system provides a simple means for merchants to integrate credit card
29
+ payments into their site, without worrying about having to capture their customers'
30
+ credit card details.
31
+
32
+ It works like this:
33
+
34
+ Step 1) Your website provides a way for customers to choose the items they wish to buy,
35
+ say via a shopping basket, or similar.
36
+
37
+ Collate an array of items with the following information
38
+
39
+ items = [
40
+ {
41
+ 'name' => 'bat',
42
+ 'number' => 'sku54321',
43
+ 'amount' => 25,
44
+ 'quantity' => 1
45
+ },
46
+ {
47
+ 'name' => 'ball',
48
+ 'number' => 'sku12345',
49
+ 'amount' => 15,
50
+ 'quantity' => 2
51
+ }
52
+ ]
53
+
54
+ and insert that items array into an array of params like so
55
+
56
+ params = {
57
+ 'total_amount' => 55,
58
+ 'currency' => 'USD',
59
+ 'items' => items
60
+ }
61
+
62
+ Note you must supply the following environment variables for this API to work.
63
+
64
+ SAFECHARGE_SECRET_KEY, SAFECHARGE_MERCHANT_ID, SAFECHARGE_MERCHANT_SITE_ID
65
+
66
+ These will have been provided to you by Safecharge.
67
+
68
+ Step 2) You offer a `checkout` button that links to the following url.
69
+
70
+ `url = Safecharge.request_url(params)`
71
+
72
+ Step 3) The SafeCharge system will redirect the user to the Public Payment Page
73
+ and there they will enter in their credit card and other payment details as needed.
74
+ When the user confirms their payment, the Safecharge system authenticates it and
75
+ redirects the user to either a 'success', 'failure' or 'back' page. The back page
76
+ is used if the user suspends the payment processing by clicking on their browser's
77
+ back button.
78
+
79
+ ### Web Cashier
80
+
81
+ The Safecharge Web Cashier extends the standard Public Payment Page with paramaters that
82
+ allow you to register and identify customers in SafeCharge's system, and enable you to
83
+ change the deposit amounts within limits you define.
84
+
85
+ It works like this:
86
+
87
+ Step 1) Your website provides a way for customers to choose the items they wish to buy,
88
+ say via a shopping basket, or similar.
89
+
90
+ Collate an array of items with the following information
91
+
92
+ items = [
93
+ {
94
+ 'name' => 'deposit',
95
+ 'number' => 'something',
96
+ 'amount' => 0,
97
+ 'quantity' => 1,
98
+ 'open_amount' => 'true',
99
+ 'min_amount' => 10.0,
100
+ 'max_amount' => 1000.0,
101
+ },
102
+ ]
103
+
104
+ and insert that items array into an array of params like so
105
+
106
+ params = {
107
+ 'total_amount' => 55,
108
+ 'currency' => 'USD',
109
+ 'items' => items,
110
+ 'user_token' => 'auto', # or 'register'
111
+ 'user_token_id' => 'player_id'
112
+ }
113
+
114
+ Note you must supply the following environment variables for this API to work.
115
+
116
+ SAFECHARGE_SECRET_KEY, SAFECHARGE_MERCHANT_ID, SAFECHARGE_MERCHANT_SITE_ID
117
+
118
+ These will have been provided to you by Safecharge.
119
+
120
+ Step 2) You offer a `checkout` button that links to the following url.
121
+
122
+ `url = Safecharge.wc_request_url(params)`
123
+
124
+ Step 3) The SafeCharge system will redirect the user to the WebCashier Page
125
+ and there they will enter in their credit card and other payment details as needed.
126
+ When the user confirms their payment, the Safecharge system authenticates it and
127
+ redirects the user to either a 'success', 'failure' or 'back' page. The back page
128
+ is used if the user suspends the payment processing by clicking on their browser's
129
+ back button.
130
+
131
+ ## Contributing
132
+
133
+ 1. Fork it
134
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
135
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
136
+ 4. Push to the branch (`git push origin my-new-feature`)
137
+ 5. Create new Pull Request
data/lib/safecharge.rb ADDED
@@ -0,0 +1,147 @@
1
+ #!/user/bin/env ruby
2
+ #coding: utf-8
3
+
4
+ require "safecharge/version"
5
+ require "safecharge/constants"
6
+ require "safecharge/request"
7
+ require "safecharge/wc_request"
8
+ require "safecharge/response"
9
+
10
+ # The Safecharge PPP system provides a simple means for merchants to integrate credit card
11
+ # payments into their site, without worrying about having to capture their customers'
12
+ # credit card details.
13
+ #
14
+ # It works like this:
15
+ #
16
+ # Step 1) Your website provides a way for customers to choose the items they wish to buy,
17
+ # say via a shopping basket, or similar.
18
+ #
19
+ # Collate an array of items with the following information
20
+ #
21
+ # items = [
22
+ # {
23
+ # 'name' => 'bat',
24
+ # 'number' => 'sku54321',
25
+ # 'amount' => 25,
26
+ # 'quantity' => 1
27
+ # },
28
+ # {
29
+ # 'name' => 'ball',
30
+ # 'number' => 'sku12345',
31
+ # 'amount' => 15,
32
+ # 'quantity' => 2
33
+ # }
34
+ # ]
35
+ #
36
+ # and insert that items array into an array of params like so
37
+ #
38
+ # params = {
39
+ # 'total_amount' => 55,
40
+ # 'currency' => 'USD',
41
+ # 'items' => items
42
+ # }
43
+ #
44
+ # Note you must supply the following environment variables for this API to work.
45
+ #
46
+ # SAFECHARGE_SECRET_KEY, SAFECHARGE_MERCHANT_ID, SAFECHARGE_MERCHANT_SITE_ID
47
+ #
48
+ # These will have been provided to you by Safecharge.
49
+ #
50
+ # Step 2) You offer a 'checkout' button that links to the following url.
51
+ #
52
+ # url = Safecharge.request_url(params)
53
+ #
54
+ # Step 3) The Safecharge system will redirect the user to the Public Payment Page
55
+ # and there they will enter in their credit card and other payment details as needed.
56
+ # When the user confirms their payment, the Safecharge system authenticates it and
57
+ # redirects the user to either a 'success', 'failure' or 'back' page. The back page
58
+ # is used if the user suspends the payment processing by clicking on their browser's
59
+ # back button.
60
+ # Whichever page is returned, it will incude parameters from the server
61
+ # which can be decoded into a valid Response object by your server.
62
+ #
63
+ # response = Safecharge.parse_response
64
+ #
65
+ # TODO: finish these docs
66
+ #
67
+ module Safecharge
68
+
69
+ # define errors and exceptions.
70
+ class SafechargeError < RuntimeError
71
+ end
72
+ class InternalException < SafechargeError
73
+ end
74
+ class ValidationException < SafechargeError
75
+ end
76
+
77
+ # module level method to get the redirection URL given some params.
78
+ # you must explicitly set the mode to 'live' to generate the production URL.
79
+ def self.request_url(params = {}, mode = 'test')
80
+ result = nil
81
+ url = ''
82
+ case mode
83
+ when 'test'
84
+ url = Safecharge::Constants::SERVER_TEST
85
+ when 'live'
86
+ url = Safecharge::Constants::SERVER_LIVE
87
+ else
88
+ raise ArgumentError, "Invalid request mode #{mode}"
89
+ end
90
+
91
+ begin
92
+ request = Safecharge::Request.new(url, params)
93
+ return request.full_url
94
+
95
+ rescue InternalException => e
96
+ puts "Caught Internal Exception: #{e.message}"
97
+ puts e.backtrace
98
+ raise RuntimeError, "Internal server error. Please try again later."
99
+
100
+ rescue ValidationException => e
101
+ puts "Caught Validation Exception: #{e.message}"
102
+ puts e.backtrace
103
+ raise RuntimeError, "Validation error: #{e.message} Fix your data and retry."
104
+
105
+ rescue SafechargeError => e
106
+ puts "Caught General Safecharge Error: #{e.message}"
107
+ puts e.backtrace
108
+ raise RuntimeError, "Undocumented Internal error: #{e.message}"
109
+ end
110
+ end
111
+
112
+ # module level method to get the redirection URL given some params.
113
+ # you must explicitly set the mode to 'live' to generate the production URL.
114
+ def self.wc_request_url(params = {}, mode = 'test')
115
+ result = nil
116
+ url = ''
117
+ case mode
118
+ when 'test'
119
+ url = Safecharge::Constants::SERVER_TEST
120
+ when 'live'
121
+ url = Safecharge::Constants::SERVER_LIVE
122
+ else
123
+ raise ArgumentError, "Invalid request mode #{mode}"
124
+ end
125
+
126
+ begin
127
+ request = Safecharge::WcRequest.new(url, params)
128
+ return request.full_url
129
+
130
+ rescue InternalException => e
131
+ puts "Caught Internal Exception: #{e.message}"
132
+ puts e.backtrace
133
+ raise RuntimeError, "Internal server error. Please try again later."
134
+
135
+ rescue ValidationException => e
136
+ puts "Caught Validation Exception: #{e.message}"
137
+ puts e.backtrace
138
+ raise RuntimeError, "Validation error: #{e.message} Fix your data and retry."
139
+
140
+ rescue SafechargeError => e
141
+ puts "Caught General Safecharge Error: #{e.message}"
142
+ puts e.backtrace
143
+ raise RuntimeError, "Undocumented Internal error: #{e.message}"
144
+ end
145
+ end
146
+
147
+ end
@@ -0,0 +1,44 @@
1
+ #!/user/bin/env ruby
2
+ #coding: utf-8
3
+
4
+ module Safecharge
5
+ class Constants
6
+ API_VERSION = '3.0.0'
7
+
8
+ SERVER_TEST = ENV['SAFECHARGE_SERVER_TEST'] # provided by SafeCharge
9
+ SERVER_LIVE = ENV['SAFECHARGE_SERVER_LIVE'] # provided by SafeCharge
10
+
11
+ SECRET_KEY = ENV['SAFECHARGE_SECRET_KEY'] # provided by SafeCharge
12
+ MERCHANT_ID = ENV['SAFECHARGE_MERCHANT_ID'] # provided by SafeCharge
13
+ MERCHANT_SITE_ID = ENV['SAFECHARGE_MERCHANT_SITE_ID'] # provided by SafeCharge
14
+ MERCHANT_3D_SITE_ID = ENV['SAFECHARGE_MERCHANT_3D_SITE_ID'] # provided by SafeCharge
15
+
16
+ SG_CLIENT_PASSWORD = ENV['SAFECHARGE_SG_CLIENT_PASSWORD'] # provided by SafeCharge
17
+ SG_3D_CLIENT_PASSWORD = ENV['SAFECHARGE_SG_3D_CLIENT_PASSWORD'] # provided by SafeCharge
18
+
19
+ CPANEL_PASSWORD = ENV['SAFECHARGE_CPANEL_PASSWORD'] # provided by SafeCharge
20
+
21
+ APPROVED = 'APPROVED'
22
+ DECLINED = 'DECLINED'
23
+ ERROR = 'ERROR'
24
+ BANK_ERROR = 'BANK_ERROR'
25
+ INVALID_LOGIN = 'INVALID_LOGIN'
26
+ INVALID_IP = 'INVALID_IP'
27
+ TIMEOUT = 'TIMEOUT'
28
+ UNKNOWN_ERROR = 'UNKNOWN_ERROR'
29
+
30
+ CURRENCIES = [
31
+ 'GBP', 'EUR', 'USD', 'HKD', 'YEN', 'AUD', 'CAD',
32
+ 'NOK', 'ZAR', 'SEK', 'CHF', 'NIS', 'MXN', 'RUB'
33
+ ]
34
+ # REQUEST_TYPE_AUTH = 'Auth'
35
+ # REQUEST_TYPE_SETTLE = 'Settle'
36
+ # REQUEST_TYPE_SALE = 'Sale'
37
+ # REQUEST_TYPE_CREDIT = 'Credit'
38
+ # REQUEST_TYPE_VOID = 'Void'
39
+ # REQUEST_TYPE_AVS = 'AVSOnly'
40
+
41
+ DEFAULT_ENCODING = 'utf-8'
42
+ DEFAULT_CURRENCY_CODE = 'EUR'
43
+ end
44
+ end
@@ -0,0 +1,187 @@
1
+ #!/user/bin/env ruby
2
+ #coding: utf-8
3
+
4
+ require "safecharge"
5
+ require "safecharge/constants"
6
+
7
+ module Safecharge
8
+ class Request
9
+ attr_accessor :mode, :params, :items, :url, :full_url
10
+
11
+ ALLOWED_FIELDS = {
12
+ 'currency' => {:required => true, :type => 'currency_code'},
13
+ 'customData' => {:required => false, :type => 'string', length: 255},
14
+ 'customSiteName' => {:required => false, :type => 'string', length: 50},
15
+ 'discount' => {:required => false, :type => 'currency'},
16
+ 'encoding' => {:required => false, :type => 'string', length: 20},
17
+ 'error_url' => {:required => false, :type => 'string', length: 300},
18
+ 'handling' => {:required => false, :type => 'currency'},
19
+ 'invoice_id' => {:required => false, :type => 'string', length: 400},
20
+ 'merchant_id' => {:required => true, :type => 'int'},
21
+ 'merchant_site_id' => {:required => true, :type => 'int'},
22
+ 'merchant_unique_id' => {:required => false, :type => 'string', length: 64},
23
+ 'merchantLocale' => {:required => false, :type => 'string', length: 5},
24
+ 'payment_method' => {:required => false, :type => 'string', length: 256},
25
+ 'pending_url' => {:required => false, :type => 'string', length: 300},
26
+ 'productId' => {:required => false, :type => 'string', length: 50},
27
+ 'shipping' => {:required => false, :type => 'currency'},
28
+ 'skip_billing_tab' => {:required => false, :type => 'boolstring'},
29
+ 'skip_review_tab' => {:required => false, :type => 'boolstring'},
30
+ 'success_url' => {:required => false, :type => 'string', length: 300},
31
+ 'total_amount' => {:required => true, :type => 'currency'},
32
+ 'total_tax' => {:required => false, :type => 'percent'},
33
+ 'userid' => {:required => false, :type => 'string', length: 50},
34
+ 'version' => {:required => true, :type => 'string', length: 10},
35
+ 'webMasterId' => {:required => false, :type => 'string', length: 255}
36
+
37
+ } # 'time_stamp', 'numberofitems' and 'checksum' are inserted after validation.
38
+
39
+ ALLOWED_ITEM_FIELDS = {
40
+ 'name' => {:required => true, :type => 'string', length: 400},
41
+ 'number' => {:required => true, :type => 'string', length: 400},
42
+ 'amount' => {:required => true, :type => 'currency'},
43
+ 'quantity' => {:required => true, :type => 'int'},
44
+ 'discount' => {:required => false, :type => 'percent'},
45
+ 'shipping' => {:required => false, :type => 'currency'},
46
+ 'handling' => {:required => false, :type => 'currency'}
47
+
48
+ }
49
+
50
+ DEFAULT_PARAMS = {
51
+ 'merchant_id' => Safecharge::Constants::MERCHANT_ID,
52
+ 'merchant_site_id' => Safecharge::Constants::MERCHANT_SITE_ID,
53
+ 'currency' => Safecharge::Constants::DEFAULT_CURRENCY_CODE,
54
+ 'version' => Safecharge::Constants::API_VERSION,
55
+ 'encoding' => Safecharge::Constants::DEFAULT_ENCODING
56
+ }
57
+
58
+ def initialize(url, params = {})
59
+ raise ArgumentError, "missing url" if url == nil || url.empty?
60
+ if url === Safecharge::Constants::SERVER_TEST
61
+ self.mode = 'test'
62
+ elsif url === Safecharge::Constants::SERVER_LIVE
63
+ self.mode = 'live'
64
+ else
65
+ raise ArgumentError, "invalid url #{url}"
66
+ end
67
+ self.url = url
68
+ self.full_url = url
69
+ core_params, items, extracted_items = self.extract_items(params)
70
+ raise ValidationException, "Missing array of Items." if items == nil || items.empty?
71
+ self.items = items
72
+ self.params = DEFAULT_PARAMS.merge(core_params)
73
+ self.validate_parameters(self.params)
74
+ items.each {|i| self.validate_parameters(i, self.class::ALLOWED_ITEM_FIELDS)}
75
+ self.params.merge!(extracted_items)
76
+ self.params.merge!({'numberofitems' => items.size,
77
+ 'time_stamp' => Time.now.utc.strftime("%Y-%m-%d.%H:%M:%S")})
78
+ self.params.merge!({'checksum' => calculate_checksum})
79
+ self.construct_url
80
+ end
81
+
82
+ protected
83
+
84
+ def extract_items(params)
85
+ items = params.delete('items')
86
+ return params, nil, nil if items == nil
87
+ keyed_items = {}
88
+ items.each_with_index do |item, i|
89
+ item.keys.each do |key|
90
+ new_key = "item_#{key}_#{i+1}"
91
+ keyed_items[new_key] = item[key]
92
+ end
93
+ end
94
+ return params, items, keyed_items
95
+ end
96
+
97
+ def validate_parameters(params, against = self.class::ALLOWED_FIELDS)
98
+ self.validate_fields(params, against)
99
+ self.validate_no_extra_fields(params, against)
100
+ end
101
+
102
+ def validate_fields(params, against = self.class::ALLOWED_FIELDS)
103
+ against.each do |name, meta|
104
+ required = (meta[:required] === true)
105
+ # Check that all required parameters are present
106
+ if required && !params.keys.include?(name)
107
+ raise ValidationException, "Parameter #{name} is required, but missing from #{params.keys}"
108
+ end
109
+ if params.keys.include? name
110
+ p = params[name]
111
+ if p != nil
112
+ # Check the type of the value
113
+ correct_type = false
114
+ case meta[:type]
115
+ when 'boolstring'
116
+ correct_type = p.is_a?(String) && ['True', 'False'].include?(p)
117
+ when 'string'
118
+ correct_type = p.is_a? String
119
+ raise ValidationException, sprintf("Value '%s' in field '%s' is too long.", p, name) if p.size > meta[:length]
120
+ when 'currency_code'
121
+ correct_type = p.is_a? String
122
+ when 'currency'
123
+ correct_type = p.is_a?(Float) || p.is_a?(Integer)
124
+ when 'percent'
125
+ correct_type = p.is_a?(Float) || p.is_a?(Integer)
126
+ when 'int'
127
+ correct_type = p.is_a? Integer
128
+ end
129
+
130
+ if !correct_type
131
+ raise ValidationException, sprintf("Value '%s' in field '%s' is not of expected type '%s'", p, name, meta[:type])
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ def validate_no_extra_fields(params, against = self.class::ALLOWED_FIELDS)
139
+ allowed_fields = against.keys
140
+ params.each do |key, value|
141
+ raise InternalException, "Field #{key} is not supported" if !allowed_fields.include?(key)
142
+ end
143
+ end
144
+
145
+ def calculate_checksum
146
+ codes = [Safecharge::Constants::SECRET_KEY,
147
+ self.params['merchant_id'],
148
+ self.params['currency'],
149
+ self.params['total_amount']]
150
+ self.items.each do |item|
151
+ codes << item['name']
152
+ codes << item['amount']
153
+ codes << item['quantity']
154
+ end
155
+ codes << self.params['time_stamp']
156
+ s = codes.join('')
157
+ return Digest::MD5.hexdigest(s)
158
+ end
159
+
160
+ def construct_url
161
+ uri = URI(self.url)
162
+ uri.query = URI.encode_www_form(self.params)
163
+ self.full_url = uri.to_s
164
+ return uri
165
+ end
166
+
167
+ # def calculate_item_total
168
+ # # item_amount_N - item_discount_N + item_shipping_N + item_handling_N) * item_quantity_N
169
+ # return 0.0 if self.items == nil || self.items.empty?
170
+ # result = 0
171
+ # self.items.each {|i| result += (i['amount'] +
172
+ # i['shipping'] +
173
+ # i['handling'] -
174
+ # i['discount']) * i['quantity']}
175
+ # return result
176
+ # end
177
+ #
178
+ # def calculate_total_with_tax
179
+ # # calculatedTotalAmount+=shipping+handling-discount
180
+ # tot = calculate_item_total +
181
+ # self.fields['shipping'] +
182
+ # self.fields['handling'] - self.fields['discount']
183
+ # return tot, tot * ( self.fields['total_tax'] / 100)
184
+ # end
185
+
186
+ end
187
+ end
@@ -0,0 +1,58 @@
1
+ #!/user/bin/env ruby
2
+ #coding: utf-8
3
+
4
+ require "safecharge"
5
+ require "safecharge/constants"
6
+
7
+ module Safecharge
8
+ class Response
9
+ attr_accessor :params
10
+
11
+ ALLOWED_FIELDS = [
12
+ 'Status', 'totalAmount', 'TransactionID', 'ClientUniqueID', 'ErrCode',
13
+ 'ExErrCode', 'AuthCode', 'Reason', 'Token', 'ReasonCode',
14
+ 'advanceResponseChecksum', 'ECI',
15
+ 'nameOnCard', 'currency',
16
+ 'total_discount', 'total_handling', 'total_shipping', 'total_tax',
17
+ 'customData', 'merchant_unique_id', 'merchant_site_id',
18
+ 'requestVersion', 'message', 'Error', 'PPP_TransactionID', 'UserID',
19
+ 'ProductID', 'ppp_status', 'merchantLocale', 'unknownParameters', 'webMasterId'
20
+ ]
21
+
22
+ def initialize(incoming_encoded_params = nil)
23
+ self.params = self.decode(incoming_encoded_params)
24
+ end
25
+
26
+ def decode(param_string)
27
+ #todo write this.
28
+ return {}
29
+ end
30
+
31
+ def self.code(err, exerr)
32
+ return Safecharge::Constants::APPROVED if err == 0 && exerr == 0
33
+ return Safecharge::Constants::DECLINED if err == -1 && exerr == 0
34
+ # pending? see p 22 of the spec
35
+ return Safecharge::Constants::ERROR if err == -1100 && exerr > 0
36
+ # could be more specific. see p 22 of the spec for exerr codes
37
+ return Safecharge::Constants::BANK_ERROR if err < 0 && exerr != 0
38
+ return Safecharge::Constants::INVALID_LOGIN if err == -1001
39
+ return Safecharge::Constants::INVALID_IP if err == -1005
40
+ return Safecharge::Constants::TIMEOUT if err == -1203
41
+ return Safecharge::Constants::UNKNOWN_ERROR
42
+ end
43
+
44
+ protected
45
+
46
+ def calculate_checksum
47
+ codes = [Safecharge::Constants::SECRET_KEY,
48
+ self.params['totalAmount'],
49
+ self.params['currency'],
50
+ self.params['responseTimeStamp'],
51
+ self.params['PPP_TransactionID'],
52
+ self.params['Status'],
53
+ self.params['productId']]
54
+ s = codes.join('')
55
+ return Digest::MD5.hexdigest(s)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,6 @@
1
+ #!/user/bin/env ruby
2
+ #coding: utf-8
3
+
4
+ module Safecharge
5
+ VERSION = "0.0.3"
6
+ end
@@ -0,0 +1,79 @@
1
+ #!/user/bin/env ruby
2
+ #coding: utf-8
3
+
4
+ require "safecharge"
5
+ require "safecharge/constants"
6
+
7
+ module Safecharge
8
+ class WcRequest < Request
9
+
10
+ ALLOWED_FIELDS = {
11
+ 'currency' => {:required => true, :type => 'currency_code'},
12
+ 'customData' => {:required => false, :type => 'string', length: 255},
13
+ 'customSiteName' => {:required => false, :type => 'string', length: 50},
14
+ 'discount' => {:required => false, :type => 'currency'},
15
+ 'encoding' => {:required => false, :type => 'string', length: 20},
16
+ 'error_url' => {:required => false, :type => 'string', length: 300},
17
+ 'handling' => {:required => false, :type => 'currency'},
18
+ 'invoice_id' => {:required => false, :type => 'string', length: 400},
19
+ 'merchant_id' => {:required => true, :type => 'int'},
20
+ 'merchant_site_id' => {:required => true, :type => 'int'},
21
+ 'merchant_unique_id' => {:required => false, :type => 'string', length: 64},
22
+ 'merchantLocale' => {:required => false, :type => 'string', length: 5},
23
+ 'payment_method' => {:required => false, :type => 'string', length: 256},
24
+ 'pending_url' => {:required => false, :type => 'string', length: 300},
25
+ 'productId' => {:required => false, :type => 'string', length: 50},
26
+ 'shipping' => {:required => false, :type => 'currency'},
27
+ 'skip_billing_tab' => {:required => false, :type => 'boolstring'},
28
+ 'skip_review_tab' => {:required => false, :type => 'boolstring'},
29
+ 'success_url' => {:required => false, :type => 'string', length: 300},
30
+ 'total_amount' => {:required => true, :type => 'currency'},
31
+ 'total_tax' => {:required => false, :type => 'percent'},
32
+ 'userid' => {:required => false, :type => 'string', length: 50},
33
+ 'version' => {:required => true, :type => 'string', length: 10},
34
+ 'webMasterId' => {:required => false, :type => 'string', length: 255},
35
+ 'user_token' => {:required => false, :type => 'string', length: 8}, # register or auto
36
+ 'user_token_id' => {:required => true, :type => 'string', length: 45}
37
+
38
+ } # 'time_stamp', 'numberofitems' and 'checksum' are inserted after validation.
39
+
40
+ ALLOWED_ITEM_FIELDS = {
41
+ 'name' => {:required => true, :type => 'string', length: 400},
42
+ 'number' => {:required => true, :type => 'string', length: 400},
43
+ 'amount' => {:required => true, :type => 'currency'},
44
+ 'quantity' => {:required => true, :type => 'int'},
45
+ 'discount' => {:required => false, :type => 'percent'},
46
+ 'shipping' => {:required => false, :type => 'currency'},
47
+ 'handling' => {:required => false, :type => 'currency'},
48
+ 'open_amount' => {:required => false, :type => 'boolstring'},
49
+ 'min_amount' => {:required => false, :type => 'currency'},
50
+ 'max_amount' => {:required => false, :type => 'currency'}
51
+ }
52
+
53
+ def initialize(url, params = {})
54
+ super(url, params)
55
+ end
56
+
57
+ protected
58
+
59
+ def calculate_checksum
60
+ codes = [Safecharge::Constants::SECRET_KEY,
61
+ self.params['merchant_id'],
62
+ self.params['currency'],
63
+ self.params['total_amount']]
64
+ self.items.each do |item|
65
+ codes << item['name']
66
+ codes << item['amount']
67
+ codes << item['quantity']
68
+ codes << item['open_amount']
69
+ codes << item['min_amount']
70
+ codes << item['max_amount']
71
+ end
72
+ codes << self.params['user_token_id']
73
+ codes << self.params['time_stamp']
74
+ s = codes.join('')
75
+ return Digest::MD5.hexdigest(s)
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'safecharge/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "safecharge"
8
+ spec.version = Safecharge::VERSION
9
+ spec.authors = ["Dave Sag"]
10
+ spec.email = ["davesag@gmail.com"]
11
+ spec.description = "Implements all of the features of the SafeCharge API."
12
+ spec.summary = "A Ruby Wrapper for the SafeCharge API"
13
+ spec.homepage = "http://cv.davesag.com/"
14
+ spec.license = "MIT"
15
+ spec.date = Time.now.utc.strftime("%Y-%m-%d")
16
+
17
+ spec.files = `git ls-files`.split($/) - %w(.rspec .gitignore Rakefile) - `git ls-files test_data`.split($/) - `git ls-files spec`.split($/)
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ # spec.add_dependency "ox", "~> 2.0.5"
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec", "~> 2.6"
26
+ spec.add_development_dependency "dotenv"
27
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: safecharge
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Dave Sag
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.6'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dotenv
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Implements all of the features of the SafeCharge API.
70
+ email:
71
+ - davesag@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - Gemfile
77
+ - LICENSE.txt
78
+ - README.md
79
+ - lib/safecharge.rb
80
+ - lib/safecharge/constants.rb
81
+ - lib/safecharge/request.rb
82
+ - lib/safecharge/response.rb
83
+ - lib/safecharge/version.rb
84
+ - lib/safecharge/wc_request.rb
85
+ - safecharge.gemspec
86
+ homepage: http://cv.davesag.com/
87
+ licenses:
88
+ - MIT
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project:
106
+ rubygems_version: 2.0.7
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: A Ruby Wrapper for the SafeCharge API
110
+ test_files: []