非对战游戏接入文档

1 接入步骤

cp整个接入分为三个大的阶段步骤:

1.技术接入阶段

2.资源包提审阶段

3.测试与发布阶段

1.1 技术接入阶段

该阶段主要的目的是进行技术对接。由我方提供开发版大厅apk,用于cp技术层面接入和连调,需要向我方平台申请AppKey,AppSecret,cp提供产品包名,通过配置开发版apk的配置文件(具体配置方式参见:2.2开发版配置说明),进行游戏功能连调,可采用url方式方便调试。

1.2 资源包提审阶段

开发版apk和游戏功能联调好后,进入提审阶段,将游戏按照文档中要求的方式(具体参见:4游戏资源包打包说明),放入manifest.json ,将游戏资源打包并加入签名,按审核流程要求填写游戏相关信息,并提交加签名后的游戏资源包,由官方人员进行审核通过。

1.3 测试与发布阶段

审核通过后,进入游戏测试阶段,有bug,修改后重新通过提审进行提交。测试通过后,技术层面即可具备上线发布的条件,具体发布方式则由业务产品进行制定和操作。

2 开发调试

2.1 下载调试工具

点击下载

注意:真机调试时须要使用OPPO手机调试

2.2.1 配置说明

配置文件文件名: /sdcard/Android/data/com.nearme.play/files/debug_config.json

注意: /sdcard/Android/data/com.nearme.play/files在初次运行apk才会创建,拷贝进去配置后,需要杀进程重启才生效(启动时加载该配置)。打开apk后,可点击“进入游戏(单机)”按钮,如果配置正确即可拉起游戏。

{
  "pkgName": "",
  "appKey": "",
  "appSecret": "",
  "resourceType": "",
  "gameRes": "",
  "gpkMd5": "",
  "gpkVerify": ""
}
属性 类型 描述
pkgName String 游戏包名,为游戏大厅给游戏分配的指定参数,用来区分校验游戏
appKey String 为游戏大厅给游戏分配的指定参数,用来区分校验游戏
appSecret String 为游戏大厅给游戏分配的指定参数,用来区分校验游戏
resourceType String 资源类型,"1"或"2",填"1"时,表示使用链接的形式加载资源,填"2"时, gameRes表示使用gpk本地包的形式加载资源,游戏包名pkgName固定为com.test.zip.nearme.gamecenter
gameRes String 取决于resourceType,若resourceType为"1",则代表游戏的url。若resourceType为"2",这里填入的是gpk本地包的文件名,gpk的打包方法请参考打包说明
gpkMd5 String " "或游戏包的md5,取决于gpkVerfiy
gpkVerify String "0"不开启本地游戏资源包验证,"1"开启本地资源包验证,为正式环境下验证方式,需要游戏包打签名,并在gpkMd5下填写游戏包的md5

配置文件的以及gpk的存放路径:

/sdcard/Android/data/com.nearme.play/files/debug_config.json

/sdcard/Android/data/com.nearme.play/files/xxxxx.gpk

使用链接方式加载游戏的debug_config.json配置参考示例(demo)

{
  "pkgName": "com.xxxx.game1",
  "appKey": "fasdfxdfe",
  "appSecret": "sdfesdfc",
  "resourceType": "1",
  "gameRes": "http://www.xxxxxx.com/gameres/index.html",
  "gpkMd5": "",
  "gpkVerify": "0"
}

使用gpk本地包方式加载游戏的debug_config.json配置参考示例(demo)

{
  "pkgName": "com.test.zip.nearme.gamecenter",
  "appKey": "fasdfxdfe",
  "appSecret": "sdfesdfc",
  "resourceType": "2",
  "gameRes": "com.test.zip.nearme.gamecenter.gpk",
  "gpkMd5": "",
  "gpkVerify": "0"
}

3 JS SDK

3.1 接入方式

游戏页面引入js sdk文件:

https://activity-cdo.heytapimage.com/cdo-activity/static/201809/30/gamehall/sdk/heytap-sdk-v2.js

引入后会有window.OPPO对象。

3.2 API说明

OPPO.setWebviewOrientation(ori)

设置屏幕横/竖屏。

参数

ori

类型 说明
landscape string 横屏
portrait string 竖屏

示例:

OPPO.setWebviewOrientation('landscape')
OPPO.setWebviewOrientation('portrait')

OPPO.login(object)

通过登录获取oppo会员信息。手机需要已经安装OPPO会员,OPPO手机内置此应用。

参数

object

属性 类型 说明
packageName string 包名,需要修改成开发者在oppo开放平台填写的包名
才能成功调用此方法
callback function 接口调用成功的回调函数
callback(res)回调函数参数

res

属性 类型 说明
userId string 用户id
userName string 用户名
avatar string 用户头像地址
code number 状态码
msg string 请求返回信息
constellation string 用户星座
sex string 性别,"F"代表女、"M"代表男、" "代表未设置
location string 用户所在地
age string 年龄
token string 标识身份的令牌

示例:

OPPO.login({
    packageName: 'your.package.name',//需要修改成开发者在oppo开放平台填写的包名才能成功调用此方法
    callback: function(res) {
        console.log(res)
    }
})

成功时传递给回调函数的res结构如下:

{
    "userId": "2000010000",
    "userName": "User2000010000",
    "avatar": "http://fs-uc-nearme-com-cn.oss-cn-hangzhou.aliyuncs.com/default.png",
    "code": 200,
    "msg": "成功",
}

OPPO.checkPay

检查手机是否支持js调用支付功能。

示例:

OPPO.checkPay(function(res) {
    console.log(res)
});

OPPO.pay(object)

调用支付。支付成功后,OPPO服务端将会调用游戏发货接口通知发货。

参数

object

属性 类型 是否必填 说明
packageName string 必填 开发者在oppo开放平台填写的包名
appName string 必填 游戏名称
appVersion string 必填 游戏版本
appKey string 必填 在oppo开放平台得到的appKey
orderId string 必填 开发者在自己业务系统下的订单号
price number 必填 价格,单位 分
productName string 必填 商品名称
productDesc string 必填 商品描述
callbackUrl string 必填 接收支付平台付款通知的地址,
与oppo android SDK的支付通知处理一致
callback function 必填 接口调用成功的回调函数
callback(res)回调函数参数

res

属性 类型 说明
code string 状态码
msg string 请求返回信息

示例:

OPPO.pay({
    packageName: 'com.testgame.nearme.gamecenter', //开发者在oppo开放平台填写的包名
    appName: '游戏名称',
    appVersion: '1.0',
    appKey: 'TESTOPPOPAY', //在oppo开放平台得到的appKey
    orderId: '20171208001', //开发者在自己业务系统下的订单号
    price: 1, //单位 分
    productName: '商品名称',
    productDesc: '商品描述',
    callbackUrl: 'http: //www.yourdomain.com/notify',
    //接收支付平台付款通知的地址,与oppo android SDK的支付通知处理一致
    callback: function(res) {
        console.log(res)
    }
});

成功时传递给回调函数的res结构如下:

{
    "code":200,
    "msg":""
}

JS回调的成功仅表示成功调起支付界面,用户可以在后续步骤取消付款,用户付款完成的话会有通知发送到callbackUrl,开发者应该根据自己的订单号在自己的业务系统查询实际状态。

OPPO.getAppVersion

返回当前客户端的版本号,number类型

示例:

var version = OPPO.getAppVersion()

OPPO.setLoadingProgress

  • 要求:客户端版本号 >= 1200

参数为0~100的整数,游戏启动后会拉起Loading界面,游戏需要通过setLoadingProgress把自己的加载进度传回界面。

如果游戏加载起来后5秒内没有调用setLoadingProgress,会自动关闭挡板。为了用户体验统一,务必在游戏加载后第一时间调用setLoadingProgress(0),随后再传回真实的加载进度。

示例:

OPPO.setLoadingProgress(20)

OPPO.loadingComplete

  • 要求:客户端版本号 >= 1200

游戏加载完成时调用,关闭挡板界面。

示例:

OPPO.loadingComplete()

OPPO.openRankPage

  • 要求:客户端版本号 >= 1300

打开当前游戏的排行榜界面。

示例:

OPPO.openRankPage()

4 打包说明

将游戏打包为签名包,采用jdk自带的keytool 和 jarsigner工具,为标准的jar打签名包方式。

(1)用keytool 生成keystore,如有keystore则跳过该步骤。

(2)在h5资源包中需增加 manifest.json文件(建议utf-8 无bom格式),与index.html同级目录,内容如下:

字段 说明
package 包唯一标示
name 为外显名称
icon 暂时预留
versionName 外显版本名
versionCode 游戏版本号,必须为整数
minPlatformVersion 对战平台最低能力版本号
{
"package": "com.company.unit",
"name": "游戏1",
"icon": "",
"versionName": "1.0",    
"versionCode": 1,
"minPlatformVersion": 1
}

(3)用zip工具打包,保证manifest.json等资源为根一级。

(4)使用jarsigner 将zip包和keystore打为签名包,扩展名由zip改为gpk,提交审核。

生成keystore示例

keytool -genkey -keystore testkeypair.keystore -alias testkeypair -keyalg RSA -keysize 2048 -sigalg MD5withRSA -validity 99999

jarsigner 打签名示例

jarsigner -verbose -keystore testkeypair.keystore -signedjar signed.zip unsigned.zip -digestalg SHA1 -sigalg MD5withRSA testkeypair

5 支付功能

1536301693826

支付能力

通过sdk的支付接口支付完成后,平台会通过callbackUrl通知发货。

游戏发货接口

提供方

CP提供callbackURL(游戏在下单的时候自己指定的)

实现功能

用户支付成功后,平台会通过这个地址通知CP给用户发货。

调用方

平台

实现要求

CP在收到回调后应及时(200ms内)回写结果给平台,如果调用超时,那么平台会重试,所以CP发货接口必须具有幂等性,避免多次给用户发货。返回结果数据格式:result=arg0&resultMsg=arg1。

arg0:值为“OK”或“FAIL”,两者选其一,该值为必填字段

arg1:arg0为“OK”时该值可以为空字符串,arg0为“FAIL”时建议提供些有意义的信息,便于查找问题,该值非强制字段。

例如:

result=OK&resultMsg=成功

result=FAIL&resultMsg=网络原因发货失败

注:收到开发者回写结果,当result=OK时,表示CP已正常接收到回调,我方不再重复回调。

请求地址

callbackURL

请求方法

POST请求

游戏发货接口参数

参数名 类型 是否必填 长度限制 说明
notifyId string 50 回调通知ID(该值使用系统为这次支付生成的订单号)
partnerOrder string 50 开发者订单号
productName string 50 商品名称
productDesc string 255 商品描述
price string 商品价格
count string 商品数量(默认1)
attach string 255 开发者在下单时候传递的附件描述信息
sign string 签名

验证签名方法&示例

(1) 利用回调的参数生成baseString,方法如下:

private static String getKebiContentString(String url) {
    final String[] strings = url.split("&");
    final Map < String, String > data = new HashMap < String, String > ();
    for (String string: strings) {
        final String[] keyAndValue = string.split("=");
        data.put(keyAndValue[0], keyAndValue[1]);
    }
    StringBuilder sb = new StringBuilder();
    sb.append("notifyId=").append(data.get("notifyId"));
    sb.append("&partnerOrder=").append(data.get("partnerOrder"));
    sb.append("&productName=").append(data.get("productName"));
    sb.append("&productDesc=").append(data.get("productDesc"));
    sb.append("&price=").append(data.get("price"));
    sb.append("&count=").append(data.get("count"));
    sb.append("&attach=").append(data.get("attach"));
    return sb.toString();
}

(2) 对baseString进行验证签名

public static boolean doCheck(String content, String sign, String publicKey) {
    try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = Base64.base64Decode(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature.getInstance("SHA1WithRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes("utf‐8"));
            boolean bverify = signature.verify(Base64.base64Decode(sign));
            return bverify;
        } catch (Exception e) {
            logger.error("验证签名出错.",e);
        }
        return false;
}

公钥:

MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmreYIkPwVovKR8rLHWlFVw7YDfm9uQOJKL89Smt6ypXGVdrAKKl0wNYc3
/jecAoPi2ylChfa2iRu5gunJyNmpWZzlCNRIau55fxGW0XEu553IiprOZcaw5OuYGlf60ga8QT6qToP0/dpiL/ZbmNUO9kUh
osIjEu22uFgR+5cYyQIDAQAB

6 挡板的使用

(游戏必须接入挡板,不接入挡板,将导致游戏加载时,进度条丢失,无法通过测试审核)

非对战游戏加载挡板功能怎么兼容?

通过OPPO.getAppVersion获取到的大厅版本号进行判断,从而进一步做兼容性处理。
若大厅版本号小于1200,则使用自己开发的挡板能力。
若大厅版本号大于等于1200,则使用大厅提供的挡板能力。通过OPPO.setLoadingProgress方法设置加载进度,加载完成后,通过调用OPPO.loadingComplete来关闭挡板。

    var t = 0
    var interval = setInterval(function() {
        // 模拟加载
        t+=5
        console.log('progress is:' + t)

        if (OPPO.getAppVersion() > 1200) {
            OPPO.setLoadingProgress(t)
        } else {
          // 请添加兼容老版本大厅的展示自定义挡板逻辑
        }

        if (t > 100) {        
            clearInterval(interval)
            console.log('loading complete')
            OPPO.loadingComplete()

            // 请添加兼容老版本大厅的关闭自定义挡板逻辑代码
        }
    }, 500)

results matching ""

    No results matching ""