fix: bind HWID top-ups to subscription periods
This commit is contained in:
@@ -1240,6 +1240,7 @@
|
||||
stripTopupQueryFromUrl();
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
async function loadPublicInstall(shareToken) {
|
||||
|
||||
@@ -49,6 +49,8 @@ export function rowsFromPackages(packageSet, currency, valueKey) {
|
||||
return (packageSet?.[currency] || []).map((pkg) => ({
|
||||
[valueKey]: pkg[valueKey],
|
||||
price: pkg.price,
|
||||
prices: pkg.prices ? structuredCloneSafe(pkg.prices) : undefined,
|
||||
min_price: pkg.min_price ?? "",
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -115,10 +117,20 @@ export function compactMap(obj) {
|
||||
|
||||
export function packagesFromRows(rows, valueKey) {
|
||||
return (rows || [])
|
||||
.map((row) => ({
|
||||
[valueKey]: parseNumber(row[valueKey]),
|
||||
price: parseNumber(row.price),
|
||||
}))
|
||||
.map((row) => {
|
||||
const pkg = {
|
||||
[valueKey]: parseNumber(row[valueKey]),
|
||||
price: parseNumber(row.price),
|
||||
};
|
||||
if (row.prices && typeof row.prices === "object") {
|
||||
pkg.prices = structuredCloneSafe(row.prices);
|
||||
}
|
||||
const minPrice = parseNumber(row.min_price);
|
||||
if (minPrice !== null) {
|
||||
pkg.min_price = minPrice;
|
||||
}
|
||||
return pkg;
|
||||
})
|
||||
.filter((row) => row[valueKey] > 0 && row.price !== null && row.price >= 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ export function createBillingActions({ api }) {
|
||||
months: plan.device_count || plan.months,
|
||||
device_count: plan.device_count || plan.months,
|
||||
tariff_key: plan.tariff_key || fallbackTariffKey,
|
||||
sale_mode: "hwid_devices",
|
||||
sale_mode: plan.sale_mode || "hwid_devices",
|
||||
method,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -669,6 +669,10 @@ export function applyPreviewMock(kind) {
|
||||
tariff_key: "standard",
|
||||
tariff_name: "Стандарт",
|
||||
current_limit: 5,
|
||||
extra_hwid_devices: 2,
|
||||
extra_hwid_devices_valid_until_text: "01.06.2026 12:00",
|
||||
renewal_available: false,
|
||||
renewal_recommended_count: 0,
|
||||
plans: [
|
||||
{
|
||||
id: "standard:hwid:1",
|
||||
@@ -702,6 +706,8 @@ export function applyPreviewMock(kind) {
|
||||
...DEV_MOCK.data.subscription,
|
||||
active: true,
|
||||
max_devices: 5,
|
||||
extra_hwid_devices: 2,
|
||||
extra_hwid_devices_valid_until_text: "01.06.2026 12:00",
|
||||
};
|
||||
} else if (mode === "trial") {
|
||||
DEV_MOCK.data.settings.traffic_mode = false;
|
||||
|
||||
@@ -43,9 +43,14 @@ export function createBillingStore({
|
||||
|
||||
function isSubscriptionSale(plan) {
|
||||
const saleMode = String(plan?.sale_mode || "subscription").toLowerCase();
|
||||
return !["traffic", "traffic_package", "topup", "premium_topup", "hwid_devices"].includes(
|
||||
saleMode
|
||||
);
|
||||
return ![
|
||||
"traffic",
|
||||
"traffic_package",
|
||||
"topup",
|
||||
"premium_topup",
|
||||
"hwid_devices",
|
||||
"hwid_devices_renewal",
|
||||
].includes(saleMode);
|
||||
}
|
||||
|
||||
function paymentSuccessContext(s, response = {}) {
|
||||
@@ -53,6 +58,8 @@ export function createBillingStore({
|
||||
paymentId: response.payment_id || "",
|
||||
initialSubscriptionPayment:
|
||||
!s.paymentStartedWithActiveSubscription && isSubscriptionSale(s.selectedPlan),
|
||||
renewalSubscriptionPayment:
|
||||
s.paymentStartedWithActiveSubscription && isSubscriptionSale(s.selectedPlan),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,7 +71,15 @@ export function createBillingStore({
|
||||
paymentPollToken += 1;
|
||||
}
|
||||
showToast(t("wa_payment_success", {}, "Payment successful"));
|
||||
await loadData({ fresh: true });
|
||||
const payload = await loadData({ fresh: true });
|
||||
if (
|
||||
successContext.renewalSubscriptionPayment &&
|
||||
payload?.subscription?.device_topup_renewal_available &&
|
||||
payload?.subscription?.can_topup_devices
|
||||
) {
|
||||
showToast(t("wa_hwid_devices_renewal_prompt"));
|
||||
openDeviceTopupModal(payload.payment_methods?.[0]?.id || "");
|
||||
}
|
||||
if (
|
||||
successContext.initialSubscriptionPayment &&
|
||||
typeof onSubscriptionActivated === "function"
|
||||
@@ -190,6 +205,8 @@ export function createBillingStore({
|
||||
state.update((s) => ({
|
||||
...s,
|
||||
deviceTopupModalOpen: true,
|
||||
deviceTopupOptions: null,
|
||||
selectedDeviceTopupPlan: null,
|
||||
selectedMethod: s.selectedMethod || defaultMethod,
|
||||
}));
|
||||
loadDeviceTopupOptions();
|
||||
|
||||
@@ -591,6 +591,13 @@ a {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.devices-topup-validity {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.devices-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
|
||||
@@ -197,6 +197,16 @@
|
||||
<p>{subscriptionPurchaseDescription}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if subscription?.active && Number(subscription?.extra_hwid_devices || 0) > 0}
|
||||
<div class="subscription-purchase-description">
|
||||
<p>
|
||||
{t("wa_hwid_devices_renewal_notice", {
|
||||
count: Number(subscription.extra_hwid_devices || 0),
|
||||
date: subscription.extra_hwid_devices_valid_until_text || "",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="period-grid period-grid-two-columns">
|
||||
{#each selectedTariffPlans as plan}
|
||||
<button
|
||||
@@ -251,6 +261,16 @@
|
||||
<p>{subscriptionPurchaseDescription}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if subscription?.active && Number(subscription?.extra_hwid_devices || 0) > 0}
|
||||
<div class="subscription-purchase-description">
|
||||
<p>
|
||||
{t("wa_hwid_devices_renewal_notice", {
|
||||
count: Number(subscription.extra_hwid_devices || 0),
|
||||
date: subscription.extra_hwid_devices_valid_until_text || "",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="period-grid period-grid-two-columns">
|
||||
{#each plans as plan}
|
||||
<button
|
||||
|
||||
@@ -369,6 +369,25 @@
|
||||
{#if !deviceTopupOptions}
|
||||
<DialogOptionsSkeleton label={t("wa_tariff_options_loading")} rows={3} />
|
||||
{:else if deviceTopupOptions?.plans?.length}
|
||||
{#if deviceTopupOptions?.renewal_available}
|
||||
<div class="topup-carryover-note">
|
||||
<p>
|
||||
{t("wa_hwid_devices_renewal_offer", {
|
||||
count: Number(deviceTopupOptions.renewal_recommended_count || 0),
|
||||
date: deviceTopupOptions.extra_hwid_devices_valid_until_text || "",
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
{:else if Number(deviceTopupOptions?.extra_hwid_devices || 0) > 0 && deviceTopupOptions?.extra_hwid_devices_valid_until_text}
|
||||
<div class="topup-carryover-note">
|
||||
<p>
|
||||
{t("wa_hwid_devices_valid_until", {
|
||||
count: Number(deviceTopupOptions.extra_hwid_devices || 0),
|
||||
date: deviceTopupOptions.extra_hwid_devices_valid_until_text,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="option-list">
|
||||
{#each deviceTopupOptions.plans as plan}
|
||||
<button
|
||||
|
||||
@@ -46,6 +46,14 @@
|
||||
value={devicesPercent(devicesData)}
|
||||
label={t("wa_devices_title")}
|
||||
/>
|
||||
{#if Number(subscription?.extra_hwid_devices || 0) > 0 && subscription?.extra_hwid_devices_valid_until_text}
|
||||
<p class="devices-topup-validity">
|
||||
{t("wa_hwid_devices_valid_until", {
|
||||
count: Number(subscription.extra_hwid_devices || 0),
|
||||
date: subscription.extra_hwid_devices_valid_until_text,
|
||||
})}
|
||||
</p>
|
||||
{/if}
|
||||
{#if subscription?.active && subscription?.max_devices !== 0 && subscription?.can_topup_devices}
|
||||
<Button variant="secondary" class="wide" onclick={openDeviceTopupModal}>
|
||||
<Plus size={17} />
|
||||
|
||||
Reference in New Issue
Block a user