allorails 0.3.1
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/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
|