eme_services_client 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/eme/admin_user.rb +132 -0
- data/lib/eme/admin_user/required_login.rb +44 -0
- data/lib/eme/ams.rb +29 -0
- data/lib/eme/auth.rb +24 -0
- data/lib/eme/billing.rb +496 -0
- data/lib/eme/billing/workers/refresh_wallet_worker.rb +23 -0
- data/lib/eme/bt.rb +21 -0
- data/lib/eme/lootbox.rb +27 -0
- data/lib/eme/shop.rb +15 -0
- data/lib/eme/sso.rb +41 -0
- data/lib/eme/sso/token_reader.rb +74 -0
- data/lib/eme/subscription.rb +246 -0
- data/lib/eme/tera_game_data.rb +67 -0
- data/lib/eme/tera_server.rb +13 -0
- data/lib/eme/tera_server/server.rb +47 -0
- data/lib/eme_services_client.rb +19 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -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
|
data/lib/eme/ams.rb
ADDED
@@ -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
|
data/lib/eme/auth.rb
ADDED
@@ -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
|
data/lib/eme/billing.rb
ADDED
@@ -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
|