// This service is used for in-game purchases. Most of the actual work occurs in a lua script on player's clients, // but this service is responsible for sending the appropriate signals around, and exposing a lua call to our users. // There is also a nifty function exposed called "GetProductInfo" that will return a table from the web with properties about an assetId #pragma once #include "V8Tree/Service.h" #include "V8Tree/Instance.h" #include "V8DataModel/Remote.h" namespace RBX { extern const char* const sMarketplaceService; class MarketplaceService : public DescribedCreatable , public Service { std::string devProductInfoUrl; std::string productInfoUrl; std::string playerOwnsAssetUrl; rbx::signals::scoped_connection playerRemovingConnection; boost::unordered_map, boost::function)> > callbackFunctionMap; bool receiptProcessingEnabledByUser; rbx::signals::scoped_connection playerAddedReceiptConnection; rbx::signals::scoped_connection playerPurchaseReceiptConnection; rbx::signals::scoped_connection verifyPurchaseConnection; // Cache the MarketPlace Product Info Response struct ResponseCache { RBX::Time lastFetch; shared_ptr values; }; typedef boost::unordered_map UrlToResponseMap; UrlToResponseMap mMap; template void dispatchRequest(const std::string& url, boost::function resumeFunction, boost::function errorFunction); bool isValidPlayer(const shared_ptr player, const std::string& funcName, boost::function errorFunction = 0); void processPlayerOwnsAssetResponseSuccess(std::string response, boost::function resumeFunction, boost::function errorFunction); void processPlayerOwnsAssetResponseError(std::string error, boost::function errorFunction); void verifyPurchaseResponseNoDMLock(std::string response, int userId, int productId); void verifyPurchaseErrorNoDMLock(std::string error, int userId, int productId); void setupBackendOnlyReceiptHandling(); void getReceiptsForJoiningPlayer(shared_ptr newPlayer); void getReceiptsAfterPurchase(std::string response, int userId, int productId); void fetchAndProcessTransactions(int userId); static void handleReceiptQueryResponseSuccess(weak_ptr weakMk, std::string response); static void handleReceiptQueryResponseFailure(weak_ptr weakMk, std::string error); static void processReceipt(weak_ptr weakMk, shared_ptr receiptInfo); static void processReceiptSuccessContinuation(shared_ptr mk, std::string receiptId, int currencyType, int currencySpent, shared_ptr result); private: typedef DescribedCreatable Super; protected: void onServiceProvider(ServiceProvider* oldSp, ServiceProvider* newSp) override; public: MarketplaceService(); enum CurrencyType { CURRENCY_DEFAULT = 0, CURRENCY_ROBUX = 1, CURRENCY_TIX = 2, }; enum InfoType { INFO_ASSET = 0, INFO_PRODUCT = 1, INFO_NONE = 2 }; enum ProductPurchaseDecision { DECISION_NOT_PROCESSED_YET = 0, DECISION_PURCHASE_GRANTED = 1 }; static const char* urlApiPath() { return "marketplace"; } void verifyPurchaseTicket(std::string ticket, int userId, int productId); rbx::remote_signal player, std::string productId, bool isPurchased)> nativePurchaseFinished; rbx::remote_signal player, std::string productId)> promptNativePurchaseRequested; rbx::remote_signal player, std::string productId, std::string receipt, bool isPurchased)> thirdPartyPurchaseFinished; rbx::remote_signal player, std::string productId)> promptThirdPartyPurchaseRequested; rbx::remote_signal player, int assetId, bool equipIfPurchased, CurrencyType currencyType)> promptProductPurchaseRequested; rbx::remote_signal promptProductPurchaseFinished; rbx::remote_signal player, int assetId, bool isPurchased)> promptPurchaseFinished; rbx::remote_signal player, int assetId, bool equipIfPurchased, CurrencyType currencyType)> promptPurchaseRequested; rbx::remote_signal clientPurchaseSuccess; rbx::remote_signal responseTable)> serverPurchaseVerification; rbx::remote_signal)> clientLuaDialogRequested; rbx::remote_signal)> luaDialogCallbackSignal; typedef boost::function, Reflection::AsyncCallbackDescriptor::ResumeFunction, Reflection::AsyncCallbackDescriptor::ErrorFunction)> ReceiptCallback; ReceiptCallback receiptCallback; void processReceiptsCallbackChanged(const ReceiptCallback& oldValue); void signalPromptProductPurchaseFinished(int userId, const int productId, const bool success); void promptProductPurchase(const shared_ptr player, const int productId, const bool equipIfPurchased, const CurrencyType currencyType); void signalPromptPurchaseFinished(const shared_ptr player, const int assetId, const bool isPurchase); void promptPurchase(const shared_ptr player, const int assetId, const bool equipIfPurchased, const CurrencyType currencyType); void signalClientPurchaseSuccess(std::string ticket, int userId, int productId); void getProductInfo(const int assetId, const MarketplaceService::InfoType infoType, boost::function)> resumeFunction, boost::function errorFunction); void onReceivedRawProductInfoSuccess(std::string urlPath, std::string response, boost::weak_ptr weakDM, boost::function)> resumeFunction, boost::function errorFunction); void onReceivedRawProductInfoError(std::string error, boost::weak_ptr weakDM, boost::function errorFunction); void playerOwnsAsset(const shared_ptr player, const int assetId, boost::function resumeFunction, boost::function errorFunction); bool launchClientLuaDialog(std::string message, std::string accept, std::string decline, shared_ptr player, boost::function)> callback); void signalServerLuaDialogClosed(bool value); void executeClientDialogCallback(bool value, shared_ptr player); void promptThirdPartyPurchase(const shared_ptr player, std::string thirdPartyProductId); void signalPromptThirdPartyPurchaseFinished(const shared_ptr player, std::string productId, std::string receipt, const bool success); void getDeveloperProductsAsync(boost::function) > resumeFunction, boost::function errorFunction); void promptNativePurchase(const shared_ptr player, std::string nativeProductId); void signalPromptNativePurchaseFinished(const shared_ptr player, std::string productId, const bool success); void processRemoteEvent(const Reflection::EventDescriptor& descriptor, const Reflection::EventArguments& args, const SystemAddress& source) override; static void processPurchaseFinished( weak_ptr weakDm, Reflection::EventArguments args, SystemAddress source, const bool isOwner); static void processPurchaseError( weak_ptr weakDm, Reflection::EventArguments args, SystemAddress source, const std::string msg); static void processPurchaseFinishedCallback(DataModel* dm, Reflection::EventArguments args, SystemAddress source); }; }