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