Skip to content

Commit 1b77eff

Browse files
committed
Merge branch 'develop'
2 parents bcfffbf + 80e82f0 commit 1b77eff

File tree

12 files changed

+117
-64
lines changed

12 files changed

+117
-64
lines changed

assets/js/const.template.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ const PADDLE_TOKEN = '{{ .Site.Params.paddleToken }}';
66
const PADDLE_VENDOR_ID = {{ .Site.Params.paddleVendorId }};
77
const PADDLE_DESKTOP_PRICE_IDS = {{ .Site.Params.paddleDesktopPriceIds | jsonify }};
88
const PADDLE_ANDROID_PRICE_ID = '{{ .Site.Params.paddleAndroidPriceId }}';
9-
const PADDLE_HUB_SELF_HOSTED_SUBSCRIPTION_PLAN_ID = {{ .Site.Params.paddleHubSelfHostedSubscriptionPlanId }};
10-
const PADDLE_HUB_MANAGED_SUBSCRIPTION_PLAN_ID = {{ .Site.Params.paddleHubManagedSubscriptionPlanId }};
9+
const PADDLE_HUB_SELF_HOSTED_YEARLY_PLAN_ID = {{ .Site.Params.paddleHubSelfHostedYearlyPlanId }};
10+
const PADDLE_HUB_SELF_HOSTED_MONTHLY_PLAN_ID = {{ .Site.Params.paddleHubSelfHostedMonthlyPlanId }};
11+
const PADDLE_HUB_MANAGED_YEARLY_PLAN_ID = {{ .Site.Params.paddleHubManagedYearlyPlanId }};
12+
const PADDLE_HUB_MANAGED_MONTHLY_PLAN_ID = {{ .Site.Params.paddleHubManagedMonthlyPlanId }};
1113
const PADDLE_PRICES_URL = '{{ .Site.Params.paddlePricesUrl }}';
1214
const PADDLE_DISCOUNT_ID = '{{ .Site.Params.paddleDiscountId }}';
1315
const PADDLE_DISCOUNT_CODE = '{{ .Site.Params.paddleDiscountCode }}';

assets/js/hubpricing.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class HubPricing {
1111
url: PADDLE_PRICES_URL,
1212
dataType: 'jsonp',
1313
data: {
14-
product_ids: `${PADDLE_HUB_SELF_HOSTED_SUBSCRIPTION_PLAN_ID},${PADDLE_HUB_MANAGED_SUBSCRIPTION_PLAN_ID}`
14+
product_ids: `${PADDLE_HUB_SELF_HOSTED_YEARLY_PLAN_ID},${PADDLE_HUB_MANAGED_YEARLY_PLAN_ID}`
1515
},
1616
}).done(data => {
1717
this._data.selfHostedMonthlyPrice = {

assets/js/hubsubscription.js

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -161,56 +161,63 @@ class HubSubscription {
161161
loadPrice(continueHandler) {
162162
this._subscriptionData.inProgress = true;
163163
this._subscriptionData.errorMessage = '';
164-
let product_id;
165-
if (this._subscriptionData.customBilling?.managed) {
166-
product_id = PADDLE_HUB_MANAGED_SUBSCRIPTION_PLAN_ID;
167-
} else {
168-
product_id = PADDLE_HUB_SELF_HOSTED_SUBSCRIPTION_PLAN_ID;
169-
}
164+
let isManaged = this._subscriptionData.customBilling?.managed;
165+
let yearlyPlanId = isManaged ? PADDLE_HUB_MANAGED_YEARLY_PLAN_ID : PADDLE_HUB_SELF_HOSTED_YEARLY_PLAN_ID;
166+
let monthlyPlanId = isManaged ? PADDLE_HUB_MANAGED_MONTHLY_PLAN_ID : PADDLE_HUB_SELF_HOSTED_MONTHLY_PLAN_ID;
170167
$.ajax({
171168
url: PADDLE_PRICES_URL,
172169
dataType: 'jsonp',
173170
data: {
174-
product_ids: product_id
171+
product_ids: yearlyPlanId + ',' + monthlyPlanId
175172
},
176173
}).done(data => {
177-
this.onLoadPriceSucceeded(data);
174+
this.onLoadPriceSucceeded(data, yearlyPlanId, monthlyPlanId);
178175
continueHandler();
179176
}).fail(xhr => {
180177
this.onLoadPriceFailed(xhr.responseJSON?.message || 'Loading price failed.');
181178
});
182179
}
183180

184-
onLoadPriceSucceeded(data) {
185-
let product = data.response.products[0];
186-
let currency = product.currency;
187-
let netAmount;
188-
let recurringNetAmount;
189-
let grossAmount;
190-
let recurringGrossAmount;
191-
if (this._subscriptionData.customBilling?.override?.prices) {
192-
netAmount = this.getAmount(this._subscriptionData.customBilling.override.prices, currency) / 12;
193-
recurringNetAmount = this.getAmount(this._subscriptionData.customBilling.override.recurring_prices, currency) / 12;
194-
let taxRate = product.subscription.price.gross / product.subscription.price.net;
195-
grossAmount = netAmount * taxRate;
196-
recurringGrossAmount = recurringNetAmount * taxRate;
197-
} else {
198-
netAmount = product.subscription.price.net / 12;
199-
recurringNetAmount = netAmount;
200-
grossAmount = product.subscription.price.gross / 12;
201-
recurringGrossAmount = grossAmount;
202-
}
203-
this._subscriptionData.monthlyPrice = {
204-
netAmount: netAmount,
205-
recurringNetAmount: recurringNetAmount,
206-
grossAmount: grossAmount,
207-
recurringGrossAmount: recurringGrossAmount,
181+
onLoadPriceSucceeded(data, yearlyPlanId, monthlyPlanId) {
182+
let products = data.response.products;
183+
let yearlyProduct = products.find(p => p.product_id == yearlyPlanId);
184+
let monthlyProduct = products.find(p => p.product_id == monthlyPlanId);
185+
let yearlyPrice = yearlyProduct.subscription.price;
186+
let monthlyPrice = monthlyProduct.subscription.price;
187+
let currency = yearlyProduct.currency;
188+
this._subscriptionData.yearlyPlanPrice = this.calculateYearlyPlanPrice(yearlyPrice, currency);
189+
this._subscriptionData.monthlyPlanPrice = {
190+
netAmount: monthlyPrice.net,
191+
recurringNetAmount: monthlyPrice.net,
192+
grossAmount: monthlyPrice.gross,
193+
recurringGrossAmount: monthlyPrice.gross,
208194
currency: currency
209195
};
196+
this._subscriptionData.savingsPercent = Math.round((1 - yearlyPrice.net / (monthlyPrice.net * 12)) * 100);
210197
this._subscriptionData.errorMessage = '';
211198
this._subscriptionData.inProgress = false;
212199
}
213200

201+
calculateYearlyPlanPrice(yearlyPrice, currency) {
202+
let taxRate = yearlyPrice.gross / yearlyPrice.net;
203+
let customBillingOverride = this._subscriptionData.customBilling?.override;
204+
let customPriceAmount = customBillingOverride?.prices
205+
? this.getAmount(customBillingOverride.prices, currency)
206+
: null;
207+
let netAmount = (customPriceAmount ?? yearlyPrice.net) / 12;
208+
let customRecurringAmount = customBillingOverride?.recurring_prices
209+
? this.getAmount(customBillingOverride.recurring_prices, currency)
210+
: null;
211+
let recurringNetAmount = customRecurringAmount ? customRecurringAmount / 12 : netAmount;
212+
return {
213+
netAmount: netAmount,
214+
recurringNetAmount: recurringNetAmount,
215+
grossAmount: netAmount * taxRate,
216+
recurringGrossAmount: recurringNetAmount * taxRate,
217+
currency: currency
218+
};
219+
}
220+
214221
getAmount(prices, currency) {
215222
let regex = new RegExp(`^${currency}:`);
216223
let price = prices.find(price => regex.test(price));
@@ -231,11 +238,15 @@ class HubSubscription {
231238

232239
this._subscriptionData.inProgress = true;
233240
this._subscriptionData.errorMessage = '';
234-
if (this._subscriptionData.customBilling?.managed) {
235-
this.customCheckout(PADDLE_HUB_MANAGED_SUBSCRIPTION_PLAN_ID, locale);
241+
let isManaged = this._subscriptionData.customBilling?.managed;
242+
let isMonthly = this._subscriptionData.billingInterval === 'monthly';
243+
let planId;
244+
if (isManaged) {
245+
planId = isMonthly ? PADDLE_HUB_MANAGED_MONTHLY_PLAN_ID : PADDLE_HUB_MANAGED_YEARLY_PLAN_ID;
236246
} else {
237-
this.customCheckout(PADDLE_HUB_SELF_HOSTED_SUBSCRIPTION_PLAN_ID, locale);
247+
planId = isMonthly ? PADDLE_HUB_SELF_HOSTED_MONTHLY_PLAN_ID : PADDLE_HUB_SELF_HOSTED_YEARLY_PLAN_ID;
238248
}
249+
this.customCheckout(planId, locale);
239250
}
240251

241252
customCheckout(productId, locale) {

config/development/params.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ paddleDesktopPriceIds:
1414
- pri_01jb24e3z1zkzsq5yrhm5252bd
1515
paddleAndroidPriceId: pri_01jb24ewyc6fbhnzt26jdj9qp1
1616
paddleVendorId: 1385
17-
paddleHubSelfHostedSubscriptionPlanId: 23141
18-
paddleHubManagedSubscriptionPlanId: 42235
17+
paddleHubSelfHostedYearlyPlanId: 23141
18+
paddleHubSelfHostedMonthlyPlanId: 82378
19+
paddleHubManagedYearlyPlanId: 42235
20+
paddleHubManagedMonthlyPlanId: 82379
1921
paddlePricesUrl: https://sandbox-checkout.paddle.com/api/2.0/prices
2022
paddleDiscountId: dsc_01kba2drz2yg4v6h3d5f2687tw
2123
paddleDiscountCode: WINTER2025

config/production/params.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ paddleDesktopPriceIds:
1414
- pri_01jb23wh9vjqdskxyz96zhc4yk
1515
paddleAndroidPriceId: pri_01jb2432chrz583xtxb029g40m
1616
paddleVendorId: 39223
17-
paddleHubSelfHostedSubscriptionPlanId: 770132
18-
paddleHubManagedSubscriptionPlanId: 807339
17+
paddleHubSelfHostedYearlyPlanId: 770132
18+
paddleHubSelfHostedMonthlyPlanId: 914172
19+
paddleHubManagedYearlyPlanId: 807339
20+
paddleHubManagedMonthlyPlanId: 914173
1921
paddlePricesUrl: https://checkout.paddle.com/api/2.0/prices
2022
paddleDiscountId: dsc_01kba2vjmspm92sx2jhq4s2082
2123
paddleDiscountCode: WINTER2025

config/staging/params.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ paddleDesktopPriceIds:
1414
- pri_01jb24e3z1zkzsq5yrhm5252bd
1515
paddleAndroidPriceId: pri_01jb24ewyc6fbhnzt26jdj9qp1
1616
paddleVendorId: 1385
17-
paddleHubSelfHostedSubscriptionPlanId: 23141
18-
paddleHubManagedSubscriptionPlanId: 42235
17+
paddleHubSelfHostedYearlyPlanId: 23141
18+
paddleHubSelfHostedMonthlyPlanId: 82378
19+
paddleHubManagedYearlyPlanId: 42235
20+
paddleHubManagedMonthlyPlanId: 82379
1921
paddlePricesUrl: https://sandbox-checkout.paddle.com/api/2.0/prices
2022
paddleDiscountId: dsc_01kba2drz2yg4v6h3d5f2687tw
2123
paddleDiscountCode: WINTER2025

data/de/pricing_teams_faq.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
- Question: "Wie werden die Sitze berechnet?"
22
Answer: "Nur Nutzer, die Zugang zu Tresoren haben, werden auf die Anzahl der Sitze angerechnet. Du kannst zum Beispiel 10 Sitze kaufen und trotzdem deine Keycloak-Instanz mit einem Active Directory mit Tausenden von Nutzern verbinden. Jeder Nutzer mit Tresorzugang belegt einen Sitz, unabhängig von seiner Anzahl von Geräten."
33
- Question: "Wie wird die Abonnementgebühr für kostenpflichtige Pläne berechnet?"
4-
Answer: "Du zahlst eine jährliche Gebühr für jeden Sitz. Diese Gebühr gilt auch, wenn du ungenutzte Sitze hast."
4+
Answer: "Du kannst zwischen monatlicher oder jährlicher Abrechnung wählen. Bei jährlicher Abrechnung sparst du im Vergleich zur monatlichen Zahlung. Die Gebühr gilt für jeden Sitz, auch wenn einige Sitze ungenutzt sind. Wenn du deinen Abrechnungszeitraum nach dem Abschluss ändern möchtest, [kontaktiere bitte unser Vertriebsteam](/de/contact-sales/)."
55
- Question: "Kann ich während der Laufzeit weitere Sitze hinzufügen?"
66
Answer: "Ja, du kannst jederzeit weitere Sitze hinzufügen. Du kannst auch Sitze reduzieren, wenn du sie nicht mehr benötigst. Änderungen werden entweder sofort anteilig verrechnet oder im Folgejahr abgezogen."
77
- Question: "Sind in den kostenpflichtigen Plänen 5 freie Sitze enthalten?"

data/en/pricing_teams_faq.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
- Question: "How are seats calculated?"
22
Answer: "Only users who are granted access to vaults count towards the seats limit. For example, you can buy 10 seats and still connect your Keycloak instance to an Active Directory with thousands of users. Each user with vault access occupies one seat, regardless of their number of devices."
33
- Question: "How is the subscription fee calculated for paid plans?"
4-
Answer: "You will be charged a yearly fee for each seat. This fee applies even if you have unused seats."
4+
Answer: "You can choose between monthly or yearly billing. With yearly billing, you save compared to paying monthly. The fee applies to each seat, even if some seats are unused. If you need to change your billing interval after subscribing, please [contact our sales team](/contact-sales/)."
55
- Question: "Can I add more seats during the current billing cycle?"
66
Answer: "Yes, you can add more seats at any time. You can also reduce seats if you no longer need them. Changes are either prorated immediately or deducted in the following year."
77
- Question: "Do the paid plans include 5 free seats?"

i18n/de.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,14 @@
585585
translation: "Standard"
586586
- id: hub_billing_checkout_standard_description
587587
translation: "pro Sitz, pro Monat, jährlich abgerechnet"
588+
- id: hub_billing_checkout_standard_description_monthly
589+
translation: "pro Sitz, monatlich abgerechnet"
590+
- id: hub_billing_monthly
591+
translation: "Monatlich"
592+
- id: hub_billing_yearly
593+
translation: "Jährlich"
594+
- id: hub_billing_save
595+
translation: "Spare"
588596

589597
- id: hub_billing_checkout_standard_hubid
590598
translation: "Hub-ID"

i18n/en.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,14 @@
585585
translation: "Standard"
586586
- id: hub_billing_checkout_standard_description
587587
translation: "per seat, per month, billed yearly"
588+
- id: hub_billing_checkout_standard_description_monthly
589+
translation: "per seat, billed monthly"
590+
- id: hub_billing_monthly
591+
translation: "Monthly"
592+
- id: hub_billing_yearly
593+
translation: "Yearly"
594+
- id: hub_billing_save
595+
translation: "Save"
588596

589597
- id: hub_billing_checkout_standard_hubid
590598
translation: "Hub ID"

0 commit comments

Comments
 (0)