算法艺术创作
algorithmic-art
通过代码和可调参数创作生成艺术与交互式视觉效果。
paywall-upgrade-cro【设计与创意】Designs and audits the moments inside a product where an account is asked to pay or to move to a higher plan: feature gates, usage-limit screens, trial-ending and trial-expired states, seat and add-on upsells. Maps every upgrade moment, decides what blocks and what only warns, specifies each screen with the exact charge, wires checkout and entitlemen…
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
An upgrade prompt is shown to someone who is already using the product. That is its advantage over a pricing page: the product knows what the person just tried to do, how much they have used and whether they are allowed to buy. A good prompt uses those three facts, states the exact charge, and lets the person decline and carry on. A bad one interrupts work, hides the price or the exit, and gets paid for in refunds, cancellations and store rejections.
This skill produces an upgrade map (every moment where payment is asked for), a specification for each screen, the billing and entitlement wiring, and a measurement plan with guardrails.
Read .claude/product-marketing-context.md if present. Then search the code before asking:
grep -rnEi "isPro|isPaid|plan ?===|entitlement|hasFeature|canUse|quota|limitReached|upgrade" src/ app/ 2>/dev/null | head -60
grep -rnEi "checkout\.sessions|billing_portal|trial_end|trial_period_days|StoreKit|BillingClient" . --include=*.ts --include=*.js --include=*.swift --include=*.kt 2>/dev/null | head -30
From the user, get what the code does not show:
List every place where the product asks for money. One row per moment:
| Moment | Started by | Default treatment |
|---|---|---|
| Locked feature clicked | The person | Dialog over the screen they are on, with a preview of the result where it is cheap to make |
| Limit approaching (80% or more used) | The product | Inline notice next to the meter; no dialog |
| Limit reached | The person (tries to add one more) | Block only the creation; existing work stays readable and exportable; offer the free way out (archive, delete) beside the upgrade |
| Trial ending | The product | Banner with the date and what will change, from the day the billing reminder goes out |
| Trial ended without payment | The product | Reduced plan or read-only state with export; never a locked door in front of their data |
| More seats or an add-on needed | The person | In the flow (invite form, settings), showing the prorated charge |
| Ambient cue | Nobody | Plan badge on locked items, upgrade entry in the account menu |
const DAY = 86400000;
// State of a metered limit. `used` and `limit` are counts for the whole account.
export function limitState(used, limit) {
if (!Number.isFinite(limit)) return 'none';
if (used >= limit) return 'reached'; // block creating one more; never block reading or export
if (used / limit >= 0.8) return 'warning'; // inline notice, no dialog
return 'none';
}
// May a prompt that the product starts on its own appear now?
// A prompt the person asked for (a click on a locked control) is always allowed.
export function mayPrompt({ startedByUser, shownThisSession, lastDismissedAt, now = Date.now() }) {
if (startedByUser) return true;
if (shownThisSession) return false;
return !lastDismissedAt || now - lastDismissedAt > 7 * DAY;
}
// Only people who can pay see a checkout button.
export function upgradeAction(role) {
return role === 'owner' || role === 'admin' ? 'checkout' : 'ask_admin';
}
Further rules:
Every upgrade screen carries these parts, in this order:
| Part | Content | Check |
|---|---|---|
| Title | What the person tried to do or is about to lose | Names the feature or limit, not the plan |
| Reason | The usage fact that caused the prompt | A real number from their account |
| What changes | Two to four differences, the one they hit first | No full comparison table inside a dialog |
| Charge | Amount, period, tax note, date of the next charge; for a mid-cycle upgrade the prorated amount due today | The amount billed is the largest price on the screen |
| Primary action | Verb, plan and price | Leads to payment in one step |
| Decline | Plain text button ("Not now", "Stay on Free") | Visible without scrolling; Escape works |
| Terms | Trial length, what is charged after it, how to cancel | Present whenever a trial or renewal applies |
Build the dialog itself to the modal dialog pattern (name, focus, Escape, inert background); the popup-cro skill has the markup. Social proof belongs here only when it is true and attributable: US rule 16 CFR 465 prohibits invented testimonials.
For Stripe-billed products:
subscription mode with the price already chosen and a
success_url back to the blocked action.NOW=$(date +%s)
curl https://api.stripe.com/v1/invoices/create_preview \
-u "$STRIPE_SECRET_KEY:" \
-d customer=cus_R4tZ81kLmQx2Vn \
-d subscription=sub_1QnV7tKX2a9LpGdE \
-d "subscription_details[items][0][id]"=si_Rb2HqY7sT0wMcA \
-d "subscription_details[items][0][price]"=price_1QnVf2KX2a9LpGdEo4sHq7Lw \
-d "subscription_details[proration_date]"=$NOW
curl https://api.stripe.com/v1/subscriptions/sub_1QnV7tKX2a9LpGdE \
-u "$STRIPE_SECRET_KEY:" \
-d "items[0][id]"=si_Rb2HqY7sT0wMcA \
-d "items[0][price]"=price_1QnVf2KX2a9LpGdEo4sHq7Lw \
-d "items[0][quantity]"=6 \
-d proration_behavior=always_invoice \
-d proration_date=$NOW
Two details from Stripe's documentation that cause real bugs: the update must name the existing
subscription item, or the new price is added beside the old one and both are billed; and changing
the price resets quantity to 1 unless the request repeats it.
customer.subscription.updated, or entitlements.active_entitlement_summary.updated when
Stripe Entitlements is used). Unlock in the interface as soon as payment confirms; do not make
the buyer reload.customer.subscription.trial_will_end arrives three days before the end by default.
Stripe-hosted reminder emails, when enabled, go out seven days before. Choose what happens to a
trial without a card through trial_settings[end_behavior][missing_payment_method]: cancel,
pause or create_invoice.| Source | Requirement for the purchase screen |
|---|---|
| Apple, subscriptions guidance and guideline 3.1.2 | Subscription name, duration and what it provides; full renewal price shown clearly; a way to sign in or restore purchases; links to terms and privacy policy; the amount billed is the most prominent price, a per-month breakdown of an annual plan stays subordinate; trial length and the price after it |
| Google Play subscriptions policy | Offer terms, cost, billing frequency and auto-renewal disclosed without extra taps; a clearly visible dismiss button; annual price not led by its monthly equivalent; localised price; how to cancel a trial; an online cancel method reachable from the app |
| US, ROSCA (15 U.S.C. 8403) | Material terms disclosed clearly before billing details are taken; express informed consent to the charge; a simple way to stop recurring charges |
| California, Bus. & Prof. Code 17602 | Cancelling online when the purchase was online; a retention offer may be shown only together with a direct "click to cancel" link; 7 to 30 days' notice before a fee change |
The FTC's 2024 "click-to-cancel" amendments were vacated by a federal appeals court in July 2025 and the agency restarted the rulemaking in 2026; ROSCA and state laws apply in the meantime. The EU requires an online withdrawal function for distance contracts from 19 June 2026. Flag these to the user as items for their counsel, not as legal advice.
Events, each with paywall_id, moment, plan_shown, role: paywall_view, paywall_dismiss,
upgrade_request_sent, checkout_start, purchase.
Return the upgrade map, one specification block per screen in the form shown in Example 1, the billing changes, the event list, and the first experiment with its guardrails.
Request: "Notchboard has 30,000 free accounts and 1.4% upgrade to Pro at $12 a month. The gate on SVG export just says 'Upgrade to Pro'. Improve it."
The agent finds in the code that the gate fires for every role and that free accounts have a median of 7 boards before first clicking export, so activation is not the problem. Specification:
Paywall: export-svg
Moment: locked feature clicked (started by the person) - always shown
Title: Export this board as SVG
Reason: "SVG and PDF export are part of Pro. PNG export stays free."
Preview: the first frame of their own board rendered as SVG, watermarked
What changes: SVG and PDF export / unlimited boards (you have 7 of 10) / brand colours
Charge: "$12 per month, billed monthly. Cancel any time in Settings > Billing."
Toggle: "$120 per year" with "equals $10 a month" in smaller type
Primary: "Upgrade to Pro - $12 a month" -> Checkout Session, success_url returns to the board
and starts the SVG download
Decline: "Export as PNG instead"
Members: "Ask an admin to upgrade" (sends the request with the board link)
Events: paywall_view, paywall_dismiss, upgrade_request_sent, checkout_start, purchase
Test: preview versus no preview, by account, 50/50; guardrail: refunds within 14 days
Request: "Fieldnest gives a 14-day trial without a card. 38% of trials are active in week two, 7% pay ($79 a month). On day 15 we lock the account."
Findings and plan:
| Day | Surface | Content |
|---|---|---|
| 7 | Email (Stripe-hosted reminder) | Trial end date, price after trial, link to add a card |
| 11 | In-app banner, all roles | "Trial ends on 14 October. 46 jobs scheduled so far." Owner sees "Add payment details", members see "Tell your admin" |
| 14 | Dialog for the owner on sign-in, once | What stays and what stops, the charge ("$79 on 14 October, then monthly"), decline: "Decide later" |
| 15 onward | Read-only workspace, missing_payment_method=pause | Schedules visible and exportable, no new jobs; one button resumes the subscription and restores editing |
The agent replaces the lockout with the paused, read-only state, because locked-out trial accounts cannot see the work that would justify paying, and adds a guardrail: share of paused accounts that resume within 30 days. It also notes that a discount for inactive trials would train buyers to wait, and recommends an extension on request instead.