feat: guide users after subscription activation

This commit is contained in:
3252a8
2026-05-24 22:24:02 +03:00
parent 1165dc3fa6
commit a60de46173
6 changed files with 178 additions and 24 deletions
+53 -10
View File
@@ -1,12 +1,21 @@
import { writable, get } from "svelte/store";
export function createBillingStore({ billing, loadData, t, showToast, openExternalLink, tg }) {
export function createBillingStore({
billing,
loadData,
t,
showToast,
openExternalLink,
onSubscriptionActivated = null,
tg,
}) {
const state = writable({
paymentModalOpen: false,
paymentStep: "tariff",
selectedTariffKey: "",
selectedPlan: null,
selectedMethod: "",
paymentStartedWithActiveSubscription: false,
topupModalOpen: false,
topupKind: "regular",
deviceTopupModalOpen: false,
@@ -25,11 +34,44 @@ export function createBillingStore({ billing, loadData, t, showToast, openExtern
let topupOptionsRequestId = 0;
let paymentPollToken = 0;
const successfulPaymentIds = new Set();
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function isSubscriptionSale(plan) {
const saleMode = String(plan?.sale_mode || "subscription").toLowerCase();
return !["traffic", "traffic_package", "topup", "premium_topup", "hwid_devices"].includes(
saleMode
);
}
function paymentSuccessContext(s, response = {}) {
return {
paymentId: response.payment_id || "",
initialSubscriptionPayment:
!s.paymentStartedWithActiveSubscription && isSubscriptionSale(s.selectedPlan),
};
}
async function handlePaymentSuccess(successContext = {}) {
const paymentId = String(successContext.paymentId || "");
if (paymentId && successfulPaymentIds.has(paymentId)) return;
if (paymentId) {
successfulPaymentIds.add(paymentId);
paymentPollToken += 1;
}
showToast(t("wa_payment_success", {}, "Payment successful"));
await loadData();
if (
successContext.initialSubscriptionPayment &&
typeof onSubscriptionActivated === "function"
) {
await onSubscriptionActivated();
}
}
function openPaymentModal(
tariffMode,
singleTariffMode,
@@ -71,6 +113,7 @@ export function createBillingStore({ billing, loadData, t, showToast, openExtern
selectedTariffKey: tariffKey,
selectedPlan: plan,
selectedMethod: s.selectedMethod || defaultMethod,
paymentStartedWithActiveSubscription: Boolean(subscription?.active),
};
});
}
@@ -164,13 +207,12 @@ export function createBillingStore({ billing, loadData, t, showToast, openExtern
state.update((s) => ({ ...s, changeConfirmOpen: false }));
}
function openTelegramInvoice(url) {
function openTelegramInvoice(url, successContext = {}) {
if (!url) return;
if (tg?.openInvoice) {
tg.openInvoice(url, (status) => {
tg.openInvoice(url, async (status) => {
if (status === "paid") {
showToast(t("wa_payment_success", {}, "Payment successful"));
loadData();
await handlePaymentSuccess(successContext);
} else if (status === "failed") {
showToast(t("wa_payment_create_failed"));
}
@@ -180,7 +222,7 @@ export function createBillingStore({ billing, loadData, t, showToast, openExtern
openExternalLink(url);
}
function startPaymentStatusPolling(paymentId) {
function startPaymentStatusPolling(paymentId, successContext = {}) {
if (!paymentId || !billing.fetchPaymentStatus) return;
const token = ++paymentPollToken;
void (async () => {
@@ -191,8 +233,7 @@ export function createBillingStore({ billing, loadData, t, showToast, openExtern
const status = await billing.fetchPaymentStatus(paymentId);
if (!status?.ok) continue;
if (status.paid || status.status === "succeeded") {
showToast(t("wa_payment_success", {}, "Payment successful"));
await loadData();
await handlePaymentSuccess({ ...successContext, paymentId });
return;
}
const normalized = String(status.status || "").toLowerCase();
@@ -222,17 +263,19 @@ export function createBillingStore({ billing, loadData, t, showToast, openExtern
);
if (!response.ok) throw response;
showToast(t("wa_payment_created"));
const successContext = paymentSuccessContext(s, response);
if (response.action === "open_invoice") {
if (!response.payment_url) throw response;
openTelegramInvoice(response.payment_url);
openTelegramInvoice(response.payment_url, successContext);
} else if (response.action === "invoice_sent") {
startPaymentStatusPolling(response.payment_id, successContext);
state.update((s) => ({ ...s, paymentModalOpen: false }));
return;
} else {
if (!response.payment_url) throw response;
openExternalLink(response.payment_url);
}
startPaymentStatusPolling(response.payment_id);
startPaymentStatusPolling(response.payment_id, successContext);
state.update((s) => ({ ...s, paymentModalOpen: false }));
} catch (error) {
showToast(error?.message || t("wa_payment_create_failed"));