feat: guide users after subscription activation
This commit is contained in:
@@ -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"));
|
||||
|
||||
Reference in New Issue
Block a user