WebApi
Web API(server)
1 介绍
Web API 由DeNA 平台开发团队维护。
主要供游戏的server调用,如有需要也可以供GM tool调用。
我们主要负责的工作包括:
(1)开通ope账号,提供ope工具支持。
(2)配置consumer key,consumer secret,包名,签名等参数。
(3)API的开发维护。
联系方式 mobageplatformdev_cn@dena.jp
2 可部署的区域
区域 | 缩写 | 包括地区 |
---|---|---|
亚太 | ap | 日韩,港澳台,东南亚 |
中国 | cn | 中国大陆 |
北美 | na | 美国,加拿大 |
3 域名及环境
API域名
地区 | 环境 | 域名 |
---|---|---|
亚太 | production | https://lcm-prod-ap-live.mobage.cn/ |
sandbox | https://lcm-sand-ap-live.mobage.cn/ | |
中国 | production | https://lcm-prod-cn-live.mobage.cn/ |
sandbox | https://lcm-sand-cn-live.mobage.cn/ | |
北美 | production | https://lcm-prod-na-live.mobage.cn/ |
sandbox | https://lcm-sand-na-live.mobage.cn/ |
不同的环境下,数据是完全独立的,
所以在切换环境后,客户端的配置文件和服务器调用Web API的域名需要同时修改。
4 授权
4.1 模式
Web API的调用需要授权,规则很简单:)。
Game Server和LCM Server之间使用Oauth2的Client Credentials Grant模式进行授权。
4.2 header加入AUTHORIZATION_TOKEN
每一次请求Web API都需要在http的header里面加入Authorization头,
头的值为Basic AUTHORIZATION_TOKEN(注意Basic和AUTHORIZATION_TOKEN之间有空格)。
4.3 AUTHORIZATION_TOKEN的生成方法
LCM团队会事先线下发行consumer key和consumer secret给项目组,
令a = urlencode(consumerkey)
令b = urlencode(consumersecret)
令c = concat(a, ':', b)
令d = base64(c)
于是d就是AUTHORIZATION_TOKEN。
LCM Server通过解析Basic AUTHORIZATION_TOKEN来验证和确定请求来源的游戏。
5 API接入
5.1 必要接口(重要)***
(1)登录时必须接入auth/validate 或 rest/certifications其中之一对token进行验证,auth/validate 由lcm验证token,rest/certifications获取公钥,自行验证。
(2)支付时必须接入支付回调,lcm异步通知游戏,应对客户端请求丢失等问题,不能保证永远实时,但大部分订单成功后会立即通知,回调失败或请求丢失会重试,服务器宕机恢复后会重发未发送通知。
(3)客户端通知丢失的情形可以借助回调补单,可能的方式:单纯记录人工补,回调接口中直接补,启用脚本定时补,玩家登陆后自动补,进入某个界面自动补,提供按钮玩家点击补等。
(4)有余额模式(简体繁体市场),支付时必须接入bank/spend/:userId ,支付成功后,游戏调用spend接口消费L币,确保spend成功后,给玩家发放所消费L币对应的道具。
(5)无余额模式(欧美日韩市场),支付时必须接入bank/order/check,是游戏来lcm验证订单状态,成功即发道具。
(6)做好唯一性检查,注意不要重复发放道具。
(7)以上必须接入并确保成功,否则会被恶意调用刷账号道具等,
至此,生成授权token并接入以上几个接口就完成了大部分接入工作,其他接口接入都是可选的:)。
5.2 式样约定
(1)一般采用RESTful方式设计,如 bank/spend/:userId 请求时大概这样 bank/spend/12345
(2)一般采用JSON传递数据,但个别接口可能有差别
5.3 API列表
endpoint (点击api快速跳转) | method | 功能简介 | 接入必要性 | 新接口标记 | 支持的地区 | 引入版本 | 备注 |
---|---|---|---|---|---|---|---|
POST | 验证access_token是否合法 | 必须 | all | rc7 | auth/validate 或 rest/certifications 选择一个 | ||
rest/certifications | GET | 下载公钥,游戏自行验证access_token的合法性,具体请看这里 | 必须 | all | rc7 | auth/validate 或 rest/certifications 选择一个 | |
rest/store/userinfo | GET | 通过Lid 查询D商店的绑定信息和注册信息 | 可选 | 新 | all | rc9.1 | 需要接入D商店 |
rest/account/info | POST | 通过渠道id(包括dena id)查询lid信息 | 可选 | 新 | all | rc9.5 | |
bank/order/check | POST | 检查订单状态 | 可选(无余额必须) | all | rc7 | ||
支付回调 | POST | 异步回调成功订单给game server,应对客户端请求丢失等问题 | 必须 | all | rc7 | 需要在ope后台配置回调地址,lcm启动脚本 | |
bank/order/list/:userId | GET | 列出某个用户的订单 | 可选 | all | rc7 | ||
POST | 用于发道具前消费L币(L币是lcm系统中的代币) | 必须(无余额不须) | all | rc8.5 | 支付成功后,必须先消费L币,再发放道具 | ||
bank/refund/:userId | POST | 归还某一笔订单的L币 | 可选 | all | rc8.5 | ||
bank/gift/:userId | POST | 给某个lid发放免费的L币 | 可选 | all | rc8.5 | ||
bank/balance/:userId | GET | 查看某个lid余额 | 可选 | all | rc8.5 | ||
bank/vcbundle/list | GET | 获得商品列表支付档位等信息 | 可选 | 新 | all | rc9.1 | |
notification | POST | 发送远程推送 | 可选 | ap | rc7 | ||
POST | 发送远程推送(大陆) | 可选 | cn | rc7 | |||
notification/:notificationId/status | GET | 检查远程推送的状态 | 可选 | ap | rc7 | ||
notification/:notificationId | DELETE | 删除某个远程推送 | 可选 | ap | rc7 |
6 API请求参数及返回值式样
检查该accessToken是否合法
POST auth/validate
Request
Type | Value |
---|---|
Authorization | Basic #{token} |
Accept | application/json |
Query Parameters |
|
Content-Type | application/json |
Request body | {"accessToken": "sdsoiwelks.fsd9oweifnkdjfs.dfisdvhbsjkdvwoo"} |
|
Response:
status | description |
---|---|
200 | OK |
400 | request body is wrong |
401 | basic token invalid |
429 | API被乱用,too many request |
500 | 内部错误 |
Type | Value |
---|---|
Content-Type | application/json |
Response Body | { "valid": true, "lid":"1000000001" // valid为false时,lid为0 } |
通过Lid 查询D商店的绑定信息和注册信息
GET rest/store/userinfo
e.g.: rest/store/userinfo?lid=1000000000000279
request
Type | Value |
---|---|
Authorization | Basic #{token} |
Accept | application/json |
Query Parameters | lid=1000000000000279 |
Content-Type | application/json |
response
status | description |
---|---|
200 | OK |
400 | request body is wrong |
401 | basic token invalid |
404 | can not find user |
500 | 内部错误 |
Type | Value |
---|---|
Content-Type | application/json |
Response Body | {
|
POST rest/account/info
传入account & storeType 信息,找出对应的 最近登录的 lid 以及相应的用户信息,若没有该 account 的相关信息,则不返回该账号
特殊渠道需要特殊对应:account:渠道账号,storeAccount:数据库最终存储的渠道对应账号
Type | Value | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
Authorization | Basic #{token} | ||||||||||
Accept | application/json | ||||||||||
Query Parameters | storeType=UC // 可不传,不传时默认找对应的官网用户 | ||||||||||
Content-Type | application/json | ||||||||||
Request Body |
| ||||||||||
code & status |
| ||||||||||
Response Body |
|
检查某一笔订单的状态
POST bank/order/check
Request
Type | Value |
---|---|
Authorization | Basic #{token} |
Accept | application/json |
Query Parameters |
|
Content-Type | application/json |
Request body | { "transactionId": "hSkwNL-wQQN-qF-P-oOXhvphg" } |
Response:
status | description |
---|---|
200 | 查询成功 |
401 | invalid token |
400 | bad request |
402 | invalid parameter/insufficient parameter |
404 | transaction not found |
500 | internal server error/db operation failure |
Type | Value |
---|---|
Content-Type | application/json |
Response Body | { "lid": 123456, |
status为200时 Response Body中的code值及说明:
code | description |
---|---|
200 | 订单初始化状态 |
201 | 支付取消 pay canceled |
202 | 支付成功 pay success |
210 | 支付失败 pay failed |
230 | 反查失败 pending 不确定是否到账 |
获取成功的订单列表
GET bank/order/list/:userId
Request
Type | Value |
---|---|
Authorization | Basic token |
Accept | application/json |
Query Parameters | storeType=GOOGLE||APPLE||DEBUG_EDITOR(可选), numLimit=最大记录数(不传默认最大100条) |
Content-Type | application/json |
Response:
status | description |
---|---|
200 | 获取成功 |
401 | invalid token |
400 | bad request |
402 | invalid parameter/insufficient parameter |
500 | internal server error/db operation failure |
Type | Value |
---|---|
Content-Type | application/json |
Response Body | { "code": 200, "size": 2, "orders": [ { "transactionId": "hSkwNL-wQQN-qF-P-oOXhvphg", "lnum": 2, "createtime": "2015-06-09 18:28:24", "updatetime": "2015-06-09 18:30:04", "sku": "cn.g12000128.tier1", "storeType": "APPLE", "memo":"purchase时游戏方传入的memo" }, { "transactionId": "aSkwNL-wQQN-qF-P-oOXhvphh", "lnum": 1, "createtime": "2015-06-05 11:37:31", "updatetime": "2015-06-08 16:19:40", "sku": "cn.g12000128.tier1", "storeType": "APPLE", "memo":"purchase时游戏方传入的memo" } }
|
获取当日和次日的公钥,验证accesstoken的合法性。
GET rest/certifications
Request
Type | Value |
---|---|
Authorization | Basic token |
Accept | application/json |
Query Parameters |
|
Content-Type | application/json |
Request body |
|
Response:
status | description |
---|---|
200 | OK |
400 | request body is wrong |
401 | token错误 |
429 | API被乱用,too many request |
500 | 内部错误 |
Type | Value |
---|---|
Content-Type | application/json |
Response Body | { "<date2>" : "<base64 certification data>" |
获取商品列表支付档位等信息
GET bank/vcbundle/list
Request
Type | Value |
---|---|
Authorization | Basic #{token} |
Accept | application/json |
Query Parameters | storeType=GOOGLE||APPLE||DEBUG_EDITOR(可选) |
Content-Type | application/json |
Request body |
|
Response:
status | description |
---|---|
200 | OK |
400 | bad request |
401 | token错误或者过期 |
402 | invalid parameter |
429 | API被乱用,too many request |
500 | 内部错误 |
Type | Value |
---|---|
Content-Type | application/json |
Response Body | { "bundles": [ {“storeType”:”GOOGLE”,"sku":"op_coin_1_google", "pricetier":3, "lnum":50, "price4show": 0.99, "unit4show": "dollar","bundleDesc":"航海币","itemName":"50航海币"}, {“storeType”:”LCM_A_TW”,"sku":"op_coin_2_google", "pricetier":5, "lnum":150, "price4show": 2.99, "unit4show": "dollar","bundleDesc":"航海币","itemName":"150航海币"}, {“storeType”:”APPLE”,"sku":"op_coin_3_google", "pricetier":7, "lnum":250, "price4show": 4.99, "unit4show": "dollar","bundleDesc":"航海币","itemName":"250航海币"} ] } |
GET /bank/balance/:userId
发起一次远程推送任务
POST /notification
服务器返回一个推送号notificationId,应用方可以用这个推送号,通过查询API来LCM查询推送任务的结果
Request
Type | Value |
---|---|
Authorization | Basic #{accessToken} |
Accept | application/json |
Query Parameters | "storeTypes" (Optional) // 推送对象所在商店类型 "GOOGLE", "APPLE" eg: storeType=GOOGLE // only GOOGLE storeType=GOOGLE,APPLE // GOOGLE and APPLE storeType= // all |
Content-Type | application/json |
Request body | { "message":"abc", // 消息主体,长度最大xxx "userIds":[123124,23123], // 组播和单播时候用, "extras": "life:100", // optional , this is hidden(extras) data of remote notification "icon" : "notification", // optional , this is icon file path of Android "badge": 1, // optional, this is badge number of iOS "sound":"abc.wav", // optional , this is sound file path of both app "groupId":"LETS_PLAY", // optional, this is an ID to group messages by Android "publishedOn":"1234567890" // 这个时间戳值指的是lcm服务器所在地发送的时间。 // 比如lcm服务器在韩国,game_server发过来的请求都会 按照韩国时区加上时间戳来作为发送时间。 // 不设置此值,表示立即推送。如果设置,必须大于当前时间戳并且小于当前时间戳加上86400(一天)的值 } |
|
Response:
status | description |
---|---|
200 | OK |
400 | bad request,参数或者request body不正确 |
401 | auth验证不过 |
404 | 指定的应用不存在 |
500 | 内部错误 |
Type | Value |
---|---|
Content-Type | application/json |
Response Body | { "notificationId": "1234567890", } |
发起一次远程推送任务
POST /notification/notification_cn
该接口供大陆使用,实际推送由百度推送完成,可以到百度后台查询。
Request
Type | Value |
---|---|
Authorization | Basic #{accessToken} |
Accept | application/json |
Query Parameters | "storeTypes" (Optional) // 推送对象所在商店类型 eg: storeType= "GOOGLE", storeType= "APPLE" |
Content-Type | application/json |
Request body | { "pushType":2, //推送类型:1为单播,2为批量单播 } |
|
status | description |
---|---|
200 | OK |
400 | bad request,参数或者request body不正确 |
401 | auth验证不过 |
404 | 指定的应用不存在 |
500 | 内部错误 |
Type | Value |
---|---|
Content-Type | application/json |
Response Body | { "notificationId": "1234567890", } |
查询某个notificationId推送任务的状态
GET /notification/:notificationId/status
Request
Type | Value |
---|---|
Authorization | Basic #{accessToken} |
Accept | application/json |
Query Parameters |
|
Content-Type | application/json |
Request body |
|
|
Response:
status | description |
---|---|
200 | OK |
400 | bad request ,notificationId传入不正确 |
401 | auth验证不过 |
404 | 指定的task不存在 |
500 | 内部错误 |
Type | Value |
---|---|
Content-Type | application/json |
Response Body | { "numSending": 2, "numSent": 3, "numQueued": 4, "numCanceled": 1, "numFailed": 2, } |
取消notificationId对应的推送任务。
DELETE /notification/:notificationId
应用场合: 当设定了一个远程定时推送时,可以在时间到达之前,主动取消这次推送。
Request
Type | Value |
---|---|
Authorization | Basic #{accessToken} |
Accept | application/json |
Query Parameters |
|
Content-Type | application/json |
Request body |
|
|
Response:
status | description |
---|---|
200 | OK |
400 | bad request ,notificationId传入不正确 |
401 | auth验证不过 |
404 | 指定的task不存在 |
500 | 内部错误 |
Type | Value |
---|---|
Content-Type | application/json |
Response Body | { "numSending": 2, "numSent": 3, "numQueued": 4, "numCanceled": 1, "numFailed": 2, } |
消费L币
POST bank/spend/:userId
e.g. bank/spend/12345
Request
Authorization | Basic token |
Accept | application/json |
Query Parameters |
|
Content-Type | application/json |
Request body | { "items":[{"id":"gacha1", "paidValue":100, "freeValue":200,"totalValue":400,"quantity":1},{"id":"gacha2","paidValue":200, "freeValue":0,"totalValue":300,"quantity":3}], “billingId”:"abc123" //可选(最长128个字符) } 注意: 1.物品价格: totalValue是物品的单价,如果未传入totalValue或该值为0,则看paidValue和freeValue,如果有值,则不关心paidValue和freeValue的值 paidValue和freeValue加起来是物品的單價。quantity是物品的數量。L服務器端會計算總共這條request應該扣除多少Paid L幣和Free L幣 paidTotal = sum(paidValue*quantity) 例子中paidTotal = 100*1 + 200*3 即item gacha1 和gacha2 总的paidValue之和 freeTotal = sum(freeValue*quantity) 例子中freeTotal = 200*1 + 0*3 即item gacha1 和gacha2 总的freeValue之和 2.余额判断: 有totalValue时, totalValue>paidBalance+freeBalance则余额不足 没有totalValue时,paidBalance和freeBalance分别判断 paidTotal>paidBalance 或者freeTotal>freeBalance都会是余额不足 3.支付 有totalValue时,先消耗freeBalance, 后消费paidBalance 4.billing id 该字段是可选的,主要用于超时重试,如果不传会直接扣款,如果传了,第一次会扣款,重复传多次不扣款, 第三方可用该字段重试一些失败的消费,不同消费操作传入全局唯一的值,同一笔消费重试时每次传入相同的值。 |
Type | Value |
---|
Response:
200 | 成功 |
400 | bad request |
401 | token错误 |
402 | invalid parameter |
409 | 余额不足 |
404 | 指定的用户不存在Bank信息 |
429 | API被乱用,too many request |
500 | 内部错误 |
status | description |
---|
Content-Type | application/json |
Response Body | { "transactionId":"hSkwNL-wQQN-qF-P-oOXhvphg", // transaction id "paidAmount": 7, "freeAmount":2, "paidBalance": 1030, "freeBalance": 10 } |
Type | Value |
---|
归还L币
POST bank/refund/:userId
Request
Authorization | Basic token |
Accept | application/json |
Query Parameters |
|
Content-Type | application/json |
Request body | { "transactionId":"hSkwNL-wQQN-qF-P-oOXhvphg" } |
Type | Value |
---|
Response:
200 | ok |
400 | bad request |
401 | token错误 |
402 | invalid parameter |
403 | transaction不属于该用户 |
404 | transaction不存在 |
409 | 这个transaction 已经被关闭或者refund了 |
429 | API被乱用,too many request |
500 | 内部错误 |
status | description |
---|
Content-Type | application/json |
Response Body | { "transactionId":"hSkwNL-wQQN-qF-P-oOXhvphg", // transaction id "paidAmount": 700, "freeAmount": 200, "paidBalance": 1730, "freeBalance": 210 } |
Type | Value |
---|
赠送L币
POST bank/gift/:userId
Request
Authorization | Basic token |
Accept | application/json |
Query Parameters |
|
Content-Type | application/json |
Request body | { "amount": 100, // 发放数量 "reason": "level_up" // 发放原因,如升级,市场活动等 } |
Type | Value |
---|
Response:
400 | bad request |
401 | token错误 |
402 | invalid parameter |
500 | 内部错误 |
status | description |
---|
Content-Type | application/json |
Response Body | { "transactionId": "hSkwNL-wQQN-qF-P-oOXhvphg", // transaction id "freeAmount" : 100, // 发放free L币数量 "paidBalance": 1030, // 当前L币余额 "freeBalance": 110 // 当前free L币余额 } |
Type | Value |
---|
获取L币余额
GET /bank/balance/:userId
Request
Authorization | Bearer accessToken |
Accept | application/json |
Query Parameters |
|
Content-Type | application/json |
Request body |
|
Type | Value |
---|
200 | OK |
400 | bad request |
401 | token错误或者过期 |
402 | productId或storeType或lid不存在 |
429 | API被乱用,too many request |
500 | 内部错误 |
status | description |
---|
Content-Type | application/json |
Response Body | { "paidBalance":299, "freeBalance":200 }
Error { "message": "internal server error" } |
Type | Value |
---|
支付结果回调地址
POST url通过ope工具的 产品信息页面 配置
Request
Authorization | null |
Accept | application/json |
Query Parameters |
|
Content-Type | application/json |
Request body | { "lid":1, "transaction_id":"hSkwNL-wQQN-qF-P-oOXhvphg", "store_type":"APPLE", "paid_lnum":100, "free_lnum":0, "sku":"lcm.denachina.pickle.tire01", "status":0 , // 0:成功,1:失败 ,2:预留 "memo":"xxxxx" , // 客户端创建订单时透传的字段,游戏可以填入自己的信息,如订单号等详情 "sign" : "e10adc3949ba59abbe56e057f20f883e" // 签名,内容是 md5( "lid"+"transaction_id"+"store_type"+"paid_lnum"+"free_lnum"+"sku"+"status"+"consumer_secret") 之后的结果,括号里是字符窜拼接 }
sign的例子: 假设consumer_secret = 'aaa' 以上面这个请求为例:sign= md5('1hSkwNL-wQQN-qF-P-oOXhvphgAPPLE1000lcm.denachina.pickle.tire010aaa')
|
Type | Value |
---|
Response:
200 | 成功 |
其他 | 失败 |
status | description |
---|
Content-Type | |
Response Body | Response body: 无 |
Type | Value |
---|
example:
------------------------------------------
Request:
url:http://www.pickle.mobage.com/callback
header:'User-Agent': 'MobageUA/2.0', 'Content-type': 'application/json'
body:
{
"lid":1,
"transaction_id":"hSkwNL-wQQN-qF-P-oOXhvphg",
"store_type":"APPLE",
"paid_lnum":100,
"free_lnum":0,
"sku":"lcm.denachina.pickle.tire01",
"status":0,
"sign" : "e10adc3949ba59abbe56e057f20f883e"
}
Response:
body:无
备注:
游戏服务器如何判断一个客户端的accessToken是否合法呢?
步骤:
1, 客户端把accessToken传送到游戏服务器,accessToken是一个JWT(三段式,以句号分割),它的末尾段是用RS256私钥对header段和payload段的签名的值
2, 游戏服务器用LCM Server提供的证书来校验accessToken的签名值是否正确。
3, 游戏服务器验证签名正确时,就可以认为JWT的header段和payload段是没有被篡改过的
3, 游戏服务器把header和payload还原成JSON
4, 游戏服务器校验payload段的productId和exp的值,保证productId是自己的游戏ID,并且exp的值没有过期。
accessToken的样例:
eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJkZW5hY2hpbmEiLCJpYXQiOjE0NTc1MDQ5MjMsImV4cCI6MTQ1NzUwNTUyMywic3RvcmVUeXBlIjoiQVBQTEUiLCJhcGlQcm92aWRlciI6IkFQUExFIiwiYXBwVmVyc2lvbiI6IjEiLCJwcm9kdWN0SWQiOiJvcCIsInN0YWdlIjoiZmFsc2UiLCJsaWQiOjEwMDAwMDE3LCJzYW5kYm94IjoidHJ1ZSIsImFmZmNvZGUiOiIxLjEiLCJzZGtWZXJzaW9uIjoiMS4wIiwic2Vzc2lvbklkIjoiODQxNjQzMGYtZDcxZS00MmJjLWE4NWUtZDc5MzdiMjc2NzdlIiwicmVnaW9uIjoia3IifQ.1ynRhP64ZKmLOGR-PqCGbEugYTOEjHDvzf9GSXQT8XhyG1vRaezNvpuracf7RLHOedrYulvPydh_l9HjX2zuLt3KFibrdcALuS9rUDFVV6GE-J98jaykpV_smygWLmRoxQ7Cdw_U35zVrQvhgiVHgq2zaokOqWwkHh6U0OyZhB8
蓝色字体部分是header,黑丝字体部门是payload,红色字体部分是对前面两部分的签名
header的JSON还原样例
{
"alg": "RS256"
}
payload的JSON还原样例子
{
"iss":"denachina",
"iat":1457504923,
"exp":1457505523,
"storeType":"APPLE",
"apiProvider":"APPLE",
"appVersion":"1",
"productId":"op",
"stage":"false",
"lid":10000017,
"sandbox":"true",
"affcode":"1.1",
"sdkVersion":"1.0",
"sessionId":"8416430f-d71e-42bc-a85e-d7937b27677e",
"region":"kr"
}