allorails 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +16 -0
- data/README.md +7 -0
- data/Rakefile +14 -0
- data/allorails.gemspec +29 -0
- data/init.rb +1 -0
- data/lib/allorails/api/api.rb +203 -0
- data/lib/allorails/core.rb +147 -0
- data/lib/allorails/errors/errors.rb +28 -0
- data/lib/allorails/rails.rb +75 -0
- data/lib/allorails/request/request.rb +284 -0
- data/lib/allorails/response/model.rb +278 -0
- data/lib/allorails/response/response.rb +448 -0
- data/lib/allorails.rb +51 -0
- data/test/allorails_spec.rb +69 -0
- data/test/test-conf-sample.yml +11 -0
- data/test/test-conf.yml +12 -0
- metadata +75 -0
@@ -0,0 +1,284 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'cgi'
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Allorails
|
6
|
+
module Request
|
7
|
+
|
8
|
+
class ApiRequest
|
9
|
+
|
10
|
+
# API response format needed to provide response object mapping
|
11
|
+
MAPPING_FORMAT = 'json'
|
12
|
+
# API remote base path
|
13
|
+
API_PATH = '/rest'
|
14
|
+
|
15
|
+
## Returns the remote path of the Allopass API service
|
16
|
+
#
|
17
|
+
# @return (string) A remote path
|
18
|
+
def _path
|
19
|
+
self.class::PATH
|
20
|
+
end
|
21
|
+
|
22
|
+
# class of the Response
|
23
|
+
## Returns an object mapping the Allopass API response
|
24
|
+
#
|
25
|
+
# @param signature (string) Expected response signature
|
26
|
+
# @param headers (list) Response HTTP headers
|
27
|
+
# @param body (string) Raw response data
|
28
|
+
#
|
29
|
+
# @return ApiResponse An object mapping the Allopass API response
|
30
|
+
def _new_response(signature, headers, body)
|
31
|
+
raise "new_response is not implemented"
|
32
|
+
end
|
33
|
+
|
34
|
+
## Constructor
|
35
|
+
#
|
36
|
+
# @param parameters (array) Parameters to the API call
|
37
|
+
# @param mapping (boolean) Wether to return a raw response or an object
|
38
|
+
# @param emailAccount (string) Configurated email account
|
39
|
+
def initialize(parameters, mapping = true, email_account = nil)
|
40
|
+
@_mapping = mapping
|
41
|
+
@_parameters = parameters
|
42
|
+
@_email_account = email_account
|
43
|
+
end
|
44
|
+
|
45
|
+
## Call the Allopass API and returns the response (raw or object)
|
46
|
+
#
|
47
|
+
# @return ApiResponse The API response
|
48
|
+
def call()
|
49
|
+
headers, body = self._build_parameters._sign._call
|
50
|
+
signature = self._hash(body + Allorails.config.private_key(@_email_account))
|
51
|
+
|
52
|
+
if (@_mapping)
|
53
|
+
return self._new_response(signature, headers, body)
|
54
|
+
else
|
55
|
+
return Allorails::Response::ApiResponse.new(signature, headers, body)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
## Internal method building special required API parameters
|
60
|
+
#
|
61
|
+
# @return ApiRequest Class instance for chaining purpose
|
62
|
+
def _build_parameters
|
63
|
+
formats = ['json', 'xml']
|
64
|
+
|
65
|
+
@_parameters.update({
|
66
|
+
'api_ts' => Time.now.to_i,
|
67
|
+
'api_key' => Allorails.config.api_key(@_email_account),
|
68
|
+
'api_hash' => Allorails.config.default_hash
|
69
|
+
})
|
70
|
+
|
71
|
+
if @_parameters.has_key?('format')
|
72
|
+
if (@_mapping)
|
73
|
+
@_parameters['format'] = self::MAPPING_FORMAT
|
74
|
+
elsif !formats.include?(@_parameters['format'])
|
75
|
+
@_parameters['format'] = Allorails.config.default_format
|
76
|
+
end
|
77
|
+
else
|
78
|
+
@_parameters['format'] = Allorails.config.default_format
|
79
|
+
end
|
80
|
+
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
## Internal methods used to sign the request call
|
85
|
+
#
|
86
|
+
# @return ApiRequest Class instance for chaining purpose
|
87
|
+
def _sign
|
88
|
+
params = @_parameters.dup
|
89
|
+
|
90
|
+
if params.has_key?('code')
|
91
|
+
params['code'] = params['code'].join
|
92
|
+
end
|
93
|
+
|
94
|
+
sign = params.sort.map{|p| "#{p[0]}#{p[1]}"}.join
|
95
|
+
@_parameters['api_sig'] = self._hash(sign + Allorails.config.private_key(@_email_account))
|
96
|
+
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
## Internal method hashing data with defined cipher #TODO en prenant en compte la donnée incrite dans les confs
|
101
|
+
#
|
102
|
+
# @parameter data (string) Data to hash
|
103
|
+
#
|
104
|
+
# @throws ApiMissingHashFeatureError If defined cipher method isn'h available
|
105
|
+
def _hash(data)
|
106
|
+
::Digest::SHA1.hexdigest(data)
|
107
|
+
end
|
108
|
+
|
109
|
+
## Internal method deciding wether to use a POST request
|
110
|
+
#
|
111
|
+
# @return (boolean) True to use POST
|
112
|
+
def _is_http_post
|
113
|
+
false
|
114
|
+
end
|
115
|
+
|
116
|
+
## Internal method calling the Allopass API
|
117
|
+
#
|
118
|
+
# @return (tuple) Pair containing response headers and body
|
119
|
+
def _call
|
120
|
+
protocol = Allorails.config.network_protocol
|
121
|
+
server = Allorails.config.host
|
122
|
+
timeout = Allorails.config.network_timeout.to_f
|
123
|
+
|
124
|
+
port = protocol == 'https' ? 443 : 80
|
125
|
+
uri = URI(protocol + '://' + server + ApiRequest::API_PATH + _path)
|
126
|
+
method = _is_http_post ? 'POST' : 'GET'
|
127
|
+
headers = {
|
128
|
+
"Content-Type" => "application/x-www-form-urlencoded; charset=utf-8",
|
129
|
+
"User-Agent" => "Allopass-ApiKit-AlloRails"
|
130
|
+
}
|
131
|
+
|
132
|
+
# use a proxy?
|
133
|
+
use_proxy = false
|
134
|
+
http_class = if use_proxy then Net::HTTP::Proxy('127.0.0.1', 9999) else Net::HTTP end
|
135
|
+
|
136
|
+
# prepare and send HTTP request
|
137
|
+
http_class.start(uri.host, port, :use_ssl => uri.scheme == 'https') do |http|
|
138
|
+
|
139
|
+
if method == 'GET'
|
140
|
+
uri.query = _encode_parameters
|
141
|
+
req = http_class::Get.new uri.request_uri
|
142
|
+
else
|
143
|
+
#uri.query = _encode_parameters
|
144
|
+
req = http_class::Post.new uri.request_uri
|
145
|
+
req.body = _encode_parameters
|
146
|
+
end
|
147
|
+
|
148
|
+
# set headers
|
149
|
+
headers.each_pair{|k, v| req[k] = v}
|
150
|
+
|
151
|
+
# send the request and see if successful
|
152
|
+
case res = http.request(req)
|
153
|
+
when Net::HTTPSuccess then return [res.to_hash, res.body]
|
154
|
+
else raise Allorails::ApiUnavailableResourceError, "Request failed: #{res.body}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
## Internal method encoding request paramters
|
160
|
+
#
|
161
|
+
# @return (string) Encoded request parameters
|
162
|
+
def _encode_parameters
|
163
|
+
params = @_parameters.dup
|
164
|
+
# The Allopass API expects an array of codes encoded
|
165
|
+
# in a slightly different matter than urlencode does
|
166
|
+
if params.has_key?('code')
|
167
|
+
codes = params.delete('code')
|
168
|
+
hash_codes = Hash[[0..codes.length-1].map{|i| ["code[#{i}]",codes[i]]}]
|
169
|
+
params = params.merge(hash_codes)
|
170
|
+
end
|
171
|
+
|
172
|
+
URI::encode params.collect { |k,v| "#{k}=#{v}" }.join('&') #CGI::escape(v.to_s)
|
173
|
+
end
|
174
|
+
|
175
|
+
end # -- end class ApiRequest
|
176
|
+
|
177
|
+
|
178
|
+
## Class providing a onetime pricing API request
|
179
|
+
class OnetimePricingRequest < ApiRequest
|
180
|
+
PATH = '/onetime/pricing'
|
181
|
+
|
182
|
+
def _new_response(signature, headers, body)
|
183
|
+
Allorails::Response::OnetimePricingResponse.new(signature, headers, body)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
## Class providing a onetime dicrete pricing API request
|
189
|
+
class OnetimeDiscretePricingRequest < ApiRequest
|
190
|
+
PATH = '/onetime/discrete-pricing'
|
191
|
+
|
192
|
+
def _new_response(signature, headers, body)
|
193
|
+
Allorails::Response::OnetimePricingResponse.new(signature, headers, body)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
## Class providing a onetime validate codes API request
|
199
|
+
class OnetimeValidateCodesRequest < ApiRequest
|
200
|
+
PATH = '/onetime/validate-codes'
|
201
|
+
|
202
|
+
def _is_http_post
|
203
|
+
true
|
204
|
+
end
|
205
|
+
|
206
|
+
def _new_response(signature, headers, body)
|
207
|
+
Allorails::Response::OnetimeValidateCodesResponse.new(signature, headers, body)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
## Class providing a product detail API request
|
213
|
+
class ProductDetailRequest < ApiRequest
|
214
|
+
PATH = '/product'
|
215
|
+
|
216
|
+
def _new_response(signature, headers, body)
|
217
|
+
Allorails::Response::ProductDetailResponse.new(signature, headers, body)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
|
222
|
+
## Class providing a transaction prepare API request
|
223
|
+
class TransactionPrepareRequest < ApiRequest
|
224
|
+
PATH = '/transaction/prepare'
|
225
|
+
|
226
|
+
def _is_http_post
|
227
|
+
true
|
228
|
+
end
|
229
|
+
|
230
|
+
def _new_response(signature, headers, body)
|
231
|
+
Allorails::Response::TransactionPrepareResponse.new(signature, headers, body)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
## Class providing a transaction detail API request
|
237
|
+
class TransactionDetailRequest < ApiRequest
|
238
|
+
PATH = '/transaction'
|
239
|
+
|
240
|
+
def _new_response(signature, headers, body)
|
241
|
+
Allorails::Response::TransactionDetailResponse.new(signature, headers, body)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
|
246
|
+
## Class providing a transaction detail from merchant transaction id API request
|
247
|
+
class TransactionMerchantRequest < ApiRequest
|
248
|
+
PATH = '/transaction/merchant'
|
249
|
+
|
250
|
+
def _new_response(signature, headers, body)
|
251
|
+
Allorails::Response::TransactionDetailResponse.new(signature, headers, body)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
## Class providing a onetime button API request
|
257
|
+
class OnetimeButtonRequest < ApiRequest
|
258
|
+
PATH = '/onetime/button'
|
259
|
+
|
260
|
+
def _is_http_post
|
261
|
+
true
|
262
|
+
end
|
263
|
+
|
264
|
+
def _new_response(signature, headers, body)
|
265
|
+
Allorails::Response::OnetimeButtonResponse.new(signature, headers, body)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
## Class providing a onetime discrete button API request
|
271
|
+
class OnetimeDiscreteButtonRequest < ApiRequest
|
272
|
+
PATH = '/onetime/discrete-button'
|
273
|
+
|
274
|
+
def _is_http_post
|
275
|
+
true
|
276
|
+
end
|
277
|
+
|
278
|
+
def _new_response(signature, headers, body)
|
279
|
+
Allorails::Response::OnetimeButtonResponse.new(signature, headers, body)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
module Allorails
|
2
|
+
|
3
|
+
|
4
|
+
## Basis for the models used in the Apikit object responses
|
5
|
+
# extends ApiMappingResponse to inherit the node_reader helper
|
6
|
+
class Base < Allorails::Response::ApiMappingResponse
|
7
|
+
|
8
|
+
def initialize(node)
|
9
|
+
@json = node
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
## Class providing object mapping of a website item
|
16
|
+
class Website < Base
|
17
|
+
|
18
|
+
## Provides the website id
|
19
|
+
# @return (int) website id
|
20
|
+
node_reader :id, Integer
|
21
|
+
|
22
|
+
## Provides the website name
|
23
|
+
# @return (string) website name
|
24
|
+
node_reader :name
|
25
|
+
|
26
|
+
## Provides the website url
|
27
|
+
# @return (string) website url
|
28
|
+
node_reader :url
|
29
|
+
|
30
|
+
## Checks wether the website's audience is restricted
|
31
|
+
# @return (boolean) wether the website's audience is restricted
|
32
|
+
def audience_restricted?
|
33
|
+
@json.audience_restricted == 'true'
|
34
|
+
end
|
35
|
+
|
36
|
+
## Provides the website category
|
37
|
+
# @return (string) website category
|
38
|
+
node_reader :category
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
## Class providing object mapping of a country item
|
44
|
+
class Country < Base
|
45
|
+
|
46
|
+
## Provides the country code
|
47
|
+
# @return (string) country code (two characters)
|
48
|
+
node_reader :code
|
49
|
+
|
50
|
+
## Provides the country name
|
51
|
+
# @return (string) country name
|
52
|
+
node_reader :name
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
## Class providing object mapping of a region item
|
58
|
+
class Region < Base
|
59
|
+
|
60
|
+
## Provides the region name
|
61
|
+
# @return (string) region name
|
62
|
+
node_reader :name
|
63
|
+
|
64
|
+
## Provides the countries in the region
|
65
|
+
# @return (Array) region's countries
|
66
|
+
def countries
|
67
|
+
json.children.values.map{|c| ::Allorails::Country.new(c)}
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
## Class providing object mapping of a market item
|
74
|
+
class Market < Base
|
75
|
+
|
76
|
+
## Provides the country code
|
77
|
+
# @return (string) country code (two characters)
|
78
|
+
node_reader :country_code
|
79
|
+
|
80
|
+
## Provides the country name
|
81
|
+
# @return (string) country name
|
82
|
+
node_reader :country
|
83
|
+
|
84
|
+
## Provides the pricepoints available in this market
|
85
|
+
# @return (Array) available pricepoints (list of Pricepoint objects)
|
86
|
+
def pricepoints
|
87
|
+
json.children.values.map{|c| ::Allorails::Pricepoint.new(c)}
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
## Class providing object mapping of a price item
|
94
|
+
class Price < Base
|
95
|
+
|
96
|
+
## Provides the currency
|
97
|
+
# @return (string) currency (three characters)
|
98
|
+
node_reader :currency
|
99
|
+
|
100
|
+
## Provides the amount
|
101
|
+
# @return (float) amount
|
102
|
+
node_reader :currency, Float
|
103
|
+
|
104
|
+
## Provides the day's exchange rate
|
105
|
+
# @return (float) exchange rate
|
106
|
+
node_reader :exchange, Float
|
107
|
+
|
108
|
+
## Provides the reference currency
|
109
|
+
# @return (string) reference currency
|
110
|
+
node_reader :reference_currency
|
111
|
+
|
112
|
+
## Provides the amount in the reference currency
|
113
|
+
# @return (float) reference amount
|
114
|
+
node_reader :reference_amount, Float
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
## Class providing object mapping of a payout item
|
119
|
+
class Payout < Base
|
120
|
+
|
121
|
+
## Provides the currency
|
122
|
+
# @return (string) currency (three characters)
|
123
|
+
node_reader :currency
|
124
|
+
|
125
|
+
## Provides the amount
|
126
|
+
# @return (float) amount
|
127
|
+
node_reader :amount, Float
|
128
|
+
|
129
|
+
## Provides the day's exchange rate
|
130
|
+
# @return (float) exchange rate
|
131
|
+
node_reader :exchange, Float
|
132
|
+
|
133
|
+
## Provides the reference currency
|
134
|
+
# @return (string) reference currency
|
135
|
+
node_reader :reference_currency
|
136
|
+
|
137
|
+
## Provides the amount in the reference currency
|
138
|
+
# @return (float) reference amount
|
139
|
+
node_reader :reference_amount, Float
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
## Class providing object mapping of a phone number item
|
145
|
+
class PhoneNumber < Base
|
146
|
+
|
147
|
+
## Provides the phone number
|
148
|
+
# @return (string) phone number
|
149
|
+
node_reader :value
|
150
|
+
|
151
|
+
## Provides the description of the phone number
|
152
|
+
# @return (string) description
|
153
|
+
node_reader :description
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
## Class providing object mapping of a keyword item
|
159
|
+
class Keyword < Base
|
160
|
+
|
161
|
+
## Provides the keyword name
|
162
|
+
# @return (string) keyword name
|
163
|
+
node_reader :name
|
164
|
+
|
165
|
+
## Provides the keyword's shortcode
|
166
|
+
# @return (string) shortcode
|
167
|
+
node_reader :shortcode
|
168
|
+
|
169
|
+
## Provides the keyword operators
|
170
|
+
# @return (string) keyword operators
|
171
|
+
node_reader :operators
|
172
|
+
|
173
|
+
## Provides the number of billed messages of the keyword
|
174
|
+
# @return (int) number of billed messages
|
175
|
+
node_reader :number_billed_messages, Integer
|
176
|
+
|
177
|
+
## Provides a description of the keyword
|
178
|
+
# @return (string) description
|
179
|
+
node_reader :description
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
## Class providing object mapping of a pricepoint item
|
185
|
+
class Pricepoint < Base
|
186
|
+
|
187
|
+
## Provides the pricepoint's id
|
188
|
+
# @return (int) pricepoint id
|
189
|
+
node_reader :id, Integer
|
190
|
+
|
191
|
+
## Provides the pricepoint's type
|
192
|
+
# @return (string) pricepoint type
|
193
|
+
node_reader :type
|
194
|
+
|
195
|
+
## Provides the pricepoint's country code
|
196
|
+
# @return (string) country code (two characters)
|
197
|
+
node_reader :country_code
|
198
|
+
|
199
|
+
## Provides price information
|
200
|
+
# @return (Price) price information
|
201
|
+
node_reader :price, ::Allorails::Price
|
202
|
+
|
203
|
+
## Provides the pricepoint's payout
|
204
|
+
# @return (Payout) pricepoint's payout
|
205
|
+
node_reader :payout, ::Allorails::Payout
|
206
|
+
|
207
|
+
## Provides the buy url
|
208
|
+
# @return (string) buy url
|
209
|
+
node_reader :buy_url
|
210
|
+
|
211
|
+
## Provides the pricepoint's phone numbers
|
212
|
+
# @return (list) pricepoint's phone number (list of PhoneNumber objects)
|
213
|
+
def phone_numbers
|
214
|
+
return nil if json.phone_numbers.nil?
|
215
|
+
json.phone_numbers.children.values.map{|c| ::Allorails::PhoneNumber(c)}
|
216
|
+
end
|
217
|
+
|
218
|
+
## Provides the pricepoint's keywords
|
219
|
+
# @return (list) pricepoint's keywords (list of Keyword objects)
|
220
|
+
def keywords
|
221
|
+
return nil if json.keywords.nil?
|
222
|
+
json.keywords.children.values.map{|c| ::Allorails::Keyword(c)}
|
223
|
+
end
|
224
|
+
|
225
|
+
## Provides the pricepoint's description
|
226
|
+
# @return (string) pricepoint's description
|
227
|
+
node_reader :description
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
## Class providing object mapping of a partner item
|
233
|
+
class Partner < Base
|
234
|
+
|
235
|
+
## Provides the partner id
|
236
|
+
# @return (int) partner id
|
237
|
+
node_reader :id, Integer
|
238
|
+
|
239
|
+
## Provides the parnter's amount share
|
240
|
+
# @return (float) parnter's amount share
|
241
|
+
node_reader :share, Float
|
242
|
+
|
243
|
+
## Provides the associated map id
|
244
|
+
# @return (int) map id
|
245
|
+
def map
|
246
|
+
return nil unless json.map.is_a?(String) && json.map.length > 0
|
247
|
+
json.map.to_i
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
## Class providing object mapping of a code item
|
254
|
+
class Code < Base
|
255
|
+
|
256
|
+
## Provides the code value
|
257
|
+
# @return (string) code
|
258
|
+
node_reader :value
|
259
|
+
|
260
|
+
## Provides the pricepoint's description
|
261
|
+
# @return (string) pricepoint's description
|
262
|
+
node_reader :pricepoint, ::Allorails::Pricepoint
|
263
|
+
|
264
|
+
## Provides price information
|
265
|
+
# @return (Price) price information
|
266
|
+
node_reader :price, ::Allorails::Price
|
267
|
+
|
268
|
+
## Provides paid price information
|
269
|
+
# @return (Price) paid price information
|
270
|
+
node_reader :paid, ::Allorails::Price
|
271
|
+
|
272
|
+
## Provides the pricepoint's payout
|
273
|
+
# @return (Payout) pricepoint's payout
|
274
|
+
node_reader :payout, ::Allorails::Payout
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
end
|