前言
现在越来越多的游戏都开始上架EPIC(虽然大部分人上E宝只是为了白嫖 包括我自己0-0) 多多少少的开始有接入EPIC的需求 今天就来简述下接入的主要两个流程 登录和支付
分享不易 还请大佬轻喷 如果有帮助到你的话 可以的话帮我点个免费的赞和收藏吧
SDK下载
下载链接如下 具体插件的由来和导入的方式另外的博主前辈已经写的非常清楚了 我这里就不做过多赘述 非常感谢前辈的探索 ღ( ´・ᴗ・` )比心
EOS SDK For Unity地址:GitHub - PlayEveryWare/eos_plugin_for_unity_upm: specially formated git repo for using with the UnityPackageManager
导入流程原文地址:
【Unity】填坑,Unity接入Epic Online Service上架Epic游戏商城_eos unity-CSDN博客
EPIC开发者门户地址:
https://dev.epicgames.com/zh-CN/home
此链接为EPIC创建组织跟产品的链接 你也可以理解为EPIC后台 需要创建完组织后需要创建产品(需缴纳100美刀)才可以进行后续SDK的接入
SDK配置
插件导入后需要进行一下SDK的初始配置 具体步骤如下
在Unity上方菜单栏-Tools-EOS Plugin-EOS Configuration 会打开下面的界面 需要我们填写的是红框里的参数
Product Name:EPIC后台的产品名称(注意: 需要跟你EPIC后台配置的产品名字一模一样 一个字符都不能修改 有空格都不能落下 不然你的链接验证这辈子都都验证不过)
Product Version:产品版本 自行定制标准填写
Product ID:EPIC后台-产品设置-SDK下载与凭证-产品-产品ID
Sandbox ID:跟Product ID在同一页面 沙盒栏里面 向下拉便是(测试用的时候只用先填写Dev的参数即可)
Deployment ID:在Sandbox ID的下面(测试用的时候只用先填写Dev的参数即可)
Client ID:与上文Product ID 在同一页面 需要注意的是 需要在EPIC后台的产品设置里面先创建一个客户端策略后 在创建一个客户端才会有 因为创建客户端需要一个客户端策略才可以创建
参数我推荐使用默认 等到对接测试通过在根据自己游戏的具体需求选择权限
创建完毕后再去到SDK下载与凭证页面就有对应的参数啦
Client Secret:跟Client ID在一起 直接复制出来即可
Encryption Key:填入Client ID和Client Secret后点击Generate按钮自动生成
全部填完要记得点击 Save All Changes 按钮哟
登录
EPIC提供了多种的登录方式 具体的登录流程 上文的前辈已经讲述的非常清楚了 不过代码不是很完整 所以我这边还是分享一下~
代码如下:
public void EpicLogin()
{
//EPIC提供了多种登录方式 不同情况下调用不同的登录方式
#if UNITY_EDITOR
LoginWithPersistentMode();
#else
var token = string.Empty;
string[] commandArgs = System.Environment.GetCommandLineArgs();
foreach (var commandArg in commandArgs)
{
if (commandArg.Contains("AUTH_PASSWORD"))
{
var args = commandArg.Split('=');
if (args.Length >= 2)
{
token = args[1];
}
}
}
EOSManager.Instance.StartLoginWithLoginTypeAndToken(LoginCredentialType.ExchangeCode, null, token, callbackInfo =>
{
if (callbackInfo.ResultCode != Result.Success)
{
//ExchangeCode登录失败
LoginWithPersistentMode();
}
else
{
StartLoginWithLoginTypeAndTokenCallback(callbackInfo);
}
});
#endif
}
public void LoginWithPersistentMode()
{
EOSManager.Instance.StartPersistentLogin((LoginCallbackInfo callbackInfo) =>
{
if (callbackInfo.ResultCode != Result.Success)
{
//没刷新令牌去 AccountPortal模式登录
LoginWithLoginTypeAndToken();
}
else
{
StartLoginWithLoginTypeAndTokenCallback(callbackInfo);
}
});
}
private void LoginWithLoginTypeAndToken()
{
EOSManager.Instance.StartLoginWithLoginTypeAndToken(LoginCredentialType.AccountPortal, ExternalCredentialType.Epic, null, null, loginResult =>
{
EOSManager.Instance.StartConnectLoginWithEpicAccount(loginResult.LocalUserId, (Epic.OnlineServices.Connect.LoginCallbackInfo connectLoginCallbackInfo) =>
{
if (connectLoginCallbackInfo.ResultCode == Result.Success)
{
_productUserId = connectLoginCallbackInfo.LocalUserId;
LoginSuccessCallBack();
}
else if (connectLoginCallbackInfo.ResultCode == Result.InvalidUser)
{
EOSManager.Instance.CreateConnectUserWithContinuanceToken(connectLoginCallbackInfo.ContinuanceToken, (Epic.OnlineServices.Connect.CreateUserCallbackInfo createUserCallbackInfo) =>
{
EOSManager.Instance.StartConnectLoginWithEpicAccount(loginResult.LocalUserId, (Epic.OnlineServices.Connect.LoginCallbackInfo retryConnectLoginCallbackInfo) =>
{
if (retryConnectLoginCallbackInfo.ResultCode == Result.Success)
{
_productUserId = retryConnectLoginCallbackInfo.LocalUserId;
LoginSuccessCallBack();
}
});
});
}
else
{
Debug.Log("LoginWithLoginTypeAndToken failed. [" + connectLoginCallbackInfo.ResultCode + "]");
}
});
});
}
public void StartLoginWithLoginTypeAndTokenCallback(LoginCallbackInfo loginCallbackInfo)
{
if (loginCallbackInfo.ResultCode == Result.Success)
{
StartConnectLoginWithLoginCallbackInfo(loginCallbackInfo);
}
else if (loginCallbackInfo.ResultCode == Result.InvalidUser)
{
EOSManager.Instance.AuthLinkExternalAccountWithContinuanceToken(loginCallbackInfo.ContinuanceToken,
LinkAccountFlags.NoFlags,
(LinkAccountCallbackInfo linkAccountCallbackInfo) =>
{
if (linkAccountCallbackInfo.ResultCode == Result.Success)
{
StartConnectLoginWithLoginCallbackInfo(loginCallbackInfo);
}
else
{
Debug.Log("Error Doing AuthLink with continuance token in. [" + linkAccountCallbackInfo.ResultCode + "]");
}
});
}
else
{
Debug.Log("Error logging in. [" + loginCallbackInfo.ResultCode + "]");
}
}
private void StartConnectLoginWithLoginCallbackInfo(LoginCallbackInfo loginCallbackInfo)
{
EOSManager.Instance.StartConnectLoginWithEpicAccount(loginCallbackInfo.LocalUserId, (Epic.OnlineServices.Connect.LoginCallbackInfo connectLoginCallbackInfo) =>
{
if (connectLoginCallbackInfo.ResultCode == Result.Success)
{
_productUserId = connectLoginCallbackInfo.LocalUserId;
LoginSuccessCallBack();
}
else if (connectLoginCallbackInfo.ResultCode == Result.InvalidUser)
{
EOSManager.Instance.CreateConnectUserWithContinuanceToken(connectLoginCallbackInfo.ContinuanceToken, (Epic.OnlineServices.Connect.CreateUserCallbackInfo createUserCallbackInfo) =>
{
EOSManager.Instance.StartConnectLoginWithEpicAccount(loginCallbackInfo.LocalUserId, (Epic.OnlineServices.Connect.LoginCallbackInfo retryConnectLoginCallbackInfo) =>
{
if (retryConnectLoginCallbackInfo.ResultCode == Result.Success)
{
_productUserId = retryConnectLoginCallbackInfo.LocalUserId;
LoginSuccessCallBack();
}
});
});
}
});
}
private void LoginSuccessCallBack()
{
//EPIC登录成功 通知U3D登录成功 做登录后的操作
}
支付
支付这个对接让我研究了许久 最主要的原因就是EPIC的官方文档写的太屎了 一些名词叫起来也是各种混淆 官方给的商城实例也只是去查询后台配置的商品 然后进行结算 更像是一次性买断 类似DLC一样 虽然还算是完整 但是如果游戏内有直充 这种情况 官方案例就没有表现
下面我主要讲的是关于游戏内有直充的情况下 当然DLC这种也适用 其本质上只是后台配置时候类型的区别
一、创建商品
去到EPIC后台 - EPIC游戏商城 - 商品 页面 点击 创建商品(ps:默认会有一个 那个是游戏本体)
商品名字随意 但注意如果是直充类型的话要选择消耗品
价格可以设置成免费 或者创建一个方案将 中国区的价格设置为0即可
创建完成后往下拉 会有两个ID 这两个ID是我们支付所需的
二、后端通过API去查询商品
后端通过上述登录代码后 会返回一长串的token 接着后端在通过EPIC官方文档的Web API 去EPIC服务器上Get到我们的商品数据 本质上前端其实也可以去拿到的 但是为了支付的安全性肯定是要通过我们自己后端的验证的
电子商务Web API地址:https://dev.epicgames.com/docs/zh-Hans/web-api-ref/ecom-web-apis
Get的网址格式如下:
https://api.epicgames.dev/epic/ecom/v1/identities/{identityId}/namespaces/{sandboxID}/offers/
{identityId}:为玩家的localuserId 登录的时候后端会拿到 (前端要拿的话可以通过EOSManager.Instance.GetLocalUserId() 获取到)
{sandboxID}:就是我们在SDK配置里配置的Sandbox ID
参数为登录获取的token 切记toekn前要加Bearer 如下图官方案例所示 告诉EPIC服务器 不然Get一定会返回错误代码1032
响应成功的话 后端会受到一段JSON代码 仔细观察就会发现
elements-id:便是我们后台配置的商品ID
elements-items-id:便是我们的后台配置的受众项ID
我们要做的是在游戏里点对应的直充时 通过数据表也好什么方式也好 让对应的直冲价格可以唯一对应上EPIC的这两个ID
前端点击充值时 请求后端返回这两个ID给我们 后端也可以多传个订单ID来 确保唯一性 漏单补单的时候也会轻松很多 具体就不细聊了 总之就是我们前端拿到这两个EPIC后台配置的ID即可下一步
三、代码部分
具体的支付流程就是
前端通知后端充值-后端告诉前端商品信息-前端通过商品信息拉起充值-充值成功后去EPIC服务器上查询玩家是否有购买过这个权利-有的话通知后端支付成功-后端游戏内发货通知前端发货成功-前端去EPIC服务器上兑换掉权利-支付结束
乍一看挺绕的 其实慢慢读下来还是挺绕的
我们慢慢来!
PS:本文演示的主要是一对一订单的操作 即一次只购买一个商品的情况 EPIC是支持一次性购买多个商品的 这个情况大家就自行研究一下吧 流程都一样只是在兑换的时候操作不一样
orderId:后端给的订单号(看具体需求要不要 主要是用于后端自己验证的)
offerId:后端给的商品ID(本质就是我们EPIC后台配置生成的商品ID)
entitlementId:后端给的受众项ID(本质就是我们EPIC后台配置生成的受众项ID)
public void CheckOutOverlay(string orderId,string offerId,string entitlementId)
{
Debug.Log("-------开始唤起支付流程!");
//购买结构体 CheckoutOptions
//CheckoutOptions.LocalUserId 用户UserId
//CheckoutOptions.Entries 存放要购买的商品列表
CheckoutEntry checkoutEntry = new CheckoutEntry();
checkoutEntry.OfferId = offerId;
CheckoutOptions checkoutOptions = new CheckoutOptions();
checkoutOptions.LocalUserId = EOSManager.Instance.GetLocalUserId();
checkoutOptions.Entries = new CheckoutEntry[] { checkoutEntry };
//其实主要用到的只有entitlement_id 但是要把orderId 跟offerId传回给后端做对比 下面代码就跳过这部分了
ASObject args = new ASObject();
args["order_id"] = orderId;
args["offer_id"] = offerId;
args["entitlement_id"] = entitlementId;
//调用支付接口
EOSManager.Instance.GetEOSEcomInterface().Checkout(ref checkoutOptions, args, OnCheckout);
Debug.Log("-----结束支付流程!");
}
public void OnCheckout(ref CheckoutCallbackInfo checkoutCallbackInfo)
{
Debug.Log($"支付结果: {checkoutCallbackInfo.ResultCode}");
if (checkoutCallbackInfo.ResultCode == Result.Success)
{
//支付成功去查询权利
EntitlementsOffers(info["entitlement_id"].ToString());
}
}
支付成功后 我们前端通过EOSManager.Instance.GetEOSEcomInterface().QueryEntitlements()去访问EPIC服务器 查看我们是否已经购买成功
public void EntitlementsOffers(string entitlementId)
{
//通过受众项ID获取权利列表
//QueryEntitlementsOptions.LocalUserId 用户UserId
//QueryEntitlementsOptions.EntitlementNames 需要查询权利的受众项列表
QueryEntitlementsOptions queryEntitlementsOptions = new QueryEntitlementsOptions();
queryEntitlementsOptions.LocalUserId = EOSManager.Instance.GetLocalUserId();
Utf8String[] entitlementInfo = {entitlementId};
queryEntitlementsOptions.EntitlementNames = entitlementInfo;
EOSManager.Instance.GetEOSEcomInterface().QueryEntitlements(ref queryEntitlementsOptions, null, OnQueryEntitlements);
}
private void OnQueryEntitlements(ref QueryEntitlementsCallbackInfo queryEntitlementCallbackInfo)
{
Debug.Log("QueryEntitlements callback. ResultCode=" + queryEntitlementCallbackInfo.ResultCode);
if (queryEntitlementCallbackInfo.ResultCode == Result.Success)
{
//需要兑换的权利
var entitlementsCountOptions = new GetEntitlementsCountOptions();
entitlementsCountOptions.LocalUserId = EOSManager.Instance.GetLocalUserId();
//获取权利数量 打印出阿
var entitlementCount = EOSManager.Instance.GetEOSEcomInterface().GetEntitlementsCount(ref entitlementsCountOptions);
Debug.Log(string.Format("QueryEntitlements 共查询到 {0} 个权利.", entitlementCount));
for (int offerIndex = 0; offerIndex < entitlementCount; ++offerIndex)
{
//获取对应的权利的结构体 Entitlement
//Entitlement.EntitlementName 权利名称
//Entitlement.EntitlementId 权利ID
//Entitlement.CatalogItemId 受众项ID
//Entitlement.serverIndex 权利在EPIC服务器上对应的索引
var copyEntitlementByIdOptions = new CopyEntitlementByIndexOptions();
copyEntitlementByIdOptions.LocalUserId = EOSManager.Instance.GetLocalUserId();
copyEntitlementByIdOptions.EntitlementIndex = (uint)offerIndex;
var copyOfferByIndexResult = EOSManager.Instance.GetEOSEcomInterface().CopyEntitlementByIndex(ref copyEntitlementByIdOptions, out var catalogEntitlement);
if(copyOfferByIndexResult == Result.Success)
{
Debug.Log("权利ID:" + catalogEntitlement.Value.EntitlementId);
//string id = catalogEntitlement.Value.EntitlementId.ToString();
//这里进行你的逻辑 通知后端支付成功
break;
}
}
}
}
演示代码并没有添加通知后端支付成功的代码 请结合自身项目调整
等待后端发货成功后通知我们 我们便可以兑换权利(别问我为啥叫权利 因为EPIC就这么叫)
entitlementId:上面代码注释里的id
//兑换权利
public void RedeemEntitlements(Utf8String entitlementId)
{
Debug.Log("开始兑换权利!");
Debug.Log("兑换权利的right_id:" + entitlementId);
Utf8String[] entitlementIdGroup = {entitlementId};
RedeemEntitlementsOptions redeemEntitlementsOptions = new RedeemEntitlementsOptions();
redeemEntitlementsOptions.LocalUserId = EOSManager.Instance.GetLocalUserId();
redeemEntitlementsOptions.EntitlementIds = entitlementIdGroup;
EOSManager.Instance.GetEOSEcomInterface().RedeemEntitlements(ref redeemEntitlementsOptions, null, OnEntitlements);
}
//权利兑换回调
private void OnEntitlements(ref RedeemEntitlementsCallbackInfo queryOffersCallbackInfo)
{
if (queryOffersCallbackInfo.ResultCode == Result.Success)
{
Debug.Log("兑换成功!");
Debug.Log("RedeemedEntitlementIdsCount:" + queryOffersCallbackInfo.RedeemedEntitlementIdsCount.ToString());
}
}
四、结束
总的来说其实支付的流程还算简单 只是因为一些定义 再加上EPIC官方文档给出的内容不够详细 导致支付也是摸索了很久 希望有帮助到大家
发布
发布到EPIC上面本文就不做过多介绍了 上文前辈已经写的足够详细并且还贴心的制作了 可视化上传工具 给大佬点赞!我这里就贴下地址方便大家下载啦~
EPIC BuildPatch Tool:https://dev.epicgames.com/docs/epic-games-store/publishing-tools/uploading-binaries/bpt-instructions-150
大佬写的可视化工具: GitHub - sunsvip/EpicGameUploader
总结
总的来说EPIC接入还比较少 所以相关资料比较少 上文不一定是最优解 也不一定适用每一个项目 只是分享出来 希望可以帮到大家 如果有问题还请大佬 轻轻喷 当然如果帮助到你的话可以帮我点个免费的赞跟收藏哦~
标签:EPIC,Service,接入,Instance,EOSManager,ResultCode,Epic,LocalUserId,ID From: https://blog.csdn.net/2401_83152164/article/details/139739861