fix: restrict telegram pricing to private group
This commit is contained in:
@@ -62,6 +62,10 @@ import {
|
|||||||
import { createAccountCopy } from "./account-copy";
|
import { createAccountCopy } from "./account-copy";
|
||||||
import { resetWalletConnectProvider } from "./wallet";
|
import { resetWalletConnectProvider } from "./wallet";
|
||||||
import { useAccountPayment } from "./useAccountPayment";
|
import { useAccountPayment } from "./useAccountPayment";
|
||||||
|
import {
|
||||||
|
isTelegramPrivateGroupPriceEligible,
|
||||||
|
telegramPrivateGroupAmountUsdc,
|
||||||
|
} from "./telegram-pricing";
|
||||||
|
|
||||||
// --- Main Component ---
|
// --- Main Component ---
|
||||||
|
|
||||||
@@ -432,6 +436,12 @@ export function AccountCenter() {
|
|||||||
{ plan_code: "pro_monthly", plan_id: 101, amount_usdc: "29.9", duration_days: 30 },
|
{ plan_code: "pro_monthly", plan_id: 101, amount_usdc: "29.9", duration_days: 30 },
|
||||||
{ plan_code: "pro_quarterly", plan_id: 102, amount_usdc: "79.9", duration_days: 90 },
|
{ plan_code: "pro_quarterly", plan_id: 102, amount_usdc: "79.9", duration_days: 90 },
|
||||||
];
|
];
|
||||||
|
const privateTelegramGroupPriceActive = isTelegramPrivateGroupPriceEligible(
|
||||||
|
backend?.telegram_pricing,
|
||||||
|
);
|
||||||
|
const privateTelegramGroupAmount = telegramPrivateGroupAmountUsdc(
|
||||||
|
backend?.telegram_pricing,
|
||||||
|
);
|
||||||
const referral = backend?.referral;
|
const referral = backend?.referral;
|
||||||
const referralCode = String(referral?.code || "").trim();
|
const referralCode = String(referral?.code || "").trim();
|
||||||
const appliedReferralCode = String(referral?.applied_code || "").trim();
|
const appliedReferralCode = String(referral?.applied_code || "").trim();
|
||||||
@@ -1094,6 +1104,13 @@ export function AccountCenter() {
|
|||||||
const code = String(plan.plan_code || "");
|
const code = String(plan.plan_code || "");
|
||||||
const active = code === selectedPlanCode;
|
const active = code === selectedPlanCode;
|
||||||
const isQuarterly = code === "pro_quarterly";
|
const isQuarterly = code === "pro_quarterly";
|
||||||
|
const isPrivateGroupMonthly = Boolean(
|
||||||
|
code === "pro_monthly" &&
|
||||||
|
privateTelegramGroupPriceActive &&
|
||||||
|
privateTelegramGroupAmount &&
|
||||||
|
Number(plan.amount_usdc) ===
|
||||||
|
Number(privateTelegramGroupAmount),
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -1108,7 +1125,11 @@ export function AccountCenter() {
|
|||||||
>
|
>
|
||||||
<div className="flex items-center justify-between gap-2 text-xs font-bold">
|
<div className="flex items-center justify-between gap-2 text-xs font-bold">
|
||||||
<span>
|
<span>
|
||||||
{isQuarterly ? copy.quarterlyPlan : copy.monthlyPlan}
|
{isQuarterly
|
||||||
|
? copy.quarterlyPlan
|
||||||
|
: isPrivateGroupMonthly
|
||||||
|
? copy.privateGroupMonthlyPlan
|
||||||
|
: copy.monthlyPlan}
|
||||||
</span>
|
</span>
|
||||||
<span>{plan.amount_usdc} USDC</span>
|
<span>{plan.amount_usdc} USDC</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ export function runTests() {
|
|||||||
path.join(projectRoot, "components", "account", "useBilling.ts"),
|
path.join(projectRoot, "components", "account", "useBilling.ts"),
|
||||||
"utf8",
|
"utf8",
|
||||||
);
|
);
|
||||||
|
const telegramPricing = fs.readFileSync(
|
||||||
|
path.join(projectRoot, "components", "account", "telegram-pricing.ts"),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
const types = fs.readFileSync(
|
const types = fs.readFileSync(
|
||||||
path.join(projectRoot, "components", "account", "types.ts"),
|
path.join(projectRoot, "components", "account", "types.ts"),
|
||||||
"utf8",
|
"utf8",
|
||||||
@@ -66,21 +70,33 @@ export function runTests() {
|
|||||||
assert(
|
assert(
|
||||||
useAccountPayment.includes("applyTelegramGroupPricingToPlanList") &&
|
useAccountPayment.includes("applyTelegramGroupPricingToPlanList") &&
|
||||||
useAccountPayment.includes("backend?.telegram_pricing") &&
|
useAccountPayment.includes("backend?.telegram_pricing") &&
|
||||||
|
useAccountPayment.includes("isTelegramPrivateGroupPriceEligible") &&
|
||||||
|
telegramPricing.includes("is_private_group_member") &&
|
||||||
|
telegramPricing.includes("telegram_private_group_member") &&
|
||||||
|
!telegramPricing.includes("is_group_member") &&
|
||||||
useAccountPayment.includes('=== "pro_monthly"') &&
|
useAccountPayment.includes('=== "pro_monthly"') &&
|
||||||
useAccountPayment.includes("amount_usdc: telegramAmountUsdc"),
|
useAccountPayment.includes("amount_usdc: telegramAmountUsdc"),
|
||||||
"account payment plan cards must display the 5 USDC Telegram group member monthly price after /bind verification",
|
"account payment plan cards must only display the 5 USDC private Telegram group monthly price after private /bind verification",
|
||||||
);
|
);
|
||||||
assert(
|
assert(
|
||||||
useBilling.includes("telegramGroupPriceApplies") &&
|
useBilling.includes("telegramGroupPriceApplies") &&
|
||||||
useBilling.includes("backend?.telegram_pricing?.is_group_member") &&
|
useBilling.includes("isTelegramPrivateGroupPriceEligible") &&
|
||||||
|
useBilling.includes("backend?.telegram_pricing") &&
|
||||||
useBilling.includes("!telegramGroupPriceApplies"),
|
useBilling.includes("!telegramGroupPriceApplies"),
|
||||||
"billing must not let referral first-month pricing override the lower Telegram group member monthly price",
|
"billing must not let referral first-month pricing override the lower private Telegram group monthly price",
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
accountCenter.includes("privateGroupMonthlyPlan") &&
|
||||||
|
accountCopy.includes("Private group monthly") &&
|
||||||
|
accountCopy.includes("私密群月付"),
|
||||||
|
"account plan card must label the 5 USDC price as a private Telegram group monthly price",
|
||||||
);
|
);
|
||||||
assert(
|
assert(
|
||||||
types.includes("ReferralSummary") &&
|
types.includes("ReferralSummary") &&
|
||||||
types.includes("referral?: ReferralSummary | null") &&
|
types.includes("referral?: ReferralSummary | null") &&
|
||||||
|
types.includes("is_private_group_member?: boolean") &&
|
||||||
types.includes("duration_days: number") &&
|
types.includes("duration_days: number") &&
|
||||||
types.includes("max_discount_usdc_by_plan"),
|
types.includes("max_discount_usdc_by_plan"),
|
||||||
"account auth and payment types must include referral summary and plan durations",
|
"account auth and payment types must include referral summary, private Telegram pricing, and plan durations",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export function createAccountCopy(isEn: boolean): Record<string, string> {
|
|||||||
paymentMgmt: isEn ? "Payment Management" : "支付管理",
|
paymentMgmt: isEn ? "Payment Management" : "支付管理",
|
||||||
proPlan: isEn ? "Pro Plan" : "Pro 套餐",
|
proPlan: isEn ? "Pro Plan" : "Pro 套餐",
|
||||||
monthlyPlan: isEn ? "Monthly" : "月付",
|
monthlyPlan: isEn ? "Monthly" : "月付",
|
||||||
|
privateGroupMonthlyPlan: isEn ? "Private group monthly" : "私密群月付",
|
||||||
quarterlyPlan: isEn ? "Quarterly" : "季度",
|
quarterlyPlan: isEn ? "Quarterly" : "季度",
|
||||||
trialBadge: isEn ? "3-day trial" : "3天试用",
|
trialBadge: isEn ? "3-day trial" : "3天试用",
|
||||||
trialPaidGroupLocked: isEn
|
trialPaidGroupLocked: isEn
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import type { TelegramPricing } from "./types";
|
||||||
|
|
||||||
|
export function isTelegramPrivateGroupPriceEligible(
|
||||||
|
pricing?: TelegramPricing | null,
|
||||||
|
) {
|
||||||
|
return Boolean(
|
||||||
|
pricing?.is_private_group_member ||
|
||||||
|
pricing?.pricing_source === "telegram_private_group_member",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function telegramPrivateGroupAmountUsdc(
|
||||||
|
pricing?: TelegramPricing | null,
|
||||||
|
) {
|
||||||
|
if (!isTelegramPrivateGroupPriceEligible(pricing)) return "";
|
||||||
|
const amount = String(pricing?.amount_usdc || "").trim();
|
||||||
|
const numeric = Number(amount);
|
||||||
|
return Number.isFinite(numeric) && numeric > 0 ? amount : "";
|
||||||
|
}
|
||||||
@@ -46,6 +46,7 @@ export type TelegramPricing = {
|
|||||||
telegram_id?: number | null;
|
telegram_id?: number | null;
|
||||||
telegram_status?: string | null;
|
telegram_status?: string | null;
|
||||||
is_group_member?: boolean;
|
is_group_member?: boolean;
|
||||||
|
is_private_group_member?: boolean;
|
||||||
amount_usdc?: string;
|
amount_usdc?: string;
|
||||||
pricing_source?: string;
|
pricing_source?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,20 +22,17 @@ import { usePaymentState } from "./usePaymentState";
|
|||||||
import { useWalletBind } from "./useWalletBind";
|
import { useWalletBind } from "./useWalletBind";
|
||||||
import { usePaymentFlow } from "./usePaymentFlow";
|
import { usePaymentFlow } from "./usePaymentFlow";
|
||||||
import { useBilling } from "./useBilling";
|
import { useBilling } from "./useBilling";
|
||||||
|
import {
|
||||||
// ============================================================
|
isTelegramPrivateGroupPriceEligible,
|
||||||
function telegramMemberAmountUsdc(pricing?: TelegramPricing | null) {
|
telegramPrivateGroupAmountUsdc,
|
||||||
if (!pricing?.is_group_member) return "";
|
} from "./telegram-pricing";
|
||||||
const amount = String(pricing.amount_usdc || "").trim();
|
|
||||||
const numeric = Number(amount);
|
|
||||||
return Number.isFinite(numeric) && numeric > 0 ? amount : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyTelegramGroupPricingToPlanList(
|
function applyTelegramGroupPricingToPlanList(
|
||||||
plans: PaymentPlan[],
|
plans: PaymentPlan[],
|
||||||
pricing?: TelegramPricing | null,
|
pricing?: TelegramPricing | null,
|
||||||
): PaymentPlan[] {
|
): PaymentPlan[] {
|
||||||
const telegramAmountUsdc = telegramMemberAmountUsdc(pricing);
|
if (!isTelegramPrivateGroupPriceEligible(pricing)) return plans;
|
||||||
|
const telegramAmountUsdc = telegramPrivateGroupAmountUsdc(pricing);
|
||||||
if (!telegramAmountUsdc) return plans;
|
if (!telegramAmountUsdc) return plans;
|
||||||
return plans.map((plan) =>
|
return plans.map((plan) =>
|
||||||
String(plan.plan_code || "").toLowerCase() === "pro_monthly"
|
String(plan.plan_code || "").toLowerCase() === "pro_monthly"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
} from "./constants";
|
} from "./constants";
|
||||||
import { clearStoredPaymentRecovery, shortAddress } from "./formatters";
|
import { clearStoredPaymentRecovery, shortAddress } from "./formatters";
|
||||||
import { normalizePaymentError } from "./payment-utils";
|
import { normalizePaymentError } from "./payment-utils";
|
||||||
|
import { isTelegramPrivateGroupPriceEligible } from "./telegram-pricing";
|
||||||
import { trackAppEvent } from "@/lib/app-analytics";
|
import { trackAppEvent } from "@/lib/app-analytics";
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -124,7 +125,7 @@ export function useBilling(params: UseBillingParams) {
|
|||||||
const selectedPlanCode = String(selectedPlan?.plan_code || "").toLowerCase();
|
const selectedPlanCode = String(selectedPlan?.plan_code || "").toLowerCase();
|
||||||
const telegramGroupPriceApplies = Boolean(
|
const telegramGroupPriceApplies = Boolean(
|
||||||
selectedPlanCode === "pro_monthly" &&
|
selectedPlanCode === "pro_monthly" &&
|
||||||
backend?.telegram_pricing?.is_group_member,
|
isTelegramPrivateGroupPriceEligible(backend?.telegram_pricing),
|
||||||
);
|
);
|
||||||
const referral = backend?.referral;
|
const referral = backend?.referral;
|
||||||
const referralPending = Boolean(
|
const referralPending = Boolean(
|
||||||
@@ -192,7 +193,7 @@ export function useBilling(params: UseBillingParams) {
|
|||||||
paymentConfig?.points_redemption,
|
paymentConfig?.points_redemption,
|
||||||
backend?.referral,
|
backend?.referral,
|
||||||
backend?.subscription_active,
|
backend?.subscription_active,
|
||||||
backend?.telegram_pricing?.is_group_member,
|
backend?.telegram_pricing,
|
||||||
selectedPlan?.plan_code,
|
selectedPlan?.plan_code,
|
||||||
selectedPlan?.amount_usdc,
|
selectedPlan?.amount_usdc,
|
||||||
totalPoints,
|
totalPoints,
|
||||||
@@ -405,8 +406,9 @@ export function useBilling(params: UseBillingParams) {
|
|||||||
throw new Error(copy.bindFailed.replace("{raw}", raw));
|
throw new Error(copy.bindFailed.replace("{raw}", raw));
|
||||||
}
|
}
|
||||||
const data = (await res.json()) as { telegram_pricing?: TelegramPricing | null };
|
const data = (await res.json()) as { telegram_pricing?: TelegramPricing | null };
|
||||||
if (data.telegram_pricing?.is_group_member) {
|
const telegramPricing = data.telegram_pricing;
|
||||||
const amount = data.telegram_pricing.amount_usdc || "10";
|
if (isTelegramPrivateGroupPriceEligible(telegramPricing)) {
|
||||||
|
const amount = telegramPricing?.amount_usdc || "10";
|
||||||
setPaymentInfo(copy.telegramVerifySuccess.replace("{amount}", amount));
|
setPaymentInfo(copy.telegramVerifySuccess.replace("{amount}", amount));
|
||||||
}
|
}
|
||||||
await loadSnapshot();
|
await loadSnapshot();
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ class TelegramGroupPricing:
|
|||||||
os.getenv("TELEGRAM_CHAT_IDS"),
|
os.getenv("TELEGRAM_CHAT_IDS"),
|
||||||
os.getenv("TELEGRAM_CHAT_ID"),
|
os.getenv("TELEGRAM_CHAT_ID"),
|
||||||
)
|
)
|
||||||
|
self.private_group_chat_ids = dedicated_group_chat_ids
|
||||||
self.group_chat_ids = dedicated_group_chat_ids or fallback_group_chat_ids
|
self.group_chat_ids = dedicated_group_chat_ids or fallback_group_chat_ids
|
||||||
self.member_price = _decimal_env("POLYWEATHER_GROUP_MEMBER_PRICE_USDC", "5")
|
self.member_price = _decimal_env("POLYWEATHER_GROUP_MEMBER_PRICE_USDC", "5")
|
||||||
self.public_price = _decimal_env("POLYWEATHER_PUBLIC_PRICE_USDC", "10")
|
self.public_price = _decimal_env("POLYWEATHER_PUBLIC_PRICE_USDC", "10")
|
||||||
@@ -139,12 +140,18 @@ class TelegramGroupPricing:
|
|||||||
def resolve_price_for_telegram_id(self, telegram_id: Optional[int]) -> Dict[str, Any]:
|
def resolve_price_for_telegram_id(self, telegram_id: Optional[int]) -> Dict[str, Any]:
|
||||||
status = self.get_member_status(int(telegram_id or 0)) if telegram_id else None
|
status = self.get_member_status(int(telegram_id or 0)) if telegram_id else None
|
||||||
is_member = bool(status in TELEGRAM_MEMBER_STATUSES)
|
is_member = bool(status in TELEGRAM_MEMBER_STATUSES)
|
||||||
amount = self.member_price if is_member else self.public_price
|
is_private_group_member = bool(self.private_group_chat_ids and is_member)
|
||||||
|
amount = self.member_price if is_private_group_member else self.public_price
|
||||||
return {
|
return {
|
||||||
"configured": self.configured,
|
"configured": self.configured,
|
||||||
"telegram_id": int(telegram_id or 0) or None,
|
"telegram_id": int(telegram_id or 0) or None,
|
||||||
"telegram_status": status,
|
"telegram_status": status,
|
||||||
"is_group_member": is_member,
|
"is_group_member": is_member,
|
||||||
|
"is_private_group_member": is_private_group_member,
|
||||||
"amount_usdc": _format_decimal(amount),
|
"amount_usdc": _format_decimal(amount),
|
||||||
"pricing_source": "telegram_group_member" if is_member else "telegram_public",
|
"pricing_source": (
|
||||||
|
"telegram_private_group_member"
|
||||||
|
if is_private_group_member
|
||||||
|
else "telegram_public"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1696,7 +1696,7 @@ class PaymentContractCheckoutService:
|
|||||||
except Exception:
|
except Exception:
|
||||||
telegram_id = None
|
telegram_id = None
|
||||||
price_payload = pricing.resolve_price_for_telegram_id(telegram_id)
|
price_payload = pricing.resolve_price_for_telegram_id(telegram_id)
|
||||||
if not bool(price_payload.get("is_group_member")):
|
if not bool(price_payload.get("is_private_group_member")):
|
||||||
return out
|
return out
|
||||||
amount_dec = _parse_decimal(
|
amount_dec = _parse_decimal(
|
||||||
price_payload.get("amount_usdc"), out["amount_usdc_decimal"]
|
price_payload.get("amount_usdc"), out["amount_usdc_decimal"]
|
||||||
|
|||||||
@@ -74,8 +74,10 @@ def test_group_pricing_treats_member_status_as_group_member(monkeypatch):
|
|||||||
result = pricing.resolve_price_for_telegram_id(12345)
|
result = pricing.resolve_price_for_telegram_id(12345)
|
||||||
|
|
||||||
assert result["is_group_member"] is True
|
assert result["is_group_member"] is True
|
||||||
|
assert result["is_private_group_member"] is True
|
||||||
assert result["amount_usdc"] == "5"
|
assert result["amount_usdc"] == "5"
|
||||||
assert result["telegram_status"] == "member"
|
assert result["telegram_status"] == "member"
|
||||||
|
assert result["pricing_source"] == "telegram_private_group_member"
|
||||||
|
|
||||||
|
|
||||||
def test_group_member_price_defaults_to_five_usdc(monkeypatch):
|
def test_group_member_price_defaults_to_five_usdc(monkeypatch):
|
||||||
@@ -99,6 +101,7 @@ def test_group_member_price_defaults_to_five_usdc(monkeypatch):
|
|||||||
result = pricing.resolve_price_for_telegram_id(12345)
|
result = pricing.resolve_price_for_telegram_id(12345)
|
||||||
|
|
||||||
assert result["is_group_member"] is True
|
assert result["is_group_member"] is True
|
||||||
|
assert result["is_private_group_member"] is True
|
||||||
assert result["amount_usdc"] == "5"
|
assert result["amount_usdc"] == "5"
|
||||||
|
|
||||||
|
|
||||||
@@ -124,6 +127,7 @@ def test_group_pricing_treats_left_status_as_public_price(monkeypatch):
|
|||||||
result = pricing.resolve_price_for_telegram_id(12345)
|
result = pricing.resolve_price_for_telegram_id(12345)
|
||||||
|
|
||||||
assert result["is_group_member"] is False
|
assert result["is_group_member"] is False
|
||||||
|
assert result["is_private_group_member"] is False
|
||||||
assert result["amount_usdc"] == "10"
|
assert result["amount_usdc"] == "10"
|
||||||
assert result["telegram_status"] == "left"
|
assert result["telegram_status"] == "left"
|
||||||
|
|
||||||
@@ -155,6 +159,43 @@ def test_payment_plan_uses_linked_telegram_group_price(monkeypatch, tmp_path):
|
|||||||
service = PaymentContractCheckoutService()
|
service = PaymentContractCheckoutService()
|
||||||
service._db.bind_supabase_identity(12345, "user-1", "u@example.com")
|
service._db.bind_supabase_identity(12345, "user-1", "u@example.com")
|
||||||
|
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"src.auth.telegram_group_pricing.TelegramGroupPricing.resolve_price_for_telegram_id",
|
||||||
|
lambda self, telegram_id: {
|
||||||
|
"configured": True,
|
||||||
|
"telegram_id": telegram_id,
|
||||||
|
"is_group_member": True,
|
||||||
|
"is_private_group_member": True,
|
||||||
|
"telegram_status": "member",
|
||||||
|
"amount_usdc": "5",
|
||||||
|
"pricing_source": "telegram_private_group_member",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
plan = service._select_plan("pro_monthly")
|
||||||
|
priced = service._apply_telegram_group_pricing("user-1", plan)
|
||||||
|
|
||||||
|
assert priced["amount_usdc"] == "5"
|
||||||
|
assert priced["amount_usdc_decimal"] == Decimal("5")
|
||||||
|
assert priced["telegram_pricing"]["pricing_source"] == "telegram_private_group_member"
|
||||||
|
|
||||||
|
|
||||||
|
def test_payment_plan_ignores_legacy_group_member_without_private_group_flag(monkeypatch, tmp_path):
|
||||||
|
monkeypatch.setenv("POLYWEATHER_PAYMENT_ENABLED", "true")
|
||||||
|
monkeypatch.setenv("SUPABASE_URL", "https://example.supabase.co")
|
||||||
|
monkeypatch.setenv("SUPABASE_SERVICE_ROLE_KEY", "service-role")
|
||||||
|
monkeypatch.setenv("POLYWEATHER_PAYMENT_RPC_URL", "https://rpc-1.example")
|
||||||
|
monkeypatch.setenv("POLYWEATHER_DB_PATH", str(tmp_path / "payments.db"))
|
||||||
|
monkeypatch.setenv("TELEGRAM_BOT_TOKEN", "bot-token")
|
||||||
|
monkeypatch.setenv("POLYWEATHER_TELEGRAM_GROUP_ID", "-100123")
|
||||||
|
monkeypatch.setenv("POLYWEATHER_GROUP_MEMBER_PRICE_USDC", "5")
|
||||||
|
monkeypatch.setenv(
|
||||||
|
"POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON",
|
||||||
|
'[{"code":"usdc_e","address":"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174","decimals":6,"receiver_contract":"0xeD2f13Aa5fF033c58FB436E178451Cd07f693f32","is_default":true}]',
|
||||||
|
)
|
||||||
|
service = PaymentContractCheckoutService()
|
||||||
|
service._db.bind_supabase_identity(12345, "user-1", "u@example.com")
|
||||||
|
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"src.auth.telegram_group_pricing.TelegramGroupPricing.resolve_price_for_telegram_id",
|
"src.auth.telegram_group_pricing.TelegramGroupPricing.resolve_price_for_telegram_id",
|
||||||
lambda self, telegram_id: {
|
lambda self, telegram_id: {
|
||||||
@@ -170,9 +211,9 @@ def test_payment_plan_uses_linked_telegram_group_price(monkeypatch, tmp_path):
|
|||||||
plan = service._select_plan("pro_monthly")
|
plan = service._select_plan("pro_monthly")
|
||||||
priced = service._apply_telegram_group_pricing("user-1", plan)
|
priced = service._apply_telegram_group_pricing("user-1", plan)
|
||||||
|
|
||||||
assert priced["amount_usdc"] == "5"
|
assert priced["amount_usdc"] == "29.9"
|
||||||
assert priced["amount_usdc_decimal"] == Decimal("5")
|
assert priced["amount_usdc_decimal"] == Decimal("29.9")
|
||||||
assert priced["telegram_pricing"]["pricing_source"] == "telegram_group_member"
|
assert "telegram_pricing" not in priced
|
||||||
|
|
||||||
|
|
||||||
def test_payment_telegram_pricing_enabled_by_default_when_configured(monkeypatch, tmp_path):
|
def test_payment_telegram_pricing_enabled_by_default_when_configured(monkeypatch, tmp_path):
|
||||||
|
|||||||
Reference in New Issue
Block a user