← Files WixARCHIVED FILE
skills/wix-headless-templates/pricing-plans/app/components/pricing-plans/SubscribeButton.tsx
2.15 KB · Oct 8, 2026 · 12:02 UTC
// The purchase control for one plan — wire as-is on every card, detail page, and home strip.
// It owns the rules a CTA gets wrong when rewritten: rendered only for a `buyable` plan (a PUBLIC
// plan can be merchant-assigned — `assignedText` shows instead, or nothing when null), the label
// from the DTO ("Get this plan" for a free plan, "Subscribe" otherwise, your children override it),
// "Redirecting…" while the hosted checkout is starting, the failure inline. The redirect itself is
// usePlanPurchase → purchasePlan; nothing here builds a URL or marks anything paid.
import type { ReactNode } from "react";
import { usePlanPurchase } from "../../hooks/pricing-plans/usePlanPurchase";
import type { PurchaseOptions } from "../../wix/pricing-plans/purchase";
import type { PlanSummary } from "../../wix/pricing-plans/types";
export interface SubscribeButtonProps {
plan: PlanSummary;
/** thankYouPageUrl / postFlowUrl for the hosted flow — see purchase.ts; omit for the defaults. */
options?: PurchaseOptions;
/** Replaces the default label ("Get this plan" / "Subscribe"). */
children?: ReactNode;
className?: string;
/** Shown for a plan that isn't buyable; null renders nothing (a grid card). */
assignedText?: string | null;
}
export default function SubscribeButton({
plan,
options,
children,
className = "rounded-control bg-primary px-6 py-2.5 text-sm font-semibold text-primary-foreground transition-opacity hover:opacity-90 disabled:opacity-50",
assignedText = "This plan is assigned by the site owner.",
}: SubscribeButtonProps) {
const { purchase, purchasingId, error } = usePlanPurchase();
if (!plan.buyable) return assignedText ? <p className="text-sm text-muted-foreground">{assignedText}</p> : null;
const purchasing = purchasingId === plan.id;
return (
<div className="grid gap-2">
<button type="button" disabled={purchasing} onClick={() => void purchase(plan.id, options).catch(() => {})} className={className}>
{purchasing ? "Redirecting…" : children ?? (plan.free ? "Get this plan" : "Subscribe")}
</button>
{error && <p className="text-sm text-destructive" role="alert">{error}</p>}
</div>
);
}
SHA-256: 9d5cc45bbb643b63682ef2268558e9b93c43844eedc1ae4bf188f06bcc01a094