summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNoctis Linden <173088017+noctis-linden@users.noreply.github.com>2026-09-29 19:02:32 -0400
committerGitHub <noreply@github.com>2026-09-30 02:02:32 +0300
commitd2ad1c4092648af8e50c6591a287c703afed2506 (patch)
tree86f6811ac135f76d096714db27f64a4841c699fc
parent4598c539e2fd44a08bbd340ba8a50d8618598f89 (diff)
Check payment info before opening the L$ packs floater
Until the floater can add a payment method, residents without one are sent to the LindeX payment page instead of the floater. A second login probe of BuyCurrencyAddPaymentURL skips the check when it answers 200.
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/llbuycurrencyhtml.cpp101
-rw-r--r--indra/newview/llbuycurrencyhtml.h19
-rw-r--r--indra/newview/llfloaterbuycurrency.cpp40
-rw-r--r--indra/newview/llfloaterbuycurrency.h16
5 files changed, 165 insertions, 22 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index ba44b68307..f6f11f9578 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -1194,6 +1194,17 @@
<key>Value</key>
<string>https://accounts.secondlife.com/packs/floater?lang=[LANGUAGE]&amp;shortfall=[SHORTFALL]</string>
</map>
+ <key>BuyCurrencyAddPaymentURL</key>
+ <map>
+ <key>Comment</key>
+ <string>URL the viewer probes at login; a 200 means the L$ packs floater handles residents without a payment method</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>https://accounts.secondlife.com/packs/floater/add-payment-enabled</string>
+ </map>
<key>PaymentMethodURL</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llbuycurrencyhtml.cpp b/indra/newview/llbuycurrencyhtml.cpp
index 9ad068cf7f..f5bc3b5357 100644
--- a/indra/newview/llbuycurrencyhtml.cpp
+++ b/indra/newview/llbuycurrencyhtml.cpp
@@ -89,6 +89,8 @@ public:
LLBuyCurrencyHTMLHandler gBuyCurrencyHTMLHandler;
bool LLBuyCurrencyHTML::sWebFloaterEnabled = false;
+bool LLBuyCurrencyHTML::sAddPaymentEnabled = false;
+LLFetchAvatarPaymentInfo* LLBuyCurrencyHTML::sPaymentInfoRequest = NULL;
////////////////////////////////////////////////////////////////////////////////
// static
@@ -110,11 +112,37 @@ static void checkFeatureFlag_coro(std::string check_url)
}
// static
+static void checkAddPaymentFlag_coro(std::string check_url)
+{
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter = std::make_shared<LLCoreHttpUtil::HttpCoroutineAdapter>("CheckBuyCurrencyAddPaymentURL", httpPolicy);
+ LLCore::HttpRequest::ptr_t httpRequest = std::make_shared<LLCore::HttpRequest>();
+
+ // a redirect to some other page that answers 200 must not read as enabled
+ LLCore::HttpOptions::ptr_t httpOptions = std::make_shared<LLCore::HttpOptions>();
+ httpOptions->setRetries(0);
+ httpOptions->setFollowRedirects(false);
+
+ LLSD result = httpAdapter->getRawAndSuspend(httpRequest, check_url, httpOptions);
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ LLBuyCurrencyHTML::sAddPaymentEnabled = status.isHttpStatus() && status.getType() == 200;
+ LL_INFOS("LLBuyCurrency") << "Add payment probe returned " << status.toString()
+ << ", in-floater add payment " << (LLBuyCurrencyHTML::sAddPaymentEnabled ? "enabled" : "disabled") << LL_ENDL;
+}
+
+// static
void LLBuyCurrencyHTML::checkFeatureFlag()
{
std::string check_url = LLFloaterBuyCurrencyHTML::buildURL();
LLCoros::instance().launch("checkFeatureFlag_coro",
[check_url]() { checkFeatureFlag_coro(check_url); });
+
+ std::string add_payment_url = gSavedSettings.getString("BuyCurrencyAddPaymentURL");
+ LLCoros::instance().launch("checkAddPaymentFlag_coro",
+ [add_payment_url]() { checkAddPaymentFlag_coro(add_payment_url); });
}
////////////////////////////////////////////////////////////////////////////////
@@ -124,15 +152,7 @@ void LLBuyCurrencyHTML::checkFeatureFlag()
// the case where the amount is not requested.
void LLBuyCurrencyHTML::openCurrencyFloater()
{
- if (gSavedSettings.getBOOL("BuyCurrencyHTML") && sWebFloaterEnabled)
- {
- LLBuyCurrencyHTML::showDialog();
- }
- else
- {
- // legacy version
- LLFloaterBuyCurrency::buyCurrency();
- }
+ routeCurrencyRequest(false, LLStringUtil::null, 0);
}
////////////////////////////////////////////////////////////////////////////////
@@ -142,19 +162,68 @@ void LLBuyCurrencyHTML::openCurrencyFloater()
// the case where the amount and a string to display are requested.
void LLBuyCurrencyHTML::openCurrencyFloater( const std::string& message, S32 sum )
{
- if (gSavedSettings.getBOOL("BuyCurrencyHTML") && sWebFloaterEnabled)
+ routeCurrencyRequest(true, message, sum);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+// Without the in-floater add payment flow, the web floater is opened only for
+// residents with payment info on file; the rest go to the LindeX payment page.
+void LLBuyCurrencyHTML::routeCurrencyRequest( bool has_target, const std::string& message, S32 sum )
+{
+ if (!gSavedSettings.getBOOL("BuyCurrencyHTML") || !sWebFloaterEnabled)
{
- LLBuyCurrencyHTML::showDialog(sum - gStatusBar->getBalance());
- LLFloaterBuyCurrencyHTML* floater = dynamic_cast<LLFloaterBuyCurrencyHTML*>(LLFloaterReg::getInstance("buy_currency_html"));
- if (floater)
+ // legacy version, which runs its own payment info check
+ if (has_target)
{
- floater->setFallbackContext(message, sum);
+ LLFloaterBuyCurrency::buyCurrency(message, sum);
}
+ else
+ {
+ LLFloaterBuyCurrency::buyCurrency();
+ }
+ }
+ else if (sAddPaymentEnabled)
+ {
+ openWebFloater(has_target, message, sum);
}
else
{
- // legacy version
- LLFloaterBuyCurrency::buyCurrency( message, sum );
+ delete sPaymentInfoRequest;
+ sPaymentInfoRequest = new LLFetchAvatarPaymentInfo(
+ [has_target, message, sum](bool has_piof)
+ {
+ delete sPaymentInfoRequest;
+ sPaymentInfoRequest = NULL;
+
+ if (has_piof)
+ {
+ openWebFloater(has_target, message, sum);
+ }
+ else
+ {
+ LL_INFOS("LLBuyCurrency") << "No payment info on file, opening payment method page" << LL_ENDL;
+ LLFloaterBuyCurrency::openPaymentMethodPage();
+ }
+ });
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// static
+void LLBuyCurrencyHTML::openWebFloater( bool has_target, const std::string& message, S32 sum )
+{
+ if (!has_target)
+ {
+ LLBuyCurrencyHTML::showDialog();
+ return;
+ }
+
+ LLBuyCurrencyHTML::showDialog(sum - gStatusBar->getBalance());
+ LLFloaterBuyCurrencyHTML* floater = dynamic_cast<LLFloaterBuyCurrencyHTML*>(LLFloaterReg::getInstance("buy_currency_html"));
+ if (floater)
+ {
+ floater->setFallbackContext(message, sum);
}
}
diff --git a/indra/newview/llbuycurrencyhtml.h b/indra/newview/llbuycurrencyhtml.h
index 2f78199dcb..c8a98c42a9 100644
--- a/indra/newview/llbuycurrencyhtml.h
+++ b/indra/newview/llbuycurrencyhtml.h
@@ -29,6 +29,8 @@
#include "llsingleton.h"
+class LLFetchAvatarPaymentInfo;
+
class LLBuyCurrencyHTML
{
public:
@@ -45,10 +47,25 @@ class LLBuyCurrencyHTML
// close (and destroy) the currency floater
static void closeDialog();
- // probe BuyCurrencyPacksURL once at login; clears sWebFloaterEnabled if 501
+ // probe BuyCurrencyPacksURL once at login; clears sWebFloaterEnabled if 501.
+ // also launches the BuyCurrencyAddPaymentURL probe that sets sAddPaymentEnabled
static void checkFeatureFlag();
static bool sWebFloaterEnabled;
+
+ // true only when BuyCurrencyAddPaymentURL answered 200, meaning the web floater
+ // can add a payment method; otherwise residents without one are sent to the LindeX
+ static bool sAddPaymentEnabled;
+
+ private:
+ // route to the legacy floater, the web floater, or the LindeX payment page
+ static void routeCurrencyRequest( bool has_target, const std::string& message, S32 sum );
+
+ // open the web floater, with the shortfall and fallback context when there is a target
+ static void openWebFloater( bool has_target, const std::string& message, S32 sum );
+
+ // pending payment info check made before opening the web floater
+ static LLFetchAvatarPaymentInfo* sPaymentInfoRequest;
};
#endif // LL_LLBUYCURRENCYHTML_H
diff --git a/indra/newview/llfloaterbuycurrency.cpp b/indra/newview/llfloaterbuycurrency.cpp
index 56b3229fda..7533145625 100644
--- a/indra/newview/llfloaterbuycurrency.cpp
+++ b/indra/newview/llfloaterbuycurrency.cpp
@@ -338,19 +338,39 @@ void LLFloaterBuyCurrency::handleBuyCurrency(bool has_piof, bool has_target, con
}
else
{
- const std::string& grid_id = LLGridManager::getInstance()->getGridId();
- const std::string& grid_id_lower = utf8str_tolower(grid_id);
- const std::string url = gSavedSettings.getString(grid_id_lower == "damballah" ? "PaymentMethodStagingURL" : "PaymentMethodURL");
- LLWeb::loadURL(url);
+ openPaymentMethodPage();
}
}
+// static
+void LLFloaterBuyCurrency::openPaymentMethodPage()
+{
+ const std::string& grid_id = LLGridManager::getInstance()->getGridId();
+ const std::string& grid_id_lower = utf8str_tolower(grid_id);
+ const std::string url = gSavedSettings.getString(grid_id_lower == "damballah" ? "PaymentMethodStagingURL" : "PaymentMethodURL");
+ LLWeb::loadURL(url);
+}
+
LLFetchAvatarPaymentInfo::LLFetchAvatarPaymentInfo(bool has_target, const std::string& name, S32 price)
: mAvatarID(gAgent.getID()),
mHasTarget(has_target),
mPrice(price),
mName(name)
{
+ sendRequest();
+}
+
+LLFetchAvatarPaymentInfo::LLFetchAvatarPaymentInfo(const callback_t& cb)
+: mAvatarID(gAgent.getID()),
+ mHasTarget(false),
+ mPrice(0),
+ mCallback(cb)
+{
+ sendRequest();
+}
+
+void LLFetchAvatarPaymentInfo::sendRequest()
+{
LLAvatarPropertiesProcessor* processor = LLAvatarPropertiesProcessor::getInstance();
// register ourselves as an observer
processor->addObserver(mAvatarID, this);
@@ -369,6 +389,16 @@ void LLFetchAvatarPaymentInfo::processProperties(void* data, EAvatarProcessorTyp
if (data && type == APT_PROPERTIES)
{
LLAvatarData* avatar_data = static_cast<LLAvatarData*>(data);
- LLFloaterBuyCurrency::handleBuyCurrency(LLAvatarPropertiesProcessor::hasPaymentInfoOnFile(avatar_data), mHasTarget, mName, mPrice);
+ bool has_piof = LLAvatarPropertiesProcessor::hasPaymentInfoOnFile(avatar_data);
+ if (mCallback)
+ {
+ // call a local copy, since the callback may delete this observer
+ callback_t cb = mCallback;
+ cb(has_piof);
+ }
+ else
+ {
+ LLFloaterBuyCurrency::handleBuyCurrency(has_piof, mHasTarget, mName, mPrice);
+ }
}
}
diff --git a/indra/newview/llfloaterbuycurrency.h b/indra/newview/llfloaterbuycurrency.h
index 1b04c9c6d2..c6a80de926 100644
--- a/indra/newview/llfloaterbuycurrency.h
+++ b/indra/newview/llfloaterbuycurrency.h
@@ -30,21 +30,34 @@
#include "llavatarpropertiesprocessor.h"
#include "stdtypes.h"
#include "llagent.h"
+
+#include <functional>
+
class LLFloater;
class LLFetchAvatarPaymentInfo : public LLAvatarPropertiesObserver
{
public:
+ typedef std::function<void(bool has_piof)> callback_t;
+
+ // hands the result to LLFloaterBuyCurrency::handleBuyCurrency
LLFetchAvatarPaymentInfo(bool has_target, const std::string& name = std::string(), S32 price = 0);
+
+ // hands the result to cb, which may delete this observer
+ LLFetchAvatarPaymentInfo(const callback_t& cb);
+
~LLFetchAvatarPaymentInfo();
void processProperties(void* data, EAvatarProcessorType type);
private:
+ void sendRequest();
+
LLUUID mAvatarID;
bool mHasTarget;
std::string mName;
S32 mPrice;
+ callback_t mCallback;
};
@@ -62,6 +75,9 @@ public:
a space and the price will be appended
*/
+ // open the LindeX page where a resident adds a payment method
+ static void openPaymentMethodPage();
+
static LLFloater* buildFloater(const LLSD& key);
static LLFetchAvatarPaymentInfo* sPropertiesRequest;