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

前言

现在越来越多的游戏都开始上架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)
    { 
        //支付成功去查询权利
        ASObject info = (ASObject)checkoutCallbackInfo.ClientData;
        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());
    }
}

ASObject.cs

using System;
using System.Runtime.Serialization;
using System.Collections.Generic;

[Serializable]
public class ASObject : Dictionary<string, Object>
{
    private string _typeName;

    public ASObject()
    {
    }

    public ASObject(string typeName)
    {
        _typeName = typeName;
    }

    public ASObject(IDictionary<string, object> dictionary) : base(dictionary)
    {
    }

    public string TypeName
    {
        get { return _typeName; }
        set { _typeName = value; }
    }

    public bool IsTypedObject
    {
        get { return _typeName != null && _typeName != string.Empty; }
    }
}

 

四、结束

总的来说其实支付的流程还算简单 只是因为一些定义 再加上EPIC官方文档给出的内容不够详细 导致支付也是摸索了很久 希望有帮助到大家

 发布

发布到EPIC上面本文就不做过多介绍了 上文前辈已经写的足够详细并且还贴心的制作了 可视化上传工具 给大佬点赞!我这里就贴下地址方便大家下载啦~

EPIC BuildPatch Tool:https://dev.epicgames.com/docs/epic-games-store/publishing-tools/uploading-binaries/bpt-instructions-150

大佬写的可视化工具: GitHub - sunsvip/EpicGameUploader

总结

总的来说EPIC接入还比较少 所以相关资料比较少 上文不一定是最优解 也不一定适用每一个项目 只是分享出来 希望可以帮到大家 如果有问题还请大佬 轻轻喷 当然如果帮助到你的话可以帮我点个免费的赞跟收藏哦~

  • 29
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值