eme_services_client 0.0.11

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.
@@ -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