Purchasing offers
E-commerce functionality is provided via the IOnlineStoreV2
, IOnlineEntitlements
and IOnlinePurchase
interfaces. Purchasing items is done via the IOnlinePurchase
interface.
Platform support
- Epic Games Store
- Steam
- Google Play
When purchasing offers on Steam, there is no notification for a successful purchase. You can initiate a purchase, and observe the change in entitlements by polling IOnlineEntitlements
, but the purchase callback will be raised when the purchase is initiated, not completed.
DLC offers on Steam e-commerce
When using Steam e-commerce, DLC offers will be suffixed with dlc:
to the offer IDs. You can only initiate the purchase for one DLC offer at a time, which will open the Steam overlay so the user can purchase the DLC.
Steam requires installing DLC outside of the game after purchasing, so there's no actions to take after DLC is purchased; the user must restart the game, install the DLC and launch the game again.
Refer to Initating a purchase for an example on how to purchase one or more offers.
Item offers on Steam e-commerce
When using Steam e-commerce, item offers will be suffixed with itemdef:
to the offer IDs. You can purchase as many items as you want at the same time, and can specify quantities for each item. After a successful item purchase, the items will appear as entitlements after you query for them again with QueryEntitlements
.
Refer to Initating a purchase for an example on how to purchase one or more offers.
Initating a purchase on any platform
When initiating a purchase, you should use offer IDs that you obtained when Retrieving offers. The example below shows offer IDs that you would see on Steam, but the format of offer IDs will differ per-platform.
- C++
- Blueprints
To initiate a purchase, first get the online purchase interface:
#include "OnlineSubsystem.h"
#include "OnlineSubsystemUtils.h"
#include "Interfaces/OnlinePurchaseInterface.h"
// ...
IOnlineSubsystem* Subsystem = Online::GetSubsystem(this->GetWorld());
IOnlinePurchasePtr Purchase = Subsystem->GetPurchaseInterface();
Then, use Checkout
to initiate a purchase:
// The offer IDs should be obtained from QueryOffersByFilter/GetOffers.
FPurchaseCheckoutRequest Request = {};
Request.AddPurchaseOffer(TEXT(""), TEXT("itemdef:100"), 1);
Request.AddPurchaseOffer(TEXT(""), TEXT("itemdef:200"), 1);
Purchase->Checkout(
*Identity->GetUniquePlayerId(0).Get(), // The local player to get offers for.
Request,
FOnPurchaseCheckoutComplete::CreateLambda([](
const FOnlineError& Result,
const TSharedRef<FPurchaseReceipt>& Receipt)
{
if (Result.WasSuccessful())
{
// Refer to the "Platform Support" section to understand what the
// Checkout callback represents. On some platforms, it represents
// the checkout process completing, and on some platforms it only
// indicates that the process was started.
}
})
)
Querying and consuming entitlements on Epic Games Store
To consume a purchase that was made on Epic Games Store, you need to:
- Query for the entitlements the user currently has using the entitlements interface.
- Check the entitlement's
ItemType
attribute to see if it is set toConsumable
. - Then call
IOnlinePurchase::FinalizeReceiptValidationInfo
with the entitlement ID passed in asInReceiptValidationInfo
.
Consuming an entitlement on Epic Games Store
- C++
- Blueprints
Once you have the entitlement ID from the entitlements interface, call FinalizeReceiptValidationInfo
and pass the entitlement ID in the InReceiptValidationInfo
parameter:
Purchase->FinalizeReceiptValidationInfo(
*Identity->GetUniquePlayerId(0).Get(), // The local player to get offers for.
EntitlementId, // The entitlement ID.
FOnFinalizeReceiptValidationInfoComplete::CreateLambda([
PurchaseWk = TWeakPtr<IOnlinePurchase, ESPMode::ThreadSafe>(Purchase),
UserId = this->Identity->GetUniquePlayerId(0)](
const FOnlineError& Result,
const FString& ValidationInfo))
{
if (auto Purchase = PurchaseWk.Pin())
{
if (Result.WasSuccessful())
{
// The entitlement was successfully consumed.
}
}
})
)
Querying entitlements on Steam
To query what has been purchased on Steam, use the entitlements interface to query entitlements.
Querying receipts and finalizing purchases on Google Play
To finalize purchases on Google Play, you need to check for outstanding receipts and call FinalizeReceipt
.
Querying receipts on startup
When the game starts up, you should query for any outstanding receipts so that you can finalize the purchases and award items.
After a purchase is consumed with FinalizePurchase
, the receipt will no longer appear when querying receipts or calling GetReceipts
.
- C++
- Blueprints
To query receipts, use QueryReceipts
to start the query and GetReceipts
to fetch the receipt data.
Purchase->QueryReceipts(
*Identity->GetUniquePlayerId(0).Get(), // The local player to get offers for.
true, // This value is ignored.
FOnQueryReceiptsComplete::CreateLambda([
PurchaseWk = TWeakPtr<IOnlinePurchase, ESPMode::ThreadSafe>(Purchase),
UserId = this->Identity->GetUniquePlayerId(0)](
const FOnlineError& Result)
{
if (auto Purchase = PurchaseWk.Pin())
{
if (Result.WasSuccessful())
{
// Receipts were successfully queried. Access via GetReceipts.
TArray<FPurchaseReceipt> Receipts;
Purchase->GetReceipts(*UserId, Receipts);
}
}
})
)
Watching for changes to receipts
When a purchase completes on Google Play, the receipt can end up with a TransactionState
of either Purchased
or Deferred
. You must not award items to players until the receipt is in a Purchased
state.
If a purchase ends up in the Deferred
state, you may later see the receipt move to the Purchased
state. When this happens, the OnUnexpectedPurchaseReceipt
event will fire, and you can re-check the receipts via GetReceipts
to see if any have moved from Deferred
to Purchased
.
You must handle deferred purchases on Google Play. Also, you can not know when a deferred purchase fails; this is a limitation of how Google Play Billing works. Instead, you simply don't award items until the receipt moves into a Purchased
state, so a failed deferred receipt never gets handled by the game.
The OnUnexpectedPurchaseReceipt
event fires:
- When a deferred purchase moves to the
Purchased
state. - When a receipt is consumed via
FinalizePurchase
and removed from theGetReceipts
array.
The TransactionId
field of a receipt is the ReceiptId
to use in other functions.
Consuming a purchase via FinalizePurchase
To acknowledge a purchase on Google Play and consume the purchase, you must call FinalizePurchase
. If you don't call this function for a receipt, the user's purchase will automatically be refunded 3 days after it moved into the Purchased
state.
- C++
- Blueprints
To consume a purchase, call FinalizePurchase
:
Purchase->FinalizePurchase(
*Identity->GetUniquePlayerId(0).Get(), // The local player to get offers for.
ReceiptId // The TransactionId field of the receipt.
);
There is no callback to pass into FinalizePurchase
. You can know when the purchase was consumed by listening for the OnUnexpectedPurchaseReceipt
event.