eme_services_client 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8d2cc3fdf5fc14a7e25ee4744f916260a4a7a011
4
+ data.tar.gz: 7192366bb021bb78d12e55605b56a7714a2b700f
5
+ SHA512:
6
+ metadata.gz: b042ea84d0fcf96f92663e071728c2a709e01cc2219e224cf11a431a2007dbda676fdc685372a8c65599c7e365fc5f926a717608ef75ae491dc9b68a362dcd86
7
+ data.tar.gz: 931b955f67ffaecf09f663c8a2e78e2788cd93537400e0e5984ed2b3b357a3e29f365df4b62fb9f30a8872b3683b02c23764f9f0268cd010616a02c83dbf38ba
@@ -0,0 +1,132 @@
1
+ require 'api_consumer'
2
+
3
+ module EME
4
+ class AdminUser < APIConsumer
5
+ # if successful username/password combo, it should return a unique 16 digit hex session id for the user.
6
+ # {"admin_session_key": "432423h13h341ab2", "error": false}
7
+ # if failed it should return error true, and error message.
8
+ # {"error": true, "message": "Incorrect email/password combination."}
9
+ # NOTE: This is the only call that does not require an admin_session_key in the headers/cookies, still requires the API_KEY.
10
+ def self.login(email, password, conn = connection, opts = {})
11
+ opts[:method] = :post
12
+ opts[:body] = {:email => email, :password => password}.to_json
13
+ return do_request("/users/login", conn, opts_work(opts))
14
+ end
15
+
16
+ # a get or delete call. It only requires the first 8 digits of the admin_session_key, but will accept all 16 digits.
17
+ def self.logout(admin_session_key, conn = connection, opts = {})
18
+ opts[:method] = :delete
19
+ return do_request("/users/logout", conn, opts_work(opts, admin_session_key))
20
+ end
21
+
22
+ # returns all users if they are still logged in, and have permissions to see all users, user data only includes, id, name, last_login, roles, and current_logged_in_status
23
+ def self.all_users(admin_session_key, conn = connection, opts={})
24
+ return do_request("/users", conn, opts_work(opts, admin_session_key))
25
+ end
26
+
27
+ # returns specific users data, DO NOT include session_key_id, or password/hashed_password. Requires permission check.
28
+ def self.user(user_id, admin_session_key, conn = connection, opts={})
29
+ return do_request("/users/#{user_id}", conn, opts_work(opts, admin_session_key))
30
+ end
31
+
32
+ # takes a hash of user info, and posts it to the service to edit the user record, if they have permissions.
33
+ # if there is a password change it requires the fields password, and confirmed password.
34
+ def self.update_user(user_data, admin_session_key, conn = connection, opts={})
35
+ opts[:method] = :post
36
+ opts[:body] = user_data.to_json
37
+ return do_request("/users/#{user_id}", conn, opts_work(opts, admin_session_key))
38
+ end
39
+
40
+ # requires admin_session_key. Just returns true or false if you are still logged in or not.
41
+ # if still logged in: returns json {"logged_in": true, "permissions_updated": false}
42
+ # Most likely to be used via AJAX on the front end.
43
+ def self.heartbeat(admin_session_key, conn = connection, opts={})
44
+ return do_request("/users/heartbeat", conn, opts_work(opts, admin_session_key))
45
+ end
46
+
47
+ # This is the call that returns the current user data, including permissions and roles, this will be converted to a Session object that will be used by the app to get your logged in status, and permissions, etc.
48
+ def self.me(admin_session_key, conn = connection, opts={})
49
+ tmp = do_request("/users/me", conn, opts_work(opts, admin_session_key))
50
+ end
51
+
52
+ ### ROLES
53
+
54
+ # returns all roles if they are still logged in, and have permissions to see all roles, role data only includes, id, name
55
+ def self.all_roles(admin_session_key, conn = connection, opts={})
56
+ return do_request("/roles", conn, opts_work(opts, admin_session_key))
57
+ end
58
+
59
+ # returns specific a role's data; id, name, permissions, users with role.
60
+ def self.role(role_id, admin_session_key, conn = connection, opts={})
61
+ return do_request("/roles/#{role_id}", conn, opts_work(opts, admin_session_key))
62
+ end
63
+
64
+ # takes a hash of role info(name, and permissions), and posts it to the service to edit the role, if they have permissions.
65
+ def self.update_role(role_data, admin_session_key, conn = connection, opts={})
66
+ opts[:method] = :post
67
+ opts[:body] = role_data.to_json
68
+ return do_request("/roles/#{user_id}", conn, opts_work(opts, admin_session_key))
69
+ end
70
+
71
+ ### PERMISSIONS
72
+
73
+ # returns all permissions if they are still logged in, and have permissions to see all permissions, permission data only includes, id, controller, action, method
74
+ def self.all_permissions(admin_session_key, conn = connection, opts={})
75
+ return do_request("/permissions", conn, opts_work(opts, admin_session_key))
76
+ end
77
+
78
+ ### SESSION COOKIES
79
+
80
+ def self.decode_admin_key_cookie(val, ip)
81
+ check_salts
82
+ ((val.to_s.hex ^ self.settings[:pepper].hex) - self.settings[:salt].hex - ipaddress_factor(ip)).to_s(16)
83
+ end
84
+
85
+ def self.encode_admin_key_cookie(val, ip)
86
+ check_salts
87
+ return ((self.settings[:salt].hex + val.to_s.hex + ipaddress_factor(ip)) ^ self.settings[:pepper].hex).to_s(16)
88
+ end
89
+
90
+ def self.check_salts
91
+ if(self.settings[:salt].nil? || self.settings[:pepper].nil? || self.settings[:salt].length < 15 || self.settings[:pepper].length < 15)
92
+ raise RuntimeError, "Requires settings salt and pepper, min length 15."
93
+ end
94
+ end
95
+
96
+ def self.ipaddress_factor(ip)
97
+ ip.gsub(/\./, "d").to_i(17) * 93
98
+ end
99
+
100
+ private
101
+ @@auth_opts = { "Authorization" => "Token token=#{settings[:api_key]}" }
102
+
103
+ def self.opts_work(opts, admin_session_key = nil)
104
+ if opts[:headers].nil?
105
+ opts[:headers] = @@auth_opts
106
+ else
107
+ opts[:headers].merge(@@auth_opts)
108
+ end
109
+ opts[:headers]["Authorization"] = "#{opts[:headers]["Authorization"]}|#{admin_session_key}" unless admin_session_key.nil?
110
+ #opts[:headers]["Authorization"] += "|"+admin_session_key unless admin_session_key.nil?
111
+ return opts
112
+ end
113
+
114
+ class SessionInfo
115
+ attr_reader :user, :roles
116
+ BITS = []
117
+
118
+ def initialize(data)
119
+ @permissions = data["permissions"]
120
+ @user = data["user"]
121
+ @roles = data["roles"]
122
+ end
123
+
124
+ def has_permission?(app, component, action)
125
+ return @permissions[controller.to_s][action.to_s][method.to_s] if @permissions[controller.to_s] && @permissions[controller.to_s][action.to_s]
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ require_relative "admin_user/required_login"
132
+
@@ -0,0 +1,44 @@
1
+ class EME::AdminUser::RequiredLogin
2
+
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ req = Rack::Request.new(env)
9
+
10
+ # whitelist check/assets
11
+ if(["/assets/", "/images/"].include?(req.path[0..7]) || (EME::AdminUser.settings[:white_list_paths] && EME::AdminUser.settings[:white_list_paths].include?(req.path)))
12
+ return @app.call(env)
13
+ end
14
+
15
+ cookies = req.cookies
16
+ session = req.session
17
+ good = false
18
+ ip = req.ip
19
+ if session[:admin_session_key]
20
+ heartbeat = EME::AdminUser.heartbeat(session[:admin_session_key])
21
+ if heartbeat["logged_in"] == true
22
+ data = EME::AdminUser.me(session[:admin_session_key])
23
+ data_permissions = data.delete(:permissions)
24
+ puts "PERMISSIONS + #{data_permissions}"
25
+ permissions = EME::AdminUser.all_permissions(session[:admin_session_key])
26
+
27
+ if session[:admin_user_info].nil? || heartbeat["permissions"] != "uptodate"
28
+ session[:admin_user_info] = EME::AdminUser.me(session[:admin_session_key])
29
+ end
30
+ good = true
31
+ end
32
+ end
33
+
34
+ if !good && (req.path != EME::AdminUser.settings[:login_path])
35
+ return [302, {"Location" => req.base_url + EME::AdminUser.settings[:login_path] }, []]
36
+ end
37
+
38
+ @status, @headers, @response = @app.call(env)
39
+ if req.cookies["admin_session_key"].nil? && !session[:admin_session_key].nil?
40
+ Rack::Utils.set_cookie_header!(@headers, "admin_session_key", {:value => EME::AdminUser.encode_admin_key_cookie(session[:admin_session_key], ip), :path => "/", :domain => ".enmasse.com", :expires => Time.now+86400})
41
+ end
42
+ return [@status, @headers, @response]
43
+ end
44
+ end
@@ -0,0 +1,29 @@
1
+ require 'api_consumer'
2
+
3
+ class EME::AMS < APIConsumer
4
+
5
+ def self.game_account(game_account_id, conn = connection)
6
+ do_request("/api/v1/game_accounts/#{game_account_id}", conn)
7
+ end
8
+
9
+ def self.game_accounts(master_account_id, conn = connection)
10
+ do_request("/api/v1/game_accounts/all/#{master_account_id}", conn)
11
+ end
12
+
13
+ def self.vip_exp()
14
+ do_request("/api/v1/game-accounts/#{game-account_id}/get_vip_exp")
15
+ end
16
+
17
+ def self.vip_crystals()
18
+ do_request("/api/v1/vip/")
19
+ end
20
+
21
+ def self.add_vip_crystals()
22
+
23
+ end
24
+
25
+ def self.add_vip_exp()
26
+ # POST
27
+ #do_request("/api/v1/game-accounts/#{game-account_id}/get_vip_exp")
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ require 'api_consumer'
2
+
3
+ class EME::Auth < APIConsumer
4
+
5
+ def self.make_ticket(game_account_id, conn = connection)
6
+ do_request("/ticket/#{game_account_id}", conn)
7
+ end
8
+
9
+ #http://auth.service.edge.enmasse.com:4567/ticket/TGmMuniGyatDoTnythxyoT-pvad3gTRmRmmiwxv4ozsHK-tgd-/verify_simple
10
+ def self.verify_simple_ticket(ticket, conn = connection)
11
+ path = URI.encode("/ticket/#{ticket}/verify_simple")
12
+ do_request(path, conn, {:method => :post, :errors => {'404' => 'Ticket not found or already used.'}})
13
+ end
14
+
15
+ def self.verify_ticket(ticket, conn = connection)
16
+ path = URI.encode("/ticket/#{ticket}/verify")
17
+ do_request(path, conn, {:method => :post, :errors => {'404' => 'Ticket not found or already used.'}})
18
+ end
19
+
20
+ def self.generate_authentication_token(game_account_id, conn = connection)
21
+ do_request("/api/v1/generate_authentication_token?game_account_id=#{game_account_id}", conn)
22
+ end
23
+
24
+ end
@@ -0,0 +1,496 @@
1
+ require 'api_consumer'
2
+ require 'sucker_punch'
3
+ require_relative 'billing/workers/refresh_wallet_worker'
4
+ #require_relative 'billing/store_item'
5
+
6
+ class EME::Billing < APIConsumer
7
+ # Fetch the balance of user with external id equal to *external_id*. Will cache the results for 5 minutes if settings[:use_memcache] is true
8
+ #
9
+ # ==== Attributes
10
+ #
11
+ # * external_id - master_account_id of the player
12
+ # * external_account_id - Game id of the game account you are connecting too (required if multiple game accounts)
13
+ #
14
+ # ==== Options
15
+ #
16
+ # none
17
+ #
18
+ # ==== Examples
19
+ #
20
+ # EME::Billing.get_wallet(609)
21
+ def self.reload_wallet(external_id, currency = "EMP", opts = {}, conn = connection)
22
+ return get_wallet(external_id, currency, opts, conn, true)
23
+ end
24
+
25
+ def self.get_wallet(external_id, currency = "EMP", opts = {}, conn = connection, reload=false)
26
+ if settings[:use_memcache] && !reload
27
+ cached = cache.read("wallet-balance-#{external_id}-#{currency}")
28
+ return cached if cached
29
+ backup_cache = cache.read("wallet-balance-#{external_id}-#{currency}-bkup")
30
+ if backup_cache # Start ASYNC refresh here
31
+ RefreshWalletWorker.new.async.perform(external_id, currency, opts)
32
+ return backup_cache
33
+ end
34
+ end
35
+ gamecode = opts[:game_code] || "TERA"
36
+ wallet = nil
37
+ request_path = "/Payment/rest/services/balance/v1/get/#{external_id}/#{gamecode}/#{currency}/"
38
+
39
+ obj_hash = do_request(request_path, conn)
40
+
41
+ if obj_hash["RESPONSE"]
42
+ wallet = EME::Billing::Wallet.new(obj_hash["RESPONSE"])
43
+ else
44
+ wallet = EME::Billing::Wallet.new({"TOTREMAINAMT" => 0})
45
+ end
46
+
47
+ # save to memcache
48
+ if settings[:use_memcache]
49
+ cache.write("wallet-balance-#{external_id}-#{currency}", wallet, {:ttl => 300})
50
+ cache.write("wallet-balance-#{external_id}-#{currency}-bkup", wallet, {:ttl => 3600})
51
+ end
52
+ wallet
53
+ end
54
+
55
+ def self.currencies(gamecode, types = [], reload = false, conn = connection)
56
+ fetch_currencies(gamecode, "EMP", types, reload, conn)
57
+ end
58
+
59
+ def self.subscriptions(gamecode, types = [], invisible = false, reload = false, conn = connection)
60
+ fetch_currencies(gamecode, "SUB", types, invisible, reload, conn)
61
+ end
62
+
63
+ def self.fetch_currencies(gamecode, kind="EMP", types = [], invisible = false, reload = false, conn = connection)
64
+ cache_key = "currency-list-#{gamecode}-kind#{kind}-#{types.sort.join('-')}-#{'invs' if invisible}".downcase
65
+ if settings[:use_memcache] && !reload
66
+ cached = cache.obj_read(cache_key)
67
+ return cached if cached
68
+ end
69
+
70
+ currs = nil
71
+ request_path = URI.encode("/Payment/rest/services/virtualcurrency/v1/list/#{gamecode}/#{kind}/^/^#{'/^' if invisible}")
72
+ obj_hash = do_request(request_path, conn)
73
+
74
+ currs = CurrencyCollection.new(obj_hash["LIST"], types)
75
+ # save to memcache
76
+ if settings[:use_memcache]
77
+ cache.obj_write(cache_key, currs, {:ttl => 300})
78
+ end
79
+ currs
80
+ end
81
+
82
+ def self.currency_by_id(gamecode, id)
83
+ currs = currencies(gamecode)
84
+ return currs.currencies.select{|x| x.id == id }[0]
85
+ end
86
+
87
+ def self.subscription_by_id(gamecode, id, reload = false, conn = connection)
88
+ subs = subscriptions(gamecode, [], true)
89
+ return subs.currencies.select{|x| x.id == id }[0]
90
+ end
91
+
92
+ def self.cash_payment_types(gamecode, reload = false, conn = connection)
93
+ if settings[:use_memcache] && !reload
94
+ cached = cache.obj_read("payment-type-list-#{gamecode}")
95
+ return cached if cached
96
+ end
97
+
98
+ pay_types = []
99
+ request_path = URI.encode("/Payment/rest/services/virtualcurrency/v1/list/#{gamecode}/USD/^/^")
100
+ obj_hash = do_request(request_path, conn)
101
+ obj_hash["LIST"].each do |pt_hash|
102
+ pay_types.push(PaymentType.new(pt_hash))
103
+ end
104
+ pay_types.sort!{|a,b| a.sortno <=> b.sortno }
105
+
106
+ if settings[:use_memcache]
107
+ cache.obj_write("payment-type-list-#{gamecode}", pay_types, {:ttl => 300})
108
+ end
109
+ return pay_types
110
+ end
111
+ # Fetch the array of all offers. Will cache the results for 5 minutes if settings[:use_memcache] is true
112
+ #
113
+ # ==== Attributes
114
+ #
115
+ # none
116
+ #
117
+ # ==== Options
118
+ #
119
+ # none
120
+ #
121
+ # ==== Examples
122
+ #
123
+ # EME::Billing.offers()
124
+ def self.offers(game=nil, lang = nil, reset_cache = false, conn = connection)
125
+ custom_offers("all", game, lang, reset_cache, conn)
126
+ end
127
+
128
+ def self.find_item(item_id, game=nil, reset_cache = false)
129
+ game = game || settings[:game]
130
+ request_url = URI.encode("/Item/rest/services/#{game}/gameitem/v1/get/#{item_id}")
131
+ return do_request(request_url, connection(:normal), {:key => "#{game}-single-item-#{item_id}", :ttl => 60})
132
+ end
133
+
134
+ def self.custom_offers(tag = "all", game = nil, lang = nil, reset_cache = false, conn = connection)
135
+ raise RuntimeError, "game required" unless game
136
+ if !reset_cache && settings[:use_memcache]
137
+ cached = cache.obj_read("#{game.downcase}-all-#{tag}-#{lang}")
138
+ return cached if cached
139
+ end
140
+
141
+ local_offers = []
142
+ cur_page = 1
143
+ offers_per_req = 100 #250 when that is release
144
+
145
+ tag_data = tag == "all" ? "^" : "&attributes=#{tag}"
146
+
147
+ tag_data = URI.escape(tag_data)
148
+ cur_offers = []
149
+ while(cur_page == 1 || cur_offers.length == offers_per_req)
150
+ base_offer_api_request = URI.encode("/Item/rest/services/#{game}/gameitem/v1/page/#{offers_per_req}/#{cur_page}/^/true")
151
+ #puts base_offer_api_request
152
+
153
+ cur_offers = []
154
+
155
+ begin
156
+ obj_array = do_request(base_offer_api_request, conn)
157
+ obj_array["LIST"] = [] if obj_array["LIST"].nil?
158
+ obj_array["LIST"].each do |obj|
159
+ cur_offers << Offer.new(obj)
160
+ end
161
+ rescue RuntimeError => exception
162
+ puts exception.backtrace
163
+ obj_array = {"message" => "Error contacting store."}
164
+ return [] # No offer found
165
+ end
166
+
167
+ cur_page += 1
168
+ local_offers += cur_offers
169
+ end
170
+
171
+ # save to memcache
172
+ if settings[:use_memcache]
173
+ cache.obj_write("#{game.downcase}-all-#{tag}-#{lang}", local_offers, {:ttl => 3600})
174
+ end
175
+
176
+ local_offers
177
+ end
178
+
179
+ def self.campaigns(game = nil, lang = 'en', reset_cache = false, conn = connection)
180
+ return custom_campaigns("all", game, lang, reset_cache, conn)
181
+ end
182
+
183
+ def self.custom_campaigns(tag = "all", game = nil, lang = 'en', reset_cache = false, conn = connection)
184
+ raise RuntimeError, "game required" unless game
185
+ if !reset_cache && settings[:use_memcache]
186
+ cached = cache.read("#{game.downcase}-campaigns-#{tag}-#{lang}")
187
+ return cached if cached
188
+ end
189
+
190
+ local_campaigns = []
191
+ cur_page = 1
192
+ camps_per_request = 100
193
+
194
+ cur_camps = []
195
+ while(cur_page == 1 || cur_camps.length == camps_per_request)
196
+ base_campaign_api_request = URI.encode("/Item/rest/services/#{game}/promotion/v1/page/#{camps_per_request}/#{cur_page}/true")
197
+ cur_camps = []
198
+
199
+ # store in memcache here
200
+ obj_array = do_request(base_campaign_api_request, conn)
201
+ obj_array["LIST"] = [] if obj_array["LIST"].nil?
202
+ obj_array["LIST"].each do |obj|
203
+ cur_camps << Campaign.new(campaign_hash_map(obj))
204
+ end
205
+
206
+ cur_page += 1
207
+ local_campaigns += cur_camps
208
+ end
209
+ elite_campaigns = local_campaigns.select{|x| x.tags.include?("promo_tag:elite") }
210
+ sale_campaigns = local_campaigns.select{|x| x.tags.include?("promo_tag:sale") }
211
+
212
+ # save to memcache
213
+ if settings[:use_memcache]
214
+ cache.write("#{game.downcase}-campaigns-#{tag}-#{lang}", [elite_campaigns, sale_campaigns], {:ttl => 3600})
215
+ end
216
+
217
+ return [elite_campaigns, sale_campaigns]
218
+ end
219
+
220
+ def self.campaign_hash_map(camp)
221
+ {
222
+ :tags => (camp["TAGS"] || []).uniq,
223
+ :images => camp["IMAGES"],
224
+ :name => camp["EVENTNAME"],
225
+ :id => camp["EVENTID"],
226
+ :longDescription => camp["HTMLLONGDESCRIPTION"],
227
+ :discount_rate => camp["TOTDISCOUNTRATE"],
228
+ :discount_amount => camp["TOTDISCOUNTAMOUNT"],
229
+ :items => camp["ITEMS"],
230
+ :start_at => camp["STARTDATE"],
231
+ :end_at => camp["ENDDATE"]
232
+ }
233
+ end
234
+
235
+ def self.purchase(external_id, item, currency = "EMP", opts = {}, conn = connection)
236
+ purchase_body = {}
237
+ # We need accountID, and gameAccountId!!!
238
+ purchase_body = {
239
+ "USERNO" => external_id,
240
+ "USERID" => opts[:email],
241
+ "GAMEUSERNO" => opts[:game_account_id],
242
+ "QUANTITY" => opts[:quantity ] || 1,
243
+ "GAMECODE" => opts[:game_code],
244
+ "USERSUBSET" => opts[:elite] ? "elite" : "normal",
245
+ "PRICEID" => opts[:price_point_id],
246
+ "COUNTRYCODE" => "SGS", #"USA" # hard-coded due to bug in FFG system with ip addresses We are using SGS (South Geargian, and the Sandwich Islands) to denote Web Sales.
247
+ "CHARGEAMT" => opts[:amount],
248
+ "LOCATION" => opts[:location] || "web",
249
+ "ORDERTRANSACTIONID" => "#{external_id}-#{opts[:game_account_id]}-#{Time.now.strftime('%Y%m%d%H%M%S%L')}"
250
+ }
251
+ purchase_body["EVENTID"] = opts[:campaign_id] if opts[:campaign_id]
252
+ purchase_body["CHECKSUM"] = checksum "#{external_id}|#{opts[:email]}|#{opts[:game_account_id]}|#{purchase_body['GAMECODE']}|#{opts[:price_point_id]}|SGS|#{purchase_body['ORDERTRANSACTIONID']}"
253
+
254
+ raise RuntimeError, "Account required." if purchase_body["USERNO"].nil?
255
+ raise RuntimeError, "Game Account required." if purchase_body["GAMEUSERNO"].nil?
256
+ raise RuntimeError, "Price required." if purchase_body["PRICEID"].nil? || purchase_body["CHARGEAMT"].nil?
257
+ #raise RuntimeError, "IP Address required." if purchase_body["ipAddress"].nil?
258
+ raise RuntimeError, "Quantity required." if purchase_body["QUANTITY"].nil?
259
+
260
+ json = JSON.generate(purchase_body)
261
+
262
+ purchase_path = "/Payment/rest/services/purchase/v1/item"
263
+
264
+ obj_hash = do_request(purchase_path, conn, {:method => :post, :body => json})
265
+ # PUTS NOT CHECKING CORRECT ERROR MESSAGES YET!!! -CR-
266
+ if(obj_hash["RETCODE"] == 9998)
267
+ puts obj_hash.inspect
268
+ return {"message" => "Server failed to remove your EMP. Sorry no item for you right now.", "error" => true}
269
+ #return {"message" => "You do not have enough EMP for this purchase. Please buy EMP and try again.", "error" => true}
270
+ elsif(obj_hash["RETCODE"] == 0)
271
+ return {"transaction_id" => obj_hash["RESPONSE"]["TRANSACTIONID"], "error" => false, "price" => opts[:amount] || item.price }
272
+ else
273
+ puts obj_hash.inspect
274
+ return {"message" => obj_hash["ERRMSG"], "error" => true}
275
+ end
276
+ end
277
+
278
+ def self.billing_info(external_id, conn = connection)
279
+ res = do_request("/Payment/rest/services/billinginfo/v1/get/#{external_id}", conn)
280
+ return res["RESPONSE"] if res["RESPONSE"]
281
+ return res
282
+ end
283
+ # StoreAPI.do_request("/Payment/rest/services/billinginfo/v1/get/100001")
284
+
285
+ def self.subscription_billing_info(external_id, conn = connection)
286
+ res = do_request("/Payment/rest/services/get/v1/Subscription/#{external_id}", conn)
287
+ return res["RESPONSE"] if res["RESPONSE"]
288
+ return res
289
+ end
290
+ # StoreAPI.do_request("/Payment/rest/services/get/v1/Subscription/100001")
291
+
292
+ # requires publisher as AMAZON or STEAM.
293
+ def self.add_offsite_emp(external_id, emp_amount, opts, conn = connection )
294
+ emp_body = { "USERNO" => external_id,
295
+ "USERID" => opts[:user_email],
296
+ "PGCODE" => opts[:publisher],
297
+ "PAYAMT" => opts[:pay_amount],
298
+ "TAXAMT" => opts[:tax_amount],
299
+ "CURRENCYCODE" => "USD",
300
+ "CASHAMT" => emp_amount,
301
+ "ORDERTRANSACTIONID" => "#{opts[:publisher]}#{opts[:order_id]}",
302
+ "GAMECODE" => opts[:game_code],
303
+ "COUNTRYCODE" => "USA",
304
+ "IPADDR" => opts[:ip_address],
305
+ "PUBLISHER" => opts[:publisher],
306
+ "DESCRIPTION" => "#{opts[:publisher]} offsite transaction"
307
+ }
308
+ emp_body["CHECKSUM"] = checksum "#{external_id}|#{opts[:user_email]}|#{opts[:publisher]}|USD|#{opts[:pay_amount]}|#{emp_amount}|#{emp_body['GAMECODE']}|#{emp_body['COUNTRYCODE']}|#{emp_body['ORDERTRANSACTIONID']}"
309
+
310
+ log.debug "EMP_BODY: #{emp_body}"
311
+ res = do_request("/Payment/rest/services/create/v1/EMP", conn, {:method => :post, :body => emp_body.to_json})
312
+ log.debug "RES: #{res}"
313
+ return res["RESPONSE"] if res["RESPONSE"]
314
+ return res
315
+ end
316
+
317
+ def self.secret_hash(user_id, game_account_id = 0)
318
+ return Digest::SHA1.hexdigest "#{settings[:mystery_key]}#{user_id}#{game_account_id}"
319
+ end
320
+
321
+ def self.checksum(data_string)
322
+ return Digest::SHA1.hexdigest "#{data_string}|#{settings[:api_key]}"
323
+ end
324
+
325
+ class PaymentType
326
+ #{"CURRENCYID"=>0, "SORTNO"=>1, "VIRTUALNAME"=>"Paypal Cedit Card Direct", "PGCODE"=>"CREDITCARD_DIRECT", "CURRENCY"=>"USD", "PAYAMT"=>0, "CASHAMT"=>0, "CASHIDENTIFIER"=>"USD", "REALCASHAMT"=>0, "BONUSCASHAMT"=>0, "PROMOTIONS"=>nil}
327
+ attr_accessor :pg_code, :name, :sortno
328
+ def initialize(data_array)
329
+ @pg_code = data_array["PGCODE"]
330
+ @name = data_array["VIRTUALNAME"]
331
+ @sortno = data_array["SORTNO"]
332
+ end
333
+ end
334
+
335
+ class CurrencyCollection
336
+ attr_accessor :payment_types, :currencies
337
+ # [{"SORTNO"=>1, "VIRTUALNAME"=>"1100 EMP", "PGCODE"=>"PAYPAL", "CURRENCY"=>"USD", "PAYAMT"=>10, "CASHAMT"=>1100, "CASHIDENTIFIER"=>"EMP", "REALCASHAMT"=>1100, "BONUSCASHAMT"=>0}]
338
+ def initialize(data_array, allowed_types = [])
339
+ @payment_types = []
340
+ @currencies = []
341
+ data_array.each do |cur|
342
+ currency = Currency.new(cur)
343
+ if allowed_types.empty? || allowed_types.include?(currency.payment_type)
344
+ @currencies << currency
345
+ @payment_types << currency.payment_type
346
+ end
347
+ end
348
+ @payment_types.uniq!
349
+ @currencies.sort!{|a,b| a.position <=> b.position }
350
+ end
351
+
352
+ def currencies_by_payment_type(type)
353
+ @currencies.select{|c| c.payment_type == type}
354
+ end
355
+
356
+ def to_s
357
+ <<-CURR
358
+ CurrencyCollection #{self.object_id}
359
+ payment_types: #{@payment_types.inspect}
360
+ currencies (@currencies.length): #{@currencies.collect{|x| x.to_s}.inspect}
361
+ CURR
362
+ end
363
+ end
364
+
365
+ class Currency
366
+ attr_accessor :id, :name, :payment_type, :position, :cost, :cost_currency, :purchase_amount_total, :purchase_amount_bonus, :purchase_currency, :purchase_amount
367
+ # {"SORTNO"=>1, "VIRTUALNAME"=>"1100 EMP", "PGCODE"=>"PAYPAL", "CURRENCY"=>"USD", "PAYAMT"=>10, "CASHAMT"=>1100, "CASHIDENTIFIER"=>"EMP", "REALCASHAMT"=>1100, "BONUSCASHAMT"=>0}
368
+ def initialize(curr)
369
+ @id = curr["CURRENCYID"]
370
+ @name = curr["VIRTUALNAME"]
371
+ @payment_type = curr["PGCODE"]
372
+ @position = curr["SORTNO"]
373
+ @cost = curr["PAYAMT"]
374
+ @cost_currency = curr["CURRENCY"]
375
+ @purchase_amount_total = curr["CASHAMT"]
376
+ @purchase_amount = curr["REALCASHAMT"]
377
+ @purchase_amount_bonus = curr["BONUSCASHAMT"]
378
+ @purchase_currency = curr["CASHIDENTIFIER"]
379
+ end
380
+
381
+ def to_s
382
+ <<-CURR
383
+ Currency #{self.object_id}
384
+ \tid: #{@id}
385
+ \tname: #{@name}
386
+ \tpayment_type: #{@payment_type}
387
+ \tposition: #{@position}
388
+ \tcost: #{@cost}
389
+ \tcost_currency: #{@cost_currency}
390
+ \tpurchase_amount: #{@purchase_amount}
391
+ \tpurchase_amount_total: #{@purchase_amount_total}
392
+ \tpurchase_amount_bonus: #{@purchase_amount_bonus}
393
+ \tpurchase_currency: #{@purchase_currency}
394
+ \tbonus: #{bonus}
395
+ CURR
396
+ end
397
+
398
+ def bonus
399
+ return 0 if @purchase_amount_total == 0 || @purchase_amount == 0
400
+ (@purchase_amount_bonus.to_f * 100.0 / @purchase_amount).to_i
401
+ end
402
+ end
403
+
404
+ class ObjectHash < Hash
405
+ def method_missing(method)
406
+ return self[method] if self[method]
407
+ end
408
+ end
409
+
410
+ class Wallet < ObjectHash
411
+ attr_accessor :currencies
412
+
413
+ def initialize(wallet_hash)
414
+ @currencies = []
415
+ self[:error] = false
416
+ @currencies << "EMP"
417
+ self[:EMP] = ObjectHash.new
418
+ self[:EMP][:amount] = wallet_hash["TOTREMAINAMT"]
419
+ theDefault = ObjectHash.new
420
+ theDefault[:amount] = 0
421
+ self.default = theDefault
422
+ end
423
+ end
424
+
425
+ class Offer < ObjectHash
426
+ def initialize(key_values)
427
+ key_values.each do |k,v|
428
+ self[k.to_sym] = v
429
+ end
430
+ if self[:items]
431
+ local_items = []
432
+ self[:items].each {|item| local_items << Item.new(item)}
433
+ self[:items] = local_items
434
+ end
435
+ end
436
+
437
+ def price(currency = "USD")
438
+ prod = self.prices.select{|p| p["currency"] == currency}.first
439
+ return prod["price"].to_f
440
+ end
441
+
442
+ end
443
+
444
+ class Item < ObjectHash
445
+ def initialize(key_values)
446
+ key_values.each do |k,v|
447
+ self[k.to_sym] = v
448
+ end
449
+ if self[:images]
450
+ local_images = []
451
+ self[:images].each {|image| local_images << Image.new(image)}
452
+ self[:images] = local_images
453
+ end
454
+ EME::Billing.items[self[:id]] = self
455
+ end
456
+ end
457
+
458
+ class Campaign < ObjectHash
459
+ def initialize(key_values)
460
+ key_values.each do |k,v|
461
+ self[k.to_sym] = v
462
+ end
463
+ if self[:images]
464
+ local_images = []
465
+ self[:images].each {|image| local_images << Image.new(image)}
466
+ self[:images] = local_images
467
+ end
468
+ self[:items] = [] if self[:items] == nil
469
+ end
470
+ end
471
+
472
+ class Image < ObjectHash
473
+ def initialize(key_values)
474
+ key_values.each do |k,v|
475
+ self[k.to_sym] = v
476
+ end
477
+ end
478
+ end
479
+
480
+ class Transaction < ObjectHash
481
+ def initialize(key_values)
482
+ key_values.each do |k,v|
483
+ self[k.to_sym] = v
484
+ end
485
+ end
486
+
487
+ def date
488
+ Time.at(self[:walletTrxTime].to_i/1000).strftime("%m/%d/%Y")
489
+ end
490
+ end
491
+
492
+ @@items = {}
493
+ def self.items
494
+ @@items
495
+ end
496
+ end