protected override void Awake()
        {
            var playFabAuth   = new PlayFabAuth(_titleId);
            var playFabUser   = new PlayFabUser(playFabAuth);
            var playFabWallet = new PlayFabWallet()
            {
                CurrencyMap = PlayFabWalletCurrencyMap
            };
            var playFabInventory = new PlayFabInventory(_catalogVersion, playFabUser, playFabWallet)
            {
                ItemMap = PlayFabInventoryItemMap
            };
            var playFabStore = new PlayFabStore(_catalogVersion, _storeId)
            {
                PriceMap   = PlayFabStorePriceMap,
                ProductMap = PlayFabStoreProductMap
            };

            var googlePlayAuth  = new GooglePlayAuth();
            var googlePlayStore = new GooglePlayStore(_publicKey)
            {
                ProductMap      = UnityStoreProductMap,
                SubscriptionMap = UnityStoreSubscriptionMap
            };

            var googlePlayPlayFabAuth  = new GooglePlayPlayFabAuth(playFabAuth, googlePlayAuth);
            var googlePlayPlayFabStore = new GooglePlayPlayFabStore(playFabStore, googlePlayStore);

            Auth      = googlePlayPlayFabAuth;
            Inventory = playFabInventory;
            Store     = googlePlayPlayFabStore;
            User      = playFabUser;
            Wallet    = playFabWallet;
        }
        void ILink.Link(string linkKey, Action onComplete, Action onFailure)
        {
            var resultException   = default(Exception);
            var sourceLoginResult = GooglePlayPlayFabAuth.LoginResult;
            var targetLoginResult = default(LoginResult);

            LoginWithCustomID();

            void LoginWithCustomID()
            {
                try
                {
                    PlayFabClientAPI.LoginWithCustomID(
                        new LoginWithCustomIDRequest()
                    {
                        CreateAccount         = false,
                        CustomId              = linkKey,
                        InfoRequestParameters = new GetPlayerCombinedInfoRequestParams()
                        {
                            GetUserAccountInfo = true
                        },
                        TitleId = GooglePlayPlayFabAuth.TitleId
                    },
                        result =>
                    {
                        targetLoginResult = result;
                        GooglePlayPlayFabAuth.LoginResult = result;

                        UnlinkCustomID();
                    },
                        error =>
                    {
                        PlayFabErrorHandler.Process(error);

                        onFailure();
                    });
                }
                catch (Exception exception)
                {
                    ExceptionHandler.Process(exception);

                    onFailure();
                }
            }

            void UnlinkCustomID()
            {
                try
                {
                    PlayFabClientAPI.UnlinkCustomID(
                        new UnlinkCustomIDRequest()
                    {
                        CustomId = linkKey
                    },
                        result =>
                    {
                        CheckLinkKeyExpirationTime();
                    },
                        error =>
                    {
                        PlayFabErrorHandler.Process(error);

                        onFailure();
                    });
                }
                catch (Exception exception)
                {
                    ExceptionHandler.Process(exception);

                    onFailure();
                }
            }

            void CheckLinkKeyExpirationTime()
            {
                try
                {
                    PlayFabClientAPI.GetUserData(
                        new GetUserDataRequest()
                    {
                        Keys = new List <string>()
                        {
                            LinkKeyExpirationTime
                        }
                    },
                        result =>
                    {
                        var data = result.Data;

                        if (data.TryGetValue(LinkKeyExpirationTime, out var record))
                        {
                            var now            = DateTime.Now;
                            var expirationTime = DateTime.Parse(record.Value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);

                            if (now > expirationTime)
                            {
                                resultException = new Exception($"The \"{LinkKey}\" is out of date!");

                                Restore();
                            }
                            else
                            {
                                CheckGoogleId();
                            }
                        }
                        else
                        {
                            resultException = new Exception($"The \"{LinkKeyExpirationTime}\" is not found!");

                            Restore();
                        }
                    },
                        error =>
                    {
                        PlayFabErrorHandler.Process(error);

                        onFailure();
                    });
                }
                catch (Exception exception)
                {
                    ExceptionHandler.Process(exception);

                    onFailure();
                }
            }

            void CheckGoogleId()
            {
                var targetGoogleId = GetGoogleId(targetLoginResult);

                if (targetGoogleId == null)
                {
                    LinkGoogleAccount();
                }
                else
                {
                    var sourceGoogleId = GetGoogleId(sourceLoginResult);

                    resultException = sourceGoogleId == targetGoogleId
                        ? new Exception("The source account already linked to the target account!")
                        : new Exception("The target account already has the linked account!");

                    Restore();
                }
            }

            string GetGoogleId(LoginResult loginResult)
            {
                var infoResultPayload = loginResult?.InfoResultPayload;
                var accountInfo       = infoResultPayload?.AccountInfo;
                var googleInfo        = accountInfo?.GoogleInfo;

                return(googleInfo?.GoogleId);
            }

            void LinkGoogleAccount()
            {
                try
                {
                    GooglePlayPlayFabAuth.GetServerAuthCode(
                        serverAuthCode =>
                    {
                        PlayFabClientAPI.LinkGoogleAccount(
                            new LinkGoogleAccountRequest()
                        {
                            ForceLink      = true,
                            ServerAuthCode = serverAuthCode
                        },
                            result =>
                        {
                            onComplete();
                        },
                            error =>
                        {
                            PlayFabErrorHandler.Process(error);

                            onFailure();
                        });
                    }, onFailure);
                }
                catch (Exception exception)
                {
                    ExceptionHandler.Process(exception);

                    onFailure();
                }
            }

            void Restore()
            {
                Logout();

                void Logout()
                {
                    GooglePlayPlayFabAuth.Logout(Login, ProcessException);
                }

                void Login()
                {
                    GooglePlayPlayFabAuth.Login(ProcessException, ProcessException);
                }
            }

            void ProcessException()
            {
                ExceptionHandler.Process(resultException);

                onFailure();
            }
        }