Economy: Store, Purchases & DLC

UCrimsonOnlineStoreService

Wraps IOnlineStoreV2. Browses the platform storefront catalog; it never purchases anything - hand the chosen OfferId to UCrimsonOnlinePurchaseService::Checkout.

MemberDescription
QueryCategories(LocalUserNum = 0)Fetches the store category list. Completes on OnStoreCategoriesQueried, then read GetCachedCategories
QueryOffers(OfferIds, LocalUserNum = 0)Fetches specific offers by platform offer ID. Completes on OnStoreOffersQueried
QueryOffersByKeyword(Keyword, LocalUserNum = 0)Searches the catalog for offers matching a keyword. Completes on OnStoreOffersQueried
GetCachedCategories()BlueprintPure. Returns the TArray<FCrimsonStoreCategory> from the last successful QueryCategories
GetCachedOffers()BlueprintPure. Returns the TArray<FCrimsonStoreOffer> from the last successful offer query
OnStoreCategoriesQueriedDelegate: bool bSuccess
OnStoreOffersQueriedDelegate: bool bSuccess, const TArray<FCrimsonStoreOffer>& Offers
Prices are integers, not text
FCrimsonStoreOffer reports RegularPriceCents and FinalPriceCents as int64 in the smallest currency unit, plus an ISO 4217 CurrencyCode. Format them yourself for display - there is no pre-formatted price string and no is-on-sale flag; compare the two values to detect a discount.

UCrimsonOnlinePurchaseService

Wraps IOnlinePurchase. Opens the platform purchase flow and reads back receipts.

MemberDescription
Checkout(OfferIds, LocalUserNum = 0)Opens the platform purchase dialog for one or more offer IDs. Completes on OnCheckoutComplete
QueryReceipts(LocalUserNum = 0, bRestoreReceipts = false)Fetches purchase history; pass true to restore completed transactions on mobile/console. Completes on OnQueryReceiptsComplete
GetCachedReceipts()BlueprintPure. Returns TArray<FCrimsonPurchaseReceipt> from the last successful QueryReceipts
OnCheckoutCompleteDelegate: bool bSuccess, const FCrimsonPurchaseReceipt& Receipt. Success means TransactionState == Purchased
OnQueryReceiptsCompleteDelegate: bool bSuccess
OnUnexpectedReceiptDelegate with no parameters. The platform delivered a receipt outside a Checkout call - call QueryReceipts from the handler
No finalize step on this wrapper
The service does not expose a receipt-finalize call. Grant the purchased content when OnCheckoutComplete reports Purchased, and re-sync with QueryReceipts whenever OnUnexpectedReceipt fires - that is how a purchase made on another device, or a reversed refund, reaches this session.

UCrimsonOnlineEntitlementService

Wraps IOnlineEntitlements. Queries what the player owns and answers DLC ownership questions using your own internal FName IDs.

MemberDescription
QueryEntitlements(LocalUserNum = 0)Fetches the entitlement list from the platform and fills the cache. Completes on OnEntitlementsQueried
IsDLCOwned(InternalDLCId, LocalUserNum = 0)BlueprintPure. Resolves the platform ID from the mapping table and returns true if it is owned and unconsumed. Returns false, with a warning, before QueryEntitlements has completed
CheckDLCOwnership(InternalDLCId, LocalUserNum = 0)Runs the same check and reports it through OnDLCOwnershipChecked instead of returning it
GetCachedEntitlements()BlueprintPure. Returns TArray<FCrimsonEntitlementInfo> from the last successful query
OnEntitlementsQueriedDelegate: bool bSuccess, const TArray<FCrimsonEntitlementInfo>& Entitlements
OnDLCOwnershipCheckedDelegate: FName InternalDLCId, bool bOwned
Query first, then ask
IsDLCOwned reads a cache. Until QueryEntitlements has completed successfully it returns false and logs "IsDLCOwned called before QueryEntitlements has completed" - which is indistinguishable from genuinely not owning the DLC if you are not watching the log. Query once at boot, after login.
Async node
Query Entitlements (DLC) (UCrimsonAsyncAction_QueryEntitlements). Input pin: Local User Num (int32, default 0). Output pins On Success and On Failure, each carrying bSuccess and an Entitlements array. On the Null subsystem On Failure fires with an empty array - that is expected, not an integration error.

DLC Mapping Table

Every platform names the same DLC differently, so the plugin resolves the name for you. Your code asks about one internal FName; the service reads the field matching the detected ECrimsonOnlinePlatform out of a DataTable row.

Setup steps

1. Create a DataTable asset using FCrimsonDLCMappingRow as the row struct.
2. Add one row per DLC. Name the row exactly as the internal ID you will pass in code - the lookup is FindRow(InternalDLCId) on the row name - and set the InternalId field to the same value so it reads clearly in the editor.
3. Fill the PlatformIds sub-struct with whichever platform fields apply: SteamDLCAppId, EOSOfferId, PSNProductId, XboxProductId, SwitchNsuid. Leave the rest empty.
4. Assign the DataTable to DLCMappingTable in Project Settings -> Crimson -> Crimson Online Subsystem. The property filters the asset picker to tables using this row struct.
5. Call QueryEntitlements(0) once after login, then IsDLCOwned(FName("DLC_BonusCampaign")) for the rest of the session.

FCrimsonDLCMappingRow

FieldTypeDescription
InternalIdFNameMirrors the DataTable row name - the ID your game code passes
PlatformIdsFCrimsonDLCPlatformIdsNested struct holding one ID per platform (see below)

FCrimsonDLCPlatformIds

FieldTypeDescription
SteamDLCAppIdFStringSteam DLC App ID, from the Steamworks partner portal
EOSOfferIdFStringEpic Online Services catalog offer ID, from the EOS Developer Portal
PSNProductIdFStringPlayStation Network service entitlement or product ID
XboxProductIdFStringXbox / Microsoft Store product ID
SwitchNsuidFStringNintendo eShop NSUID
An unmapped platform is a silent false
If the detected platform has no matching field on the row - or the table is not assigned at all - ResolvePlatformId returns an empty string and IsDLCOwned answers false with a warning in the log. Set DebugPlatformOverride while testing so PIE resolves against the field you actually filled in.