From a9686bcc2c00db5078e5f84a145eacbbfa969262 Mon Sep 17 00:00:00 2001 From: Tobias Hagemann Date: Fri, 6 Feb 2026 07:59:36 +0100 Subject: [PATCH 1/3] refresh hub license token via API with ALTCHA captcha verification Instead of using the store's token directly, the billing page now refreshes the license token through the /licenses/hub/refresh API endpoint with captcha verification. On normal visits the token is displayed inline; on checkout/modification flows the user is redirected back to Hub. Also extracts captcha widget into a reusable partial and fixes Paddle passthrough JSON serialization. --- assets/js/hubsubscription.js | 40 +++++++++++++++++++++++++++----- layouts/hub-billing/single.html | 10 +++++++- layouts/hub-register/single.html | 18 ++------------ layouts/partials/captcha.html | 4 ++-- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/assets/js/hubsubscription.js b/assets/js/hubsubscription.js index db82044b0..727dfb790 100644 --- a/assets/js/hubsubscription.js +++ b/assets/js/hubsubscription.js @@ -5,6 +5,7 @@ const CUSTOM_BILLING_URL = LEGACY_STORE_URL + '/hub/custom-billing'; const GENERATE_PAY_LINK_URL = LEGACY_STORE_URL + '/hub/generate-pay-link'; const MANAGE_SUBSCRIPTION_URL = LEGACY_STORE_URL + '/hub/manage-subscription'; const UPDATE_PAYMENT_METHOD_URL = LEGACY_STORE_URL + '/hub/update-payment-method'; +const REFRESH_LICENSE_URL = API_BASE_URL + '/licenses/hub/refresh'; class HubSubscription { @@ -56,7 +57,7 @@ class HubSubscription { } onLoadSubscriptionSucceeded(data) { - this._subscriptionData.token = data.token; + this._subscriptionData.verificationToken = data.token; this._subscriptionData.details = data.subscription; if (data.subscription.quantity) { this._subscriptionData.quantity = data.subscription.quantity; @@ -64,6 +65,7 @@ class HubSubscription { this._subscriptionData.state = 'EXISTING_CUSTOMER'; this._subscriptionData.errorMessage = ''; this._subscriptionData.inProgress = false; + this._subscriptionData.needsTokenRefresh = true; } onLoadSubscriptionFailed(status, error) { @@ -271,7 +273,7 @@ class HubSubscription { override: payLink, email: this._subscriptionData.email, locale: locale, - passthrough: '{"hub_id": ' + this._subscriptionData.hubId + '}', + passthrough: JSON.stringify({ hub_id: this._subscriptionData.hubId }), successCallback: data => this.getPaddleOrderDetails(data.checkout.id), closeCallback: () => { this._subscriptionData.inProgress = false; @@ -311,7 +313,7 @@ class HubSubscription { onPostSucceeded(data) { this._subscriptionData.state = 'EXISTING_CUSTOMER'; - this._subscriptionData.token = data.token; + this._subscriptionData.verificationToken = data.token; this._subscriptionData.details = data.subscription; this._subscriptionData.session = data.session; var searchParams = new URLSearchParams(window.location.search) @@ -320,7 +322,8 @@ class HubSubscription { history.pushState(null, '', newRelativePathQuery); this._subscriptionData.errorMessage = ''; this._subscriptionData.inProgress = false; - this.transferTokenToHub(); + this._subscriptionData.shouldTransferToHub = true; + this._subscriptionData.needsTokenRefresh = true; } onPostFailed(error) { @@ -477,12 +480,13 @@ class HubSubscription { } onPutSucceeded(data, shouldOpenReturnUrl) { - this._subscriptionData.token = data.token; + this._subscriptionData.verificationToken = data.token; this._subscriptionData.details = data.subscription; this._subscriptionData.errorMessage = ''; this._subscriptionData.inProgress = false; if (shouldOpenReturnUrl) { - this.transferTokenToHub(); + this._subscriptionData.shouldTransferToHub = true; + this._subscriptionData.needsTokenRefresh = true; } } @@ -494,6 +498,30 @@ class HubSubscription { this._subscriptionData.inProgress = false; } + refreshToken() { + this._subscriptionData.inProgress = true; + this._subscriptionData.errorMessage = ''; + $.ajax({ + url: REFRESH_LICENSE_URL, + type: 'POST', + data: { + token: this._subscriptionData.verificationToken, + captcha: this._subscriptionData.captcha + } + }).done(token => { + this._subscriptionData.token = token; + this._subscriptionData.needsTokenRefresh = false; + this._subscriptionData.errorMessage = ''; + this._subscriptionData.inProgress = false; + if (this._subscriptionData.shouldTransferToHub) { + this.transferTokenToHub(); + } + }).fail(xhr => { + this._subscriptionData.errorMessage = xhr.responseJSON?.message || 'Refreshing license failed.'; + this._subscriptionData.inProgress = false; + }); + } + transferTokenToHub() { window.open(this._subscriptionData.returnUrl + '?token=' + this._subscriptionData.token, '_self'); } diff --git a/layouts/hub-billing/single.html b/layouts/hub-billing/single.html index f7ee27147..65c895959 100644 --- a/layouts/hub-billing/single.html +++ b/layouts/hub-billing/single.html @@ -3,7 +3,7 @@ {{ end }} {{ define "main" }}
-
+