diff --git a/assets/js/customerportal.js b/assets/js/customerportal.js index a1bc3388d..a86228441 100644 --- a/assets/js/customerportal.js +++ b/assets/js/customerportal.js @@ -29,7 +29,7 @@ class CustomerPortal { }).done(_ => { this.onRequestSucceeded(); }).fail(xhr => { - this.onRequestFailed(xhr.responseJSON?.message || 'Requesting supporter certificate failed.'); + this.onRequestFailed(xhr.responseJSON?.message || 'Requesting customer portal failed.'); }); } diff --git a/assets/js/hubsubscription.js b/assets/js/hubsubscription.js index e2dfa0005..06a7d9239 100644 --- a/assets/js/hubsubscription.js +++ b/assets/js/hubsubscription.js @@ -1,9 +1,11 @@ "use strict"; // requires store.js +const BILLING_PORTAL_SESSION_URL = STORE_API_URL + '/hub/billing-portal-session'; const CUSTOM_BILLING_URL = STORE_API_URL + '/hub/custom-billing'; const GENERATE_PAY_LINK_URL = STORE_API_URL + '/hub/generate-pay-link'; -const SUBSCRIPTION_URL = STORE_API_URL + '/hub/subscription'; +const MANAGE_SUBSCRIPTION_URL = STORE_API_URL + '/hub/manage-subscription'; +const UPDATE_PAYMENT_METHOD_URL = STORE_API_URL + '/hub/update-payment-method'; class HubSubscription { @@ -11,13 +13,15 @@ class HubSubscription { this._form = form; this._subscriptionData = subscriptionData; this._subscriptionData.hubId = searchParams.get('hub_id'); - if (this._subscriptionData.hubId && this._subscriptionData.hubId.length > 0) { - this.get(); - } let encodedReturnUrl = searchParams.get('return_url'); if (encodedReturnUrl) { this._subscriptionData.returnUrl = decodeURIComponent(encodedReturnUrl); } + this._subscriptionData.session = searchParams.get('session'); + if (this._subscriptionData.hubId && this._subscriptionData.hubId.length > 0 && this._subscriptionData.returnUrl && this._subscriptionData.returnUrl.length > 0) { + this._subscriptionData.state = 'LOADING'; + this.loadSubscription(); + } this._paddle = $.ajax({ url: 'https://cdn.paddle.com/paddle/paddle.js', cache: true, @@ -31,55 +35,89 @@ class HubSubscription { }); } - get() { - this._subscriptionData.getSuccess = false; - this.determineCustomBilling(() => { + loadSubscription() { + this.loadCustomBilling(() => { this.loadPrice(() => { this._subscriptionData.inProgress = true; this._subscriptionData.errorMessage = ''; $.ajax({ - url: SUBSCRIPTION_URL, + url: MANAGE_SUBSCRIPTION_URL, type: 'GET', data: { - hub_id: this._subscriptionData.hubId + hub_id: this._subscriptionData.hubId, + session: this._subscriptionData.session } }).done(data => { - this.onGetSucceeded(data); + this.onLoadSubscriptionSucceeded(data); }).fail(xhr => { - if (xhr.status == 404 && xhr.responseJSON?.status == 'error') { - this.onGetNotFound(); - } else { - this.onGetFailed(xhr.responseJSON?.message || 'Fetching subscription failed.'); - } + this.onLoadSubscriptionFailed(xhr.status, xhr.responseJSON?.message || 'Loading subscription failed.'); }); }); }); } - onGetSucceeded(data) { + onLoadSubscriptionSucceeded(data) { this._subscriptionData.token = data.token; this._subscriptionData.details = data.subscription; if (data.subscription.quantity) { this._subscriptionData.quantity = data.subscription.quantity; } - this._subscriptionData.getSuccess = true; + this._subscriptionData.state = 'EXISTING_CUSTOMER'; this._subscriptionData.errorMessage = ''; this._subscriptionData.inProgress = false; } - onGetNotFound() { - this._subscriptionData.getSuccess = true; + onLoadSubscriptionFailed(status, error) { + if (status == 404) { + this._subscriptionData.state = 'NEW_CUSTOMER'; + this._subscriptionData.errorMessage = ''; + } else if (status == 400) { + // Assuming that the error is due to the session being missing. + this._subscriptionData.state = 'CREATE_SESSION'; + this._subscriptionData.errorMessage = ''; + } else { + this._subscriptionData.state = 'CREATE_SESSION'; + this._subscriptionData.errorMessage = error; + } + this._subscriptionData.inProgress = false; + } + + createSession() { + if (!$(this._form)[0].checkValidity()) { + $(this._form).find(':input').addClass('show-invalid'); + this._subscriptionData.errorMessage = 'Please fill in all required fields.'; + return; + } + + this._subscriptionData.inProgress = true; this._subscriptionData.errorMessage = ''; + $.ajax({ + url: BILLING_PORTAL_SESSION_URL, + type: 'POST', + data: { + captcha: this._subscriptionData.captcha, + hub_id: this._subscriptionData.hubId, + return_url: this._subscriptionData.returnUrl + } + }).done(_ => { + this.onCreateSessionSucceeded(); + }).fail(xhr => { + this.onCreateSessionFailed(xhr.responseJSON?.message || 'Creating billing portal session failed.'); + }); + } + + onCreateSessionSucceeded() { + this._subscriptionData.state = 'CREATE_SESSION_SUCCESS'; this._subscriptionData.inProgress = false; + this._subscriptionData.errorMessage = ''; } - onGetFailed(error) { - this._subscriptionData.getSuccess = false; - this._subscriptionData.errorMessage = error; + onCreateSessionFailed(error) { this._subscriptionData.inProgress = false; + this._subscriptionData.errorMessage = error; } - determineCustomBilling(continueHandler) { + loadCustomBilling(continueHandler) { this._subscriptionData.inProgress = true; this._subscriptionData.errorMessage = ''; $.ajax({ @@ -89,19 +127,17 @@ class HubSubscription { hub_id: this._subscriptionData.hubId } }).done(data => { - this.onDetermineCustomBillingSucceeded(data); + this.onLoadCustomBillingSucceeded(data); continueHandler(); }).fail(xhr => { + this.onLoadCustomBillingFailed(xhr.status, xhr.responseJSON?.message || 'Loading custom billing options failed.'); if (xhr.status == 404 && xhr.responseJSON?.status == 'error') { - this.onDetermineCustomBillingManagedNotFound(); continueHandler(); - } else { - this.onDetermineCustomBillingManagedFailed(xhr.responseJSON?.message || 'Fetching custom billing options failed.'); } }); } - onDetermineCustomBillingSucceeded(data) { + onLoadCustomBillingSucceeded(data) { this._subscriptionData.customBilling = data.custom_billing; this._subscriptionData.quantity = this._subscriptionData.customBilling.quantity || this._subscriptionData.quantity; this._subscriptionData.email = this._subscriptionData.customBilling.email || this._subscriptionData.email; @@ -109,14 +145,13 @@ class HubSubscription { this._subscriptionData.inProgress = false; } - onDetermineCustomBillingManagedNotFound() { - this._subscriptionData.customBilling = null; - this._subscriptionData.errorMessage = ''; - this._subscriptionData.inProgress = false; - } - - onDetermineCustomBillingManagedFailed(error) { - this._subscriptionData.errorMessage = error; + onLoadCustomBillingFailed(status, error) { + if (status == 404) { + this._subscriptionData.customBilling = null; + this._subscriptionData.errorMessage = ''; + } else { + this._subscriptionData.errorMessage = error; + } this._subscriptionData.inProgress = false; } @@ -185,7 +220,6 @@ class HubSubscription { this._subscriptionData.inProgress = true; this._subscriptionData.errorMessage = ''; - this._subscriptionData.postSuccess = false; if (this._subscriptionData.customBilling?.managed && this._subscriptionData.customBilling?.override) { // managed && override this.customCheckout(PADDLE_HUB_MANAGED_SUBSCRIPTION_PLAN_ID, locale); @@ -263,10 +297,11 @@ class HubSubscription { post(subscriptionId) { $.ajax({ - url: SUBSCRIPTION_URL, + url: MANAGE_SUBSCRIPTION_URL, type: 'POST', data: { hub_id: this._subscriptionData.hubId, + session: this._subscriptionData.session, subscription_id: subscriptionId } }).done(data => { @@ -277,18 +312,20 @@ class HubSubscription { } onPostSucceeded(data) { + this._subscriptionData.state = 'EXISTING_CUSTOMER'; this._subscriptionData.token = data.token; this._subscriptionData.details = data.subscription; - this._subscriptionData.postSuccess = true; + this._subscriptionData.session = data.session; + var searchParams = new URLSearchParams(window.location.search) + searchParams.set('session', data.session); + var newRelativePathQuery = window.location.pathname + '?' + searchParams.toString(); + history.pushState(null, '', newRelativePathQuery); this._subscriptionData.errorMessage = ''; this._subscriptionData.inProgress = false; - if (this._subscriptionData.returnUrl) { - window.open(this._subscriptionData.returnUrl + '?token=' + data.token, '_self'); - } + this.transferTokenToHub(); } onPostFailed(error) { - this._subscriptionData.postSuccess = false; this._subscriptionData.errorMessage = error; this._subscriptionData.inProgress = false; } @@ -296,15 +333,27 @@ class HubSubscription { updatePaymentMethod(locale) { this._subscriptionData.inProgress = true; this._subscriptionData.errorMessage = ''; - this._paddle.then(paddle => { - paddle.Checkout.open({ - override: this._subscriptionData.details.update_url, - locale: locale, - successCallback: _ => this.get(), - closeCallback: () => { - this._subscriptionData.inProgress = false; - } + $.ajax({ + url: UPDATE_PAYMENT_METHOD_URL, + type: 'GET', + data: { + hub_id: this._subscriptionData.hubId, + session: this._subscriptionData.session, + subscription_id: this._subscriptionData.details.subscription_id + } + }).done(data => { + this._paddle.then(paddle => { + paddle.Checkout.open({ + override: data.url, + locale: locale, + successCallback: _ => this.loadSubscription(), + closeCallback: () => { + this._subscriptionData.inProgress = false; + } + }); }); + }).fail(xhr => { + this.onPutFailed(xhr.status, xhr.responseJSON?.message || 'Updating payment method failed.'); }); } @@ -312,16 +361,17 @@ class HubSubscription { this._subscriptionData.inProgress = true; this._subscriptionData.errorMessage = ''; $.ajax({ - url: SUBSCRIPTION_URL, + url: MANAGE_SUBSCRIPTION_URL, type: 'PUT', data: { hub_id: this._subscriptionData.hubId, + session: this._subscriptionData.session, pause: true } }).done(data => { this.onPutSucceeded(data, false); }).fail(xhr => { - this.onPutFailed(xhr.responseJSON?.message || 'Updating subscription failed.'); + this.onPutFailed(xhr.status, xhr.responseJSON?.message || 'Updating subscription failed.'); }); } @@ -335,10 +385,11 @@ class HubSubscription { this._subscriptionData.inProgress = true; this._subscriptionData.errorMessage = ''; $.ajax({ - url: SUBSCRIPTION_URL, + url: MANAGE_SUBSCRIPTION_URL, type: 'PUT', data: { hub_id: this._subscriptionData.hubId, + session: this._subscriptionData.session, pause: false, preview: true } @@ -347,7 +398,7 @@ class HubSubscription { this._subscriptionData.errorMessage = ''; this._subscriptionData.inProgress = false; }).fail(xhr => { - this.onPutFailed(xhr.responseJSON?.message || 'Calculating price failed.'); + this.onPutFailed(xhr.status, xhr.responseJSON?.message || 'Calculating price failed.'); }); } @@ -355,16 +406,17 @@ class HubSubscription { this._subscriptionData.inProgress = true; this._subscriptionData.errorMessage = ''; $.ajax({ - url: SUBSCRIPTION_URL, + url: MANAGE_SUBSCRIPTION_URL, type: 'PUT', data: { hub_id: this._subscriptionData.hubId, + session: this._subscriptionData.session, pause: false } }).done(data => { - this.onPutSucceeded(data, true); + this.onPutSucceeded(data, this._subscriptionData.details.state == 'paused'); }).fail(xhr => { - this.onPutFailed(xhr.responseJSON?.message || 'Updating subscription failed.'); + this.onPutFailed(xhr.status, xhr.responseJSON?.message || 'Updating subscription failed.'); }); } @@ -390,10 +442,11 @@ class HubSubscription { this._subscriptionData.inProgress = true; this._subscriptionData.errorMessage = ''; $.ajax({ - url: SUBSCRIPTION_URL, + url: MANAGE_SUBSCRIPTION_URL, type: 'PUT', data: { hub_id: this._subscriptionData.hubId, + session: this._subscriptionData.session, quantity: this._subscriptionData.quantity, preview: true } @@ -402,7 +455,7 @@ class HubSubscription { this._subscriptionData.errorMessage = ''; this._subscriptionData.inProgress = false; }).fail(xhr => { - this.onPutFailed(xhr.responseJSON?.message || 'Calculating price failed.'); + this.onPutFailed(xhr.status, xhr.responseJSON?.message || 'Calculating price failed.'); }); } @@ -410,17 +463,18 @@ class HubSubscription { this._subscriptionData.inProgress = true; this._subscriptionData.errorMessage = ''; $.ajax({ - url: SUBSCRIPTION_URL, + url: MANAGE_SUBSCRIPTION_URL, type: 'PUT', data: { hub_id: this._subscriptionData.hubId, + session: this._subscriptionData.session, quantity: this._subscriptionData.quantity } }).done(data => { this._subscriptionData.changeSeatsModal.open = false; this.onPutSucceeded(data, true); }).fail(xhr => { - this.onPutFailed(xhr.responseJSON?.message || 'Updating subscription failed.'); + this.onPutFailed(xhr.status, xhr.responseJSON?.message || 'Updating subscription failed.'); }); } @@ -429,14 +483,21 @@ class HubSubscription { this._subscriptionData.details = data.subscription; this._subscriptionData.errorMessage = ''; this._subscriptionData.inProgress = false; - if (shouldOpenReturnUrl && this._subscriptionData.returnUrl) { - window.open(this._subscriptionData.returnUrl + '?token=' + data.token, '_self'); + if (shouldOpenReturnUrl) { + this.transferTokenToHub(); } } - onPutFailed(error) { + onPutFailed(status, error) { + if (status == 401) { + this._subscriptionData.state = 'CREATE_SESSION'; + } this._subscriptionData.errorMessage = error; this._subscriptionData.inProgress = false; } + transferTokenToHub() { + window.open(this._subscriptionData.returnUrl + '?token=' + this._subscriptionData.token, '_self'); + } + } diff --git a/i18n/de.yaml b/i18n/de.yaml index d84613446..0a36ae057 100644 --- a/i18n/de.yaml +++ b/i18n/de.yaml @@ -362,14 +362,23 @@ translation: "Vielen Dank für deine Anmeldung! Ein Bestätigungslink wurde an deine E-Mail-Adresse zugeschickt." # Hub Billing -- id: hub_billing_landing_title +- id: hub_billing_generic_title translation: "Hub-Abonnement verwalten" -- id: hub_billing_landing_description - translation: "Bitte gib deine Hub-ID ein, um fortzufahren. Diese findest du als Admin in deiner Hub-Instanz unter \"Abrechnung\"." -- id: hub_billing_landing_hubid_placeholder - translation: "Hub-ID" -- id: hub_billing_landing_submit - translation: "Weiter" + +- id: hub_billing_missingparams_description + translation: "Um dein Abonnement zu verwalten, verwende bitte den \"Abonnement verwalten\"-Button in deiner Hub-Instanz unter \"Admin\"." + +- id: hub_billing_loading_description + translation: "Bitte warten, während wir deine Abonnement-Informationen laden." + +- id: hub_billing_createsession_description + translation: "Um auf deine Abonnement-Informationen zuzugreifen, wird ein Bestätigungslink an die E-Mail-Adresse gesendet, die du für den Kauf verwendet hast." +- id: hub_billing_createsession_success_description + translation: "Bitte überprüfe deine E-Mails auf den Link zur Verwaltung deines Hub-Abonnements." +- id: hub_billing_createsession_loadcaptcha + translation: "Weiter…" +- id: hub_billing_createsession_submit + translation: "Zugang anfordern" - id: hub_billing_manage_status_title translation: "Status" @@ -392,8 +401,6 @@ translation: "Nächste Zahlung" - id: hub_billing_manage_next_payment_date_description translation: "Fälligkeitsdatum" -- id: hub_billing_manage_next_payment_date_unavailable - translation: "N/A (pausiert)" - id: hub_billing_manage_next_payment_pause_action translation: "Abonnement pausieren" - id: hub_billing_manage_next_payment_restart_action @@ -412,10 +419,8 @@ - id: hub_billing_manage_license_key_title translation: "Lizenzschlüssel" -- id: hub_billing_manage_license_key_instruction_with_returnurl - translation: "Dies ist dein Lizenzschlüssel, der an deine Hub-ID gebunden ist. Drück auf den Button unten, um ihn automatisch auf deine Hub-Instanz zu übertragen. Andernfalls gib ihn gegebenenfalls manuell in deiner Hub-Instanz unter \"Abrechnung\" ein." -- id: hub_billing_manage_license_key_instruction_without_returnurl - translation: "Dies ist dein Lizenzschlüssel, der an deine Hub-ID gebunden ist. Gib ihn in deiner Hub-Instanz unter \"Abrechnung\" gegebenenfalls manuell ein." +- id: hub_billing_manage_license_key_instruction + translation: "Dies ist dein Lizenzschlüssel, der an deine Hub-ID gebunden ist. Drück auf den Button unten, um ihn automatisch auf deine Hub-Instanz zu übertragen." - id: hub_billing_manage_license_key_transfer_action translation: "Zum Hub übertragen" diff --git a/i18n/en.yaml b/i18n/en.yaml index 1ebac61ec..4494ac0db 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -362,14 +362,23 @@ translation: "Thank you for subscribing! A confirmation link has been sent to your email address." # Hub Billing -- id: hub_billing_landing_title +- id: hub_billing_generic_title translation: "Manage Hub Subscription" -- id: hub_billing_landing_description - translation: "Please enter your Hub ID in order to continue. You will find this as an admin of your Hub instance under \"Billing\"." -- id: hub_billing_landing_hubid_placeholder - translation: "Hub ID" -- id: hub_billing_landing_submit - translation: "Continue" + +- id: hub_billing_missingparams_description + translation: "To manage your subscription, please use the \"Manage Subscription\" button in your Hub instance under \"Admin\"." + +- id: hub_billing_loading_description + translation: "Please wait while we're loading your subscription information." + +- id: hub_billing_createsession_description + translation: "To access your subscription information, a confirmation link will be sent to the email you used to make your purchase." +- id: hub_billing_createsession_success_description + translation: "Please check your email for the link to manage your Hub subscription." +- id: hub_billing_createsession_loadcaptcha + translation: "Continue…" +- id: hub_billing_createsession_submit + translation: "Request Access" - id: hub_billing_manage_status_title translation: "Status" @@ -392,8 +401,6 @@ translation: "Next Payment" - id: hub_billing_manage_next_payment_date_description translation: "Due Date" -- id: hub_billing_manage_next_payment_date_unavailable - translation: "N/A (Paused)" - id: hub_billing_manage_next_payment_pause_action translation: "Pause Subscription" - id: hub_billing_manage_next_payment_restart_action @@ -412,10 +419,8 @@ - id: hub_billing_manage_license_key_title translation: "License Key" -- id: hub_billing_manage_license_key_instruction_with_returnurl - translation: "This is your license key that is bound to your Hub ID. Press the button below to transfer it to your Hub instance automatically. Otherwise, enter it in your Hub instance under \"Billing\" manually if necessary." -- id: hub_billing_manage_license_key_instruction_without_returnurl - translation: "This is your license key that is bound to your Hub ID. Enter it in your Hub instance under \"Billing\" manually if necessary." +- id: hub_billing_manage_license_key_instruction + translation: "This is your license key that is bound to your Hub ID. Press the button below to transfer it to your Hub instance automatically." - id: hub_billing_manage_license_key_transfer_action translation: "Transfer to Hub" diff --git a/layouts/hub-billing/single.html b/layouts/hub-billing/single.html index fabe4ead3..a6f3b4ec5 100644 --- a/layouts/hub-billing/single.html +++ b/layouts/hub-billing/single.html @@ -1,26 +1,64 @@ {{ define "main" }}