メインコンテンツまでスキップ

Implement Subsystem - Store Item Purchase - (Unreal Engine module)

Last updated on July 28, 2025

Unwrap the subsystem

Byte Wars uses the Game Instance Subsystem called StoreItemPurchaseSubsystem to wrap the AccelByte Gaming Services (AGS) Online Subsystem (OSS). This subsystem uses FOnlinePurchaseAccelByte, which is AccelByte's implementation of Unreal Engine’s IOnlinePurchase interface. In this tutorial, you'll be working with a starter version of the subsystem, allowing you to implement the required functions from scratch.

What's in the Starter Pack

To follow along with this tutorial, a starter subsystem class named StoreItemPurchaseSubsystem_Starter has been prepared. You can find it in the Resources section. It includes the following files:

  • Header file: Source/AccelByteWars/TutorialModules/Monetization/StoreItemPurchase/StoreItemPurchaseSubsystem_Starter.h
  • CPP file: Source/AccelByteWars/TutorialModules/Monetization/StoreItemPurchase/StoreItemPurchaseSubsystem_Starter.cpp

The StoreItemPurchaseSubsystem_Starter class contains several helpful components:

  • Declaration and initialization of the AGS OSS FOnlinePurchaseAccelByte interface, which provides access to AGS Software Development Kit (SDK) features.
private:
FOnlinePurchaseAccelBytePtr PurchaseInterface;
void UStoreItemPurchaseSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
const IOnlinePurchasePtr PurchasePtr = Online::GetSubsystem(GetWorld())->GetPurchaseInterface();
ensure(PurchasePtr);

PurchaseInterface = StaticCastSharedPtr<FOnlinePurchaseAccelByte>(PurchasePtr);
ensure(PurchaseInterface);
// ...
}
  • Helper function to retrieve the unique Net ID from a Player Controller. This is necessary because widgets, which are the primary users of this subsystem, use Player Controllers to reference players, while the OSS interface requires a unique Net ID to identify the user.
private:
// ...
FUniqueNetIdPtr GetLocalPlayerUniqueNetId(const APlayerController* PlayerController) const;

Additionally, there is a model file located at Source/AccelByteWars/TutorialModules/Monetization/StoreItemPurchase/StoreItemPurchaseModel.h that defines the delegate used to handle backend responses.

DECLARE_MULTICAST_DELEGATE_OneParam(FOnOrderComplete, const FOnlineError& /*Error*/)

Implement item purchase

  1. Open StoreItemPurchaseSubsystem_Starter header file and declare the following function to send an item purchase request.

    public:
    void CreateNewOrder(
    const APlayerController* OwningPlayer,
    const TWeakObjectPtr<UStoreItemDataObject> StoreItemData,
    const int32 SelectedPriceIndex,
    const int32 Quantity = 1) const;
  2. Declare the following delegate and function to handle the response from backend.

    public:
    // ...
    FOnOrderComplete OnCheckoutCompleteDelegates;
    private:
    // ...
    void OnCreateNewOrderComplete(
    bool bWasSuccessful,
    const FAccelByteModelsOrderInfo& OrderInfo,
    const FOnlineErrorAccelByte& OnlineError) const;
  3. Open the StoreItemPurchaseSubsystem_Starter CPP file and implement the CreateNewOrder() function. This function sends the item purchase request to the backend.

    void UStoreItemPurchaseSubsystem_Starter::CreateNewOrder(
    const APlayerController* OwningPlayer,
    const TWeakObjectPtr<UStoreItemDataObject> StoreItemData,
    const int32 SelectedPriceIndex,
    const int32 Quantity) const
    {
    const UStoreItemPriceDataObject* SelectedPrice = StoreItemData->GetPrices()[SelectedPriceIndex];
    const FAccelByteModelsOrderCreate Order{
    StoreItemData->GetStoreItemId(),
    Quantity,
    static_cast<int32>(SelectedPrice->GetRegularPrice()) * Quantity,
    static_cast<int32>(SelectedPrice->GetFinalPrice()) * Quantity,
    FPreConfigCurrency::GetCodeFromType(SelectedPrice->GetCurrencyType())
    };

    PurchaseInterface->CreateNewOrder(
    *GetLocalPlayerUniqueNetId(OwningPlayer).Get(),
    Order);
    }
  4. Implement the OnCreateNewOrderComplete() function. This function triggers the OnCheckoutCompleteDelegates delegate with the data returned from the backend.

    void UStoreItemPurchaseSubsystem_Starter::OnCreateNewOrderComplete(
    bool bWasSuccessful,
    const FAccelByteModelsOrderInfo& OrderInfo,
    const FOnlineErrorAccelByte& OnlineError) const
    {
    if (OnCheckoutCompleteDelegates.IsBound())
    {
    OnCheckoutCompleteDelegates.Broadcast(OnlineError);
    }
    }
  5. Still in the CPP file, locate the Initialize() function and replace the existing implementation with the code below. This code binds the OnCreateNewOrderComplete() function to the corresponding delegate in the FOnlinePurchaseAccelByte interface.

    void UStoreItemPurchaseSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    Super::Initialize(Collection);

    const IOnlinePurchasePtr PurchasePtr = Online::GetSubsystem(GetWorld())->GetPurchaseInterface();
    ensure(PurchasePtr);

    PurchaseInterface = StaticCastSharedPtr<FOnlinePurchaseAccelByte>(PurchasePtr);
    ensure(PurchaseInterface);

    PurchaseInterface->OnCreateNewOrderCompleteDelegates.AddUObject(this, &ThisClass::OnCreateNewOrderComplete);
    }
  6. Navigate to the Deinitialize() function and replace the existing implementation with the code below. This code unbinds the OnCreateNewOrderComplete() function.

    void UStoreItemPurchaseSubsystem_Starter::Deinitialize()
    {
    Super::Deinitialize();

    PurchaseInterface->ClearOnCreateNewOrderCompleteDelegates(this);
    }

Resources