55#include " NativePlatformPurchaseSubsystem.h"
66
77#include " Algo/Transform.h"
8+ #include " Interfaces/OnlineStoreInterfaceV2.h"
9+ #include " Interfaces/OnlineIdentityInterface.h"
810#include " OnlineStoreInterfaceV2AccelByte.h"
911#include " OnlineEntitlementsInterfaceAccelByte.h"
1012#include " OnlinePurchaseInterfaceAccelByte.h"
13+ #include " OnlineIdentityInterfaceAccelByte.h"
1114#include " Monetization/InGameStoreEssentials/UI/ShopWidget.h"
1215#include " Monetization/InGameStoreEssentials/InGameStoreEssentialsModel.h"
1316
@@ -19,6 +22,12 @@ void UNativePlatformPurchaseSubsystem::Initialize(FSubsystemCollectionBase& Coll
1922
2023 const IOnlineSubsystem* Subsystem = Online::GetSubsystem (GetWorld ());
2124 ensure (Subsystem);
25+
26+ const IOnlineIdentityPtr IdentityPtr = Subsystem->GetIdentityInterface ();
27+ ensure (IdentityPtr);
28+ IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(IdentityPtr);
29+ ensure (IdentityInterface);
30+
2231 const IOnlineStoreV2Ptr StorePtr = Subsystem->GetStoreV2Interface ();
2332 ensure (StorePtr);
2433 StoreInterface = StaticCastSharedPtr<FOnlineStoreV2AccelByte>(StorePtr);
@@ -149,6 +158,9 @@ void UNativePlatformPurchaseSubsystem::CheckoutItem(const APlayerController* Own
149158 * This is necessary because the AccelByte OSS does not natively support this setup.*/
150159 CheckoutItemWithGooglePlay (OwningPlayer, CheckoutRequest, StoreItemData->GetIsConsumable ());
151160 return ;
161+ #elif PLATFORM_EOS
162+ CheckoutItemWithEpic (OwningPlayer, CheckoutRequest);
163+ return ;
152164#endif
153165
154166 const FAccelByteModelsEntitlementSyncBase EntitlementSyncBase = { *NativeProductId };
@@ -212,6 +224,15 @@ void UNativePlatformPurchaseSubsystem::OnQueryItemMappingComplete(
212224
213225#if PLATFORM_STEAM
214226 OnQueryItemMappingCompleted.ExecuteIfBound (GetSteamItemPricing (ItemMappings));
227+ #elif PLATFORM_EOS
228+ if (!GetItemPrices ().IsEmpty ())
229+ {
230+ OnQueryItemMappingCompleted.ExecuteIfBound (GetItemPrices ());
231+ }
232+ else
233+ {
234+ QueryEpicItems (UserId, ItemMappings);
235+ }
215236#elif PLATFORM_ANDROID
216237 // Query native item information from Google Play Games Services (GPGS) if not yet.
217238 if (!GetItemPrices ().IsEmpty ())
@@ -436,6 +457,179 @@ void UNativePlatformPurchaseSubsystem::HandleSteamPurchaseOverlay(GameOverlayAct
436457#endif
437458#pragma endregion
438459
460+ #pragma region "Epic Games Store"
461+ #if PLATFORM_EOS
462+ void UNativePlatformPurchaseSubsystem::QueryEpicItems (const FUniqueNetIdPtr UserId, const TArray<TSharedRef<FAccelByteModelsItemMapping>>& ItemMappings)
463+ {
464+ const IOnlineSubsystem* PlatformSubsystem = IOnlineSubsystem::GetByPlatform ();
465+ if (!PlatformSubsystem)
466+ {
467+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" Failed to query native pricing. The native platform subsystem is invalid." ));
468+ return ;
469+ }
470+
471+ IOnlineStoreV2Ptr PlatformStore = PlatformSubsystem->GetStoreV2Interface ();
472+ if (!PlatformStore)
473+ {
474+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" Failed to query native pricing. The native store v2 interface is invalid." ));
475+ return ;
476+ }
477+
478+ IOnlineIdentityPtr PlatformIdentity = PlatformSubsystem->GetIdentityInterface ();
479+ if (!PlatformIdentity)
480+ {
481+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" Failed to query native pricing. The native identity interface is invalid." ));
482+ return ;
483+ }
484+
485+ TArray<FString> OfferIds;
486+ const UEpicGamesStoreItemConfig* ItemConfigs = GetDefault<UEpicGamesStoreItemConfig>();
487+ if (!ItemConfigs)
488+ {
489+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" Failed to query native pricing. Unable to load UEpicGamesStoreItemConfig." ));
490+ return ;
491+ }
492+
493+ for (const TSharedRef<FAccelByteModelsItemMapping>& OfferId : ItemMappings)
494+ {
495+ if (OfferId->Platform == EAccelBytePlatformMapping::EPIC_GAMES)
496+ {
497+ OfferIds.Add (ItemConfigs->AudienceOffers [OfferId->PlatformProductId ]);
498+ }
499+ }
500+
501+ FPlatformUserId PlatformUserId = IdentityInterface->GetPlatformUserIdFromUniqueNetId (UserId.ToSharedRef ().Get ());
502+ ensure (PlatformUserId.IsValid ());
503+ const int32 LocalUserNum = IdentityInterface->GetLocalUserNumFromPlatformUserId (PlatformUserId);
504+
505+ FUniqueNetIdPtr PlatformUniqueId = PlatformIdentity->GetUniquePlayerId (LocalUserNum);
506+ ensure (PlatformUniqueId.IsValid ());
507+
508+ PlatformStore->QueryOffersById (*PlatformUniqueId, OfferIds, FOnQueryOnlineStoreOffersComplete::CreateUObject (this , &ThisClass::OnEpicQueryOfferCompleted));
509+ }
510+
511+ void UNativePlatformPurchaseSubsystem::OnEpicQueryOfferCompleted (bool bWasSuccessful, const TArray<FUniqueOfferId>& OfferIds, const FString& Error)
512+ {
513+ if (!bWasSuccessful)
514+ {
515+ OnQueryItemMappingCompleted.ExecuteIfBound ({});
516+ return ;
517+ }
518+
519+ const IOnlineSubsystem* PlatformSubsystem = IOnlineSubsystem::GetByPlatform ();
520+ if (!PlatformSubsystem)
521+ {
522+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" Failed to query native pricing. The native platform subsystem is invalid." ));
523+ return ;
524+ }
525+
526+ IOnlineStoreV2Ptr PlatformStore = PlatformSubsystem->GetStoreV2Interface ();
527+ if (!PlatformStore)
528+ {
529+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" Failed to query native pricing. The native store v2 interface is invalid." ));
530+ return ;
531+ }
532+
533+ const UEpicGamesStoreItemConfig* ItemConfigs = GetDefault<UEpicGamesStoreItemConfig>();
534+ if (!ItemConfigs)
535+ {
536+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" Failed to query native pricing. Unable to load UEpicGamesStoreItemConfig." ));
537+ return ;
538+ }
539+
540+ TArray<FOnlineStoreOfferRef> Offers;
541+ PlatformStore->GetOffers (Offers);
542+
543+ TMap<FString, FString> AbItemMapping;
544+ for (const TSharedRef<FAccelByteModelsItemMapping>& OfferId : GetItemMapping ())
545+ {
546+ if (OfferId->Platform == EAccelBytePlatformMapping::EPIC_GAMES)
547+ {
548+ if (const FString* EpicOfferId = ItemConfigs->AudienceOffers .Find (OfferId->PlatformProductId ))
549+ {
550+ AbItemMapping.Add (**EpicOfferId, OfferId->ItemIdentity );
551+ }
552+ }
553+ }
554+
555+ ItemPriceMap.Empty ();
556+ for (const FOnlineStoreOfferRef& Offer : Offers)
557+ {
558+ const FString& PlatformProductId = Offer->OfferId ;
559+ if (!PlatformProductId.IsEmpty () && AbItemMapping.Contains (PlatformProductId))
560+ {
561+ ItemPriceMap.Add (AbItemMapping[PlatformProductId], {PlatformProductId, (uint64)Offer->NumericPrice });
562+ FNativePlatformPurchaseUtils::RegionalCurrencyCode = Offer->CurrencyCode ;
563+ }
564+ }
565+
566+ OnQueryItemMappingCompleted.ExecuteIfBound (ItemPriceMap);
567+ }
568+
569+ void UNativePlatformPurchaseSubsystem::CheckoutItemWithEpic (const APlayerController* OwningPlayer, const FPurchaseCheckoutRequest CheckoutRequest)
570+ {
571+ FString ErrorMessage = TEXT (" " );
572+
573+ const IOnlineSubsystem* PlatformSubsystem = IOnlineSubsystem::GetByPlatform ();
574+ if (!PlatformSubsystem)
575+ {
576+ ErrorMessage = TEXT (" Failed to checkout item using Epic Online Service. Identity interface is invalid." );
577+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" %s" ), *ErrorMessage);
578+ OnSyncPurchaseCompleteDelegates.ExecuteIfBound (false , ErrorMessage);
579+ return ;
580+ }
581+
582+ IOnlinePurchasePtr PlatformPurchase = PlatformSubsystem->GetPurchaseInterface ();
583+ if (!PlatformPurchase)
584+ {
585+ ErrorMessage = TEXT (" Failed to checkout item using Epic Online Service. Purchase interface is invalid." );
586+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" %s" ), *ErrorMessage);
587+ OnSyncPurchaseCompleteDelegates.ExecuteIfBound (false , ErrorMessage);
588+ return ;
589+ }
590+
591+ const ULocalPlayer* LocalPlayer = OwningPlayer->GetLocalPlayer ();
592+ if (!LocalPlayer)
593+ {
594+ ErrorMessage = TEXT (" Failed to checkout item using Epic Online Services. Local Player is invalid." );
595+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" %s" ), *ErrorMessage);
596+ OnSyncPurchaseCompleteDelegates.ExecuteIfBound (false , ErrorMessage);
597+ return ;
598+ }
599+
600+ const int32 LocalUserNum = LocalPlayer->GetControllerId ();
601+ IOnlineIdentityPtr PlatformIdentity = PlatformSubsystem->GetIdentityInterface ();
602+ const FUniqueNetIdPtr UserId = PlatformIdentity->GetUniquePlayerId (LocalUserNum);
603+ if (!UserId)
604+ {
605+ ErrorMessage = TEXT (" Failed to checkout item using Epic Online Services. User ID is invalid." );
606+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" %s" ), *ErrorMessage);
607+ OnSyncPurchaseCompleteDelegates.ExecuteIfBound (false , ErrorMessage);
608+ return ;
609+ }
610+
611+ PlatformPurchase->Checkout (*UserId, CheckoutRequest,
612+ FOnPurchaseCheckoutComplete::CreateWeakLambda (this ,
613+ [this , LocalUserNum, PlatformIdentity]
614+ (const FOnlineError& Result, const TSharedRef<FPurchaseReceipt>& Receipt)
615+ {
616+ if (!Result.bSucceeded )
617+ {
618+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Warning, TEXT (" Failed to purchase item using Epic Online Services. Error: %s" ), *Result.ErrorMessage .ToString ());
619+ OnSyncPurchaseCompleteDelegates.ExecuteIfBound (false , Result.ErrorMessage .ToString ());
620+ return ;
621+ }
622+
623+ // Sync Epic Online Services purchase with AccelByte.
624+ UE_LOG_NATIVE_PLATFORM_PURCHASE (Log, TEXT (" Success to purchase item using Epic Online Services. Synching purchase to AccelByte" ));
625+ PurchaseSyncState = EPurchaseState::SyncInProgress;
626+ EntitlementInterface->SyncPlatformPurchase (LocalUserNum, FAccelByteModelsEntitlementSyncBase{}, OnSyncPurchaseCompleteDelegates);
627+ })
628+ );
629+ }
630+ #endif
631+ #pragma endregion
632+
439633#pragma region "Google Play"
440634#if PLATFORM_ANDROID
441635void UNativePlatformPurchaseSubsystem::CheckoutItemWithGooglePlay (
0 commit comments