首页 > 其他分享 >Unity接入Epic Online Service上架Epic游戏商城 登录和支付SDK配置与接入

Unity接入Epic Online Service上架Epic游戏商城 登录和支付SDK配置与接入

时间:2024-06-17 17:03:10浏览次数:12  
标签:EPIC Service 接入 Instance EOSManager ResultCode Epic LocalUserId ID

前言

现在越来越多的游戏都开始上架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 IDClient 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

相关文章

  • django 接入OIDC认证登录(django admin后台使用OIDC 或github账号登录) django 使用p
    参考文档模块文档:https://python-social-auth.readthedocs.io/en/latest/接入github账号登录参考:https://blog.csdn.net/yannanxiu/article/details/112622781;测试项目地址:https://github.com/AngelLiang/django-social-auth-demo/tree/main--创建githubOAuth应用:https:/......
  • springboot与flowable(3):启动、审批、各个Service服务
    一、启动流程        流程定义与实例的关系类似于Java的类与对象,通过定义的id创建流程实例,编写测试代码:packageorg.example.flowabledemo2;importorg.flowable.engine.RuntimeService;importorg.flowable.engine.runtime.ProcessInstance;importorg.ju......
  • Android 使用绑定式调用service中的方法
    在Android中,Service有两种启动方式:startService()和bindService()。startService()启动Service时,Service会被创建并且调用onCreate()和onStartcommand()方法。Service会一直保持运行状态,直到调用stopService()或者stopSelf()方法。bindService()启动Service时,Service会被创建......
  • 为企业提供了跨地域、跨网络环境的一键接入云体验
    随着云计算技术的不断发展,企业对云服务的依赖越来越深。然而,面对多云环境、跨地域部署和复杂网络环境等挑战,如何实现高效、稳定、安全的云连接,成为众多企业关注的焦点。联通云联网以其丰富的全球资源、灵活统一的管理、强大的安全性和高可靠性,为企业提供了跨地域、跨网络环境的一......
  • k8s学习--Traffic Shifting 流量接入
    文章目录应用环境一、Argorollouts安装1.在Kubernetes集群中安装argorollouts2.安装argorollouts的kubectlplugin3.Argo-RolloutsDashboard二、负载均衡器metallb部署1.修改kube-proxy代理模式2.metallb部署3.IP地址池准备4.开启二层通告三、TrafficShifting......
  • Java与服务网格(Service Mesh):构建高效微服务架构
    在微服务架构成为企业开发标准的今天,如何有效地管理众多微服务之间复杂的通信成为了一个挑战。服务网格作为一种解决方案,它通过提供一个专门的基础设施层来处理服务间通信,从而使得应用开发更加专注于业务逻辑而非通信细节。本文将介绍服务网格的基本概念,探讨其在Java环境中的应......
  • 核心(Hutool-core)工具类(SPI加载工具-ServiceLoaderUtil)
    介绍SPI(ServiceProviderInterface),是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。更多介绍见:https://www.jianshu.com/p/3a3edbcd8f24使用定义一个接口:packagecn.hutool.test.spi;publicinterfaceSPIService......
  • CAS单点登录:获取请求中的Service(九)
    1.需求在cas-server处理客户端请求的过程中,偶尔需要这个客户端的信息,这里我们就需要获取该次请求中的Service2.引入依赖<dependency><groupId>org.apereo.cas</groupId><artifactId>cas-server-core-web-api</artifactId><version>${cas.version}</version>......
  • 车载android开发 carservice(一)
    车载android开发carservice是什么?车载Android开发中的CarService是一个专门为汽车环境设计的系统服务。CarService通常是AndroidAutomotiveOS的一部分,提供一系列API和框架,允许开发人员构建与汽车相关的应用和服务。以下是CarService的一些主要功能和作用:车辆数据访问:C......
  • 实时api接入指南|1688商品详情实时数据接口(1688.item_get)图片、库存、规格、销量等数
    接入1688商品详情实时数据接口(1688.item_get),涉及到图片、库存、规格、销量等重要信息的获取,这些数据对于电商平台来说至关重要,可用于商品展示、分析市场趋势、优化库存管理等。下面将详细讨论如何接入此API接口,并有效利用返回的数据:注册与创建应用账号注册:需要进行账号注册......