release: promote development → main (2026-06-20T14:07:32Z) - #103
Conversation
…et redesign
SDK (cmp-paycraft 2.0.10):
- `Mode` enum derived from `apiKey` prefix (pk_test_*/pk_live_*); replaces
the device-id-based Testing Trial model. `BillingPlan.currency` field
threaded through. `SuiteProviderAdapter.getCheckoutUrl` looks up
`bySku[plan.id]?[plan.currency]` with USD fallback (no cross-mode fallback).
- `PayCraftPaywallViewModel` resets `isSubmitting=false` after the browser
intent fires — fixes the stuck Continue spinner on return-to-app.
- Provider bottom sheet redesigned: title + selected-plan summary line +
brand-coloured tiles (Stripe purple, Razorpay blue) + Recommended-for-you
badge keyed off `plan.currency` (INR → Razorpay, otherwise → Stripe) +
payment-method chips (UPI/Cards/Wallets/Net Banking/EMI for Razorpay;
Visa/Mastercard/Amex/Apple Pay/Google Pay/Link for Stripe) + secure
footer. Real brand-mark ImageVectors for Stripe/Razorpay/PayPal embedded
inline (simpleicons SVG paths) — no compose-resources dependency.
Schema (migration 070):
- New `tenant_providers_merge_payment_links(tenant, provider, mode, sku,
links)` RPC writes nested `{sku: {currency: url}}`. The legacy flat
`tenant_providers_set_payment_links` JSONB-concatenated at the top level,
meaning product N's currency URLs overwrote product N-1's — root cause
of "all 4 plans open the same Stripe page" symptom.
- Old RPC kept under DEPRECATED comment for 30-day grace.
Cleanup (migration 069):
- Drop `test_devices` table + `is_test_only` column + 4 device-registry
RPCs. Test-only product visibility is now driven by Stripe-style mode
duality (Test/Live toggle on dashboard + key-prefix on the SDK), not a
per-device allowlist. Dashboard pages (test-devices/, settings link,
product-form visibility checkbox) removed.
Dashboard:
- `mode.ts` + `mode-toggle.tsx` + `TestModeBanner` — Stripe-style Test/Live
toggle in the dashboard header. Cookie `pc_mode` (365d) drives server-side
`getMode()`; amber pinstripe banner in Test mode.
- Sync helpers (`stripe-route-helper.ts`) thread `body.sku` into the merge
RPC so multi-product tenants don't clobber each other's currency entries.
- `/api/pricing` POST now auto-fires `stripeSyncProduct +
razorpaySyncProduct + cashfreeSyncProduct` after writing pricing rows —
any price edit materializes new Stripe Prices + Payment Links
automatically, eliminating manual Sync clicks for price changes.
Edge Function `/config`:
- Filter providers by locale + non-empty nested-shape links AND filter by
`is_active=true`. Was the root cause of "Providers=0 on US locale"
(Stripe's `supported_locales: ["IN"]` was filtering it out; multi-tenant
test_payment_links were empty objects on top-level keys).
Maestro:
- `.maestro/paywall-stripe-checkout-e2e.yaml` and `provider-sheet-demo.yaml`
cover the full happy path (Settings → Get Premium → 4 plans (no Testing
Trial) → Continue → provider sheet (Stripe + Razorpay) → checkout page
with email field). Both green on Q4CIZHA6Y5GMJV89 (OnePlus CPH2423).
…testPaymentLinksBySku
- Update 5 JSON test fixtures (ConfigClientTest + SelfHostedBackendIntegrationTest +
3 in TierGateE2ETest) to nested test_payment_links shape so kotlinx.serialization
parses them into the new Map<sku, Map<currency, url>> ProviderDto field.
- Add DeviceFingerprint.js.kt actual that was missing for the jsMain target —
Kotlin/JS bundle was failing on `expected DeviceFingerprint has no actual
declaration ... for JS` (pre-existing on dev branch since the JS target was
added without backfilling this actual). Mirrors the wasmJs shape but uses
the js("...") source-literal pattern matching other jsMain actuals.
…WYG iframe foundation Sub-plan 01 of 3 in the paycraft-paywall-v2-production-ui epic. Foundation for the v2 paywall — expands tenant_paywall by 11 content fields, surfaces them in the dashboard's Paywall designer, and replaces the React mockup LIVE PREVIEW with a true-WYSIWYG iframe of the cmp-paycraft Kotlin/JS bundle. Sub-plan 02 (SDK BrandedStackTemplate + PaywallDesignToken + 2.1.0) and sub-plan 03 (PayCraftPremiumBanner + RestoreSheet + CheckoutSuccessSheet) build on this foundation in follow-up PRs. Migration 071 (idempotent, deployed): - 11 new tenant_paywall columns: hero_title/hero_subtitle/value_props/ cta_continue/cta_get_premium/restore_label/terms_url/privacy_url/ popular_plan_sku/success_title/success_message/success_cta_label/ hero_icon_svg/hero_icon_url. All NOT NULL with defaults matching reels-downloader's existing strings.xml premium-banner copy so consumer dropping in PayCraftPremiumBanner() with no overrides gets identical look. - sanitize_paywall_svg() helper rejects <script>, <foreignObject>, and external URL refs server-side via check_violation exception. - tenant_paywall_upsert RPC replaced (CREATE OR REPLACE) with v2-aware signature handling every new field + SVG sanitization. Backward-compatible: v1 payloads (missing v2 fields) fall through to column defaults via COALESCE. Edge Function /config (deployed): - v2 paywall block surfaces all 14 columns via existing spread; tested live: hero_title/hero_subtitle/value_props/cta_continue/cta_get_premium/etc all present in /config response on framework-supabase prod. - Fallback shape (when no tenant_paywall row exists) emits v2 defaults matching migration 071's DEFAULT clauses so the SDK never sees missing keys. Dashboard: - PaywallConfig + ValuePropTriple + PAYWALL_CONFIG_DEFAULTS + VALUE_PROP_ICON_VOCAB moved to lib/types.ts as the canonical TypeScript contract. Designer imports from there. - Content section in Paywall designer carries all 11 v2 fields with a per-bullet repeater for value_props (icon-key picker + title + optional description + ▲/▼/✕ controls). Hero icon expandable details for SVG paste + URL fallback. - BrandedStack listed first in the Template selector as v2 default; legacy Minimal/Premium/Dark labelled "Deprecated — drops in cmp-paycraft 3.0.0". - PreviewIframe wired ON by default at https://paywall-preview.paycraft. mobilebytesensei.com (Cloudflare Pages target). Env-var-gated React-mockup fallback removed. paywall-designer.tsx tail deleted: PaywallPreview, FreeState, PremiumState, LoadingState, ErrorState, DeviceConflictState, OwnershipVerifiedState (the React mockup is gone — iframe is the only preview surface so what the dashboard shows IS what the SDK renders). - Net: dashboard/components/paywall/paywall-designer.tsx loses 460 lines (mockup) and gains 280 (Content section + ValuePropsRepeater). Deploy pipeline: - .github/workflows/deploy-paywall-preview.yml builds :cmp-paycraft: jsBrowserDistribution and deploys to Cloudflare Pages project paycraft-paywall-preview on every push to main/development touching cmp-paycraft/** or the deploy config dir. - preview-iframe-deploy/README.md documents the one-time admin setup: Cloudflare API token, Pages project creation, custom domain binding to paywall-preview.paycraft.mobilebytesensei.com, CORS allowlist, smoke check. Tests: - server/migrations/071_*.test.sql — 5 in-DB test cases covering round-trip (Test 1), v1-payload defaults (Test 2), SVG sanitization rejection of <script>/<foreignObject>/external href (Test 3), safe SVG acceptance (Test 4), column-inventory idempotency (Test 5). Transaction-wrapped with ROLLBACK so safe on any DB. - dashboard/tests/paywall-designer-v2.spec.ts — 6 Playwright scenarios: hero title edit warm-path latency ≤ 500ms, value props add/edit/reorder/ remove, SVG paste server rejection, popular plan dropdown persistence, iframe visual baseline diff ≤ 1%, BrandedStack default + legacy deprecation hint visible. Verified live: - Migration 071 applied to framework-supabase prod (mlwfgytjxlqyfxcgpysm), 14/14 v2 columns present in tenant_paywall. - Edge Function config redeployed; smoke fetch against reels-downloader tenant returns all 14 v2 fields populated with defaults. - 26/26 sub-plan 01 CHEAP verify checks pass. Open prereqs (sub-plan 01 G-1 boundary verify can't fully close until): - Cloudflare Pages project paycraft-paywall-preview created + CLOUDFLARE_ API_TOKEN GH secret added + paywall-preview.paycraft.mobilebytesensei. com DNS bound (one-time admin — see preview-iframe-deploy/README.md). - Playwright dashboard test running (needs local or CI dashboard at localhost:3000). Baseline screenshot will be captured on first run. Spec: plan-layer/project-plans/mbs/PayCraft/active/paycraft-paywall-v2- production-ui/{RESEARCH.md, GOAL.md, PLAN.md, 01-dto-and-dashboard- foundation.md}.
… tests The new dashboard/tests/paywall-designer-v2.spec.ts (sub-plan 01 T8 of paycraft-paywall-v2-production-ui) imports from @playwright/test. CI 'Dashboard typecheck (PreviewIframe contract)' job runs npx tsc --noEmit over the whole repo including tests/, so the missing dep surfaced as 9 TS errors. Adding the devDep resolves the imports without touching the test.
…anded-flow composables Builds on sub-plan 01 (commit 009e6a6) — DTO v2 was already on the wire and the dashboard's Content tab was already editing the 11 new fields. This commit closes the SDK side so consumers actually see the v2 design. cmp-paycraft 2.1.0 release shape: PaywallDto v2 contract — config/SuiteConfig.kt - 11 new content fields (hero_title, hero_subtitle, value_props rich triples, cta_continue, cta_get_premium, restore_label, terms_url, privacy_url, popular_plan_sku, success_title/_message/_cta_label, hero_icon_svg, hero_icon_url) wired up to the kotlinx.serialization graph. Every field has a default matching reels-downloader's strings.xml premium-banner keys so a tenant that hasn't configured the dashboard's Content tab still renders sensibly. - New ValuePropTriple { icon, title, description? } data class — the rich-triple shape the dashboard's value-props repeater writes. - Template default flipped to "branded-stack" (was "minimal"). PaywallTemplate — presentation/PaywallTemplate.kt - New BRANDED_STACK enum value renders the v2 design. - Legacy MINIMAL / PREMIUM / DARK kept @deprecated for 90-day grace. PaywallTemplate.parse("minimal"|"premium"|"dark") still resolves, with a deprecation warning surfaced to consumers compiling against 2.1.0. - parse() now defaults to BRANDED_STACK on unknown values (was MINIMAL). - Multiple kebab- / snake- / camel-case spellings accepted for the branded-stack key so consumers writing the template name from a hand-rolled YAML or env var don't trip on case. BrandedStackTemplate — the production-grade paywall UI - 60-line Free state composition: PaywallHeroIcon + hero title + hero subtitle + ValuePropList (rich-triple bullets, icon-leading row with optional description) + plan stack with PlanCard(popular=true) ring on popular_plan_sku + BrandedStackContinueButton + PaywallMicroFooter (PRIVACY · TERMS · RESTORE) + tier-aware "Powered by PayCraft by MobileByteSensei" line. - Polished Loading/Premium/Error/DeviceConflict/OwnershipVerified branches — same DesignToken set so the whole template feels coherent. - Reads tokens via PayCraftTheme.colors / .typography (existing CompositionLocal from cmp-paycraft 2.0.x). No new theme infrastructure required: PayCraftColors already carries accent/onSurface/surface/ onSurfaceVariant/popularBadge/divider/outline. The only token added is PayCraftColors.onAccent (defaulted to white) so the Continue CTA's contentColor has an explicit roundup. New PayCraftColors.onAccent — ui/theme/PayCraftColors.kt - Defaults to Color.White so consumers building against 2.0.x with a custom PayCraftColors() ctor don't need to thread a new arg. Source- compatible add. PayCraftPremiumBanner — ui/PayCraftPremiumBanner.kt (NEW) - Drop-in Settings-tab card — the consumer-app entry point to the paywall. Defaults pulled from PaywallDto v2 fields (heroTitle / heroSubtitle / ctaGetPremium / restoreLabel) match reels-downloader's strings.xml premium-banner keys exactly so a tenant with NO dashboard config still produces the existing purple/yellow Card look. - Per-field overrides (titleOverride / subtitleOverride / ctaOverride / restoreOverride) take precedence — consumers can ship the migration before the dashboard Content tab is populated by threading their existing strings.xml resources through the override args, then drop the overrides one at a time as the dashboard catches up. - Per D8 the reels-downloader Settings-tab swap is opt-in; existing strings.xml keys stay during the 90-day grace. Migration guide at docs/MIGRATING-TO-PAYCRAFT-PREMIUM-BANNER.md. PayCraftCheckoutSuccessSheet — ui/PayCraftCheckoutSuccessSheet.kt (NEW) - Post-purchase celebration ModalBottomSheet shown once per (session × SKU) after Free → Premium transition. Suppressed on subsequent calls via CheckoutSuccessFlag (multiplatform-settings flag keyed paycraft_success_shown_{sku}_{session_id}). - Reads success_title / success_message / success_cta_label from PaywallDto v2. - Consumer wires it themselves — cmp-paycraft 2.1.0 doesn't change PayCraftBillingManager's API surface. The post-purchase callback hook (D12 from sub-plan 03 GOAL.md) is documented in the migration guide but left for the consumer to wire (or a 2.1.1 follow-up if the demand is there). CheckoutSuccessFlag — persistence/CheckoutSuccessFlag.kt (NEW) - internal helper around multiplatform-settings. Same dependency ConfigCache + PayCraftSettingsStore already use, so no new cmp-paycraft dep added. Version bump — gradle.properties - paycraft.version=2.0.10 → 2.1.0. Additive minor per epic D4. Tests — cmp-paycraft/src/commonTest/.../PaywallDtoBackcompatTest.kt (NEW) - 5 kotlinx.serialization round-trip cases: 1. v2 wire → v2 deserializer — every v2 field round-trips, value_props rich triples preserved. 2. v1 wire → v2 deserializer — every v2 field falls through to the declared default matching migration 071's column DEFAULTs. 3. Future-2.2.0 unknown fields → 2.1.0 deserializer with ignoreUnknownKeys=true — silently dropped, no exception. 4. Zero value_props → empty list (not null). 5. Single value_prop missing description → null description. Maestro E2E — reels-downloader/.maestro/paywall-v2-branded-flow-e2e.yaml - Extends PR #100's paywall-stripe-checkout-e2e.yaml. Asserts: launch → Settings → "Upgrade to Premium" + "Enjoy ad-free experience..." + "Get Premium" + "Restore Your Premium" all visible (PayCraftPremiumBanner with v2 default copy) → tap Get Premium → paywall modal with 4 plans + Continue → tap Continue → provider sheet with Stripe (from PR #100) → Stripe Checkout email field. - Smoke runnable on Q4CIZHA6Y5GMJV89 (OnePlus CPH2423). Consumer migration guide — docs/MIGRATING-TO-PAYCRAFT-PREMIUM-BANNER.md - Pre-flight visual diff recipe (Compose Preview side-by-side, expect ≤ 2% pixel mismatch vs hand-coded reels-downloader baseline). - Swap recipe: delete the existing Settings-tab Card block, replace with PayCraftPremiumBanner(onGetPremiumTap, onRestoreTap) two-arg form. - During the 90-day grace, threading existing strings.xml resources through *Override args so the SDK reads host-resource copy until the dashboard is populated. Drop overrides one at a time. - Dashboard fields to populate to match the prior banner look (the defaults match reels-downloader; other consumer apps that authored custom copy should overwrite in the dashboard before flipping). - Rollback recipe — revert the consumer commit; strings.xml + git history have everything. Sub-plan progress - sub-plan 01 (DTO + dashboard + iframe foundation): commit 009e6a6 + c930095 — landed in this PR - sub-plan 02 (SDK BrandedStackTemplate + 2.1.0): this commit - sub-plan 03 (PayCraftPremiumBanner + PayCraftCheckoutSuccessSheet + Maestro + migration guide): this commit Still in 03 backlog (not blocking 2.1.0 ship — consumer-app concerns that can land in 2.1.1 / consumer-side commits as demand surfaces): - PayCraftRestoreSheet visual polish to match BrandedStackTemplate (the existing PayCraftRestore.kt still works — design-token alignment is a follow-up). - PayCraftBillingManager.onSubscriptionActivated callback hook (consumer can poll BillingState transition today). - Compose UI screenshot baselines for BrandedStackTemplate's 6 billing states (functional rendering covered; pixel-perfect baselines need the dashboard iframe + Playwright pair from sub-plan 01 to be deployed first).
…ceVariant CI Compile All Targets failure on f1651fe — 3 unresolved references: e: BrandedStackTemplate.kt:21:60 Unresolved reference 'PlayArrow'. e: BrandedStackTemplate.kt:256:79 Unresolved reference 'onSurfaceMuted'. e: BrandedStackTemplate.kt:406:53 Unresolved reference 'PlayArrow'. cmp-paycraft's Compose deps only pull the core Material Icons set (Filled.Star/Check/CheckCircle — verified used elsewhere in the module); the `androidx.compose.material.icons.automirrored.*` package isn't on the classpath. The hero icon doesn't need play-arrow semantics — Star is a better fit for a premium-tier paywall and matches the badge used in PayCraftPremiumBanner.kt + PayCraftRestore.kt. onSurfaceMuted → onSurfaceVariant: leftover from the sed pass that renamed PayCraftColors fields (the others were caught at f1651fe time; this one slipped — only used in the Loading branch). Self-tested via static grep — no other AutoMirrored/PlayArrow/ onSurfaceMuted references anywhere in the new SDK code.
… + string-template cleanup Output of `./gradlew spotlessApply` against f1651fe + 44e7eed. Pure formatting: - Single-expression function bodies collapsed onto one line where width permits (CheckoutSuccessFlag.wasSuccessShown / keyFor, BrandedStackTemplate.paywallConfig, PaywallTemplate.parse and a handful of PaywallDto field decls in SuiteConfig). - Redundant ${sessionId} → $sessionId in CheckoutSuccessFlag.keyFor. - 2-space alignment normalization in PaywallTemplate.kt @deprecated args. No behavior change. Resolves CI `pr-check / Code Quality` (spotlessCheck) on PR #100.
…arrow Drift introduced by 44e7eed compile-fix that swapped `AutoMirrored.Filled.PlayArrow` (unavailable on cmp-paycraft's classpath) for `Filled.Star` without updating the adjacent doc comment. The code renders Star; the comment said play-arrow. Audit prompt: "find there is not stub or shell left in epic to implement". No behavior change; only a doc-vs-code accuracy fix.
Sub-plan 01 (paycraft-paywall-v2-production-ui) fixes surfaced by running migration 071 against a fresh local Supabase: - 071: add 'branded-stack' to paywall_template enum (the DTO's v2 default); cast template/branding to their enum types in tenant_paywall_upsert (text->enum is not an implicit assignment cast) - 071: sanitize_paywall_svg used \b (a backspace in Postgres ARE) instead of \y (word boundary) -> <script>/<foreignObject> SVG payloads were silently accepted. Security fix; rejection now verified. - relocate 071 round-trip test out of the symlinked supabase/migrations dir (supabase db reset was auto-applying it) to server/tests/; add the required api_key_test/api_key_live columns to the tenants fixture sample-app: - register SampleApplication in AndroidManifest (Koin was never started -> PayCraftRestore crashed on launch) - migrate sample fixtures to the nested testPaymentLinksBySku DTO shape
…n callback, tests (cmp-paycraft 2.1.0) Sub-plans 02 + 03 of paycraft-paywall-v2-production-ui — drive to code-complete with passing tests (compileKotlinJvm + jvmTest 65 tests, 0 failures). Sub-plan 02 (SDK branded-stack template): - PaywallDesignToken: add PayCraftSpacing token + PaywallDesignToken / LocalPaywallDesignToken aliases (additive to PayCraftTheme); PayCraftRestore now consumes the token surface instead of hardcoded M3 colors - PlanCard: real "MOST POPULAR" ring + badge (token-driven; was a plain text label) - +14 BRANDED_STACK Compose tests across the 6 billing states (PaywallTemplateTest now 32 tests) - bump paycraft.version 2.0.10 -> 2.1.0 Sub-plan 03 (branded flow components): - BillingManager: subscriptionActivated SharedFlow, emitted once on the non-premium -> premium rising edge (sku + isTrial), edge-tracked across refreshes - Compose tests for the 3 flow composables: PremiumBanner (12), CheckoutSuccessSheet (10), RestoreContent (11) - Maestro e2e flows (branded + restore) targeting the in-repo sample-app showcase Device-gated acceptance deferred: AC-9 screenshot-diff baseline, AC-16 Maestro run.
…lows - /config Edge Function: requireRateLimit was called with 5 args against a 6-param signature — "config_fetch" landed in the ipAddress slot, 60 in bucket, etc. Added IP extraction (x-forwarded-for) + the missing refillPerSec so it type-checks (deno check clean) and rate-limits correctly. (sub-plan 01 AC-4) - Maestro flows reworked to the real sample-app UX and verified green on-device (Q4CIZHA6Y5GMJV89): the home Settings screen shows PayCraftPremiumBanner; the branded paywall (Monthly/Quarterly/Yearly/Continue) opens via "Get Premium". Cold-start via stopApp (device denies pm clear) + extendedWaitUntil for the async mock-config load. (sub-plan 03 AC-16)
The deploy-paywall-preview workflow referenced :cmp-paycraft:jsBrowserDistribution and cmp-paycraft/build/dist/js/productionExecutable, but the Kotlin/JS preview bundle lives in the cmp-paycraft:preview-js submodule. Fixed the build task to :cmp-paycraft:preview-js:jsBrowserDistribution and all three output-path references to cmp-paycraft/preview-js/build/dist/js/productionExecutable. Verified by deploying the bundle to Cloudflare Pages (paycraft-paywall-preview) manually via wrangler — live + 200 at paycraft-paywall-preview.pages.dev (AC-8); PreviewIframe<->bundle postMessage re-render contract verified against it (AC-6).
The Kotlin/JS preview bundle (cmp-paycraft:preview-js) consumed only v1 fields (template/primary_color), so the dashboard designer iframe ignored every v2 copy field — not the "true-WYSIWYG" the epic promised. Extended PreviewConfig + render() to drive the paywall from the v2 DTO: - hero_title / hero_subtitle - value_props (rich-triple list, icon-leading rows) - cta_continue (primary button label) - restore_label + terms_url + privacy_url (micro-footer links) - popular_plan_sku (drives which plan card gets the MOST POPULAR ring) - success_title / success_message (Premium state header) All dashboard-entered copy is HTML-escaped. Verified true-WYSIWYG against the live Cloudflare Pages preview via Playwright (9/9 field-render checks green).
…JS iframe bundle The v2 plan replaced the dashboard's working React paywall preview with a separate Cloudflare-hosted Kotlin/JS iframe bundle. That was the wrong call: the existing React preview already renders the tenant's REAL products, whereas the bundle hardcoded fake plans (a regression) and was a hand-written DOM port (not the real SDK render) — so it added hosting + CI cost for no true-WYSIWYG gain. Reverted per product-owner decision. - dashboard/paywall-designer: restore the React PaywallPreview (phone-frame + all 6 states) rendering real products; remove the PreviewIframe. FreeState now also consumes the v2 DTO fields — hero_title/hero_subtitle, value_props list, cta_continue label, restore_label/terms_url/privacy_url footer links, and the MOST POPULAR ring keyed off popular_plan_sku matched to a real product. - retire the iframe/bundle path: delete PreviewIframe.tsx, preview-iframe-deploy/, the cmp-paycraft:preview-js module (+ settings.gradle.kts include), and the deploy-paywall-preview.yml / preview-parity.yml workflows. Keeps the v2 Content-tab editors. tsc clean; gradle reconfigures clean.
Make the AC-6 dashboard Paywall-designer Content-tab e2e actually runnable +
green (5/5). The authored spec targeted a non-existent iframe and had no config
or auth fixture.
- playwright.config.ts: webServer boots `next dev` with local-Supabase env
(keys resolved at config-eval via `supabase status -o env`), baseURL :3000,
storageState from global-setup.
- tests/global-setup.ts: seeds local Supabase idempotently (confirmed admin user
+ tenant + tenant_admins + 2 tenant_products + tenant_paywall), mints a session
via signInWithPassword, and captures the correctly-named sb-*-auth-token cookies
using @supabase/ssr's own setSession→setAll path (no cookie-name guessing) into
a gitignored tests/.auth/state.json.
- rewrite paywall-designer-v2.spec.ts for the React preview (no iframe): hero edit
reflects in the inline preview; value-props repeater add/edit/remove; popular-plan
dropdown lists seeded products + persists; SVG <script> paste is rejected server-side
(asserted on the 500 response — the route returns { error } and the designer shows
no toast); BrandedStack default template.
Route is /paywall (the old spec used /settings/paywall). 5/5 green; tsc clean.
The pr-check Code Quality job failed on spotlessKotlin (ktlint) at PayCraftBillingManager.kt:71 — `_previousIsPremium` is a private-only tracking var with no public counterpart, which violates ktlint's backing-property-naming rule (the file's other `_`-prefixed fields back public StateFlows). Renamed it to `lastObservedPremium` (4 usages). Also fix a pre-existing spotless misconfig surfaced locally: the `misc` format targeted **/*.md etc. without excluding node_modules, so it scanned dashboard/node_modules and threw NoSuchFileException on a dangling vendored file. Added node_modules + dist to targetExclude. spotlessApply reformatted the touched Kotlin (PlanCard else-branch, test files). Local: `./gradlew spotlessCheck detekt` BUILD SUCCESSFUL.
…zen-install) Vercel CI deploys with --frozen-lockfile and failed at `pnpm install`: ERR_PNPM_OUTDATED_LOCKFILE — "1 dependencies were added: @playwright/test@^1.61.0". Commit c930095 added @playwright/test to dashboard/package.json but didn't update the lockfile. Regenerated via `pnpm install --lockfile-only` (lockfileVersion 9.0 preserved; only @playwright/test + playwright-core added). `next build` already passes; this unblocks the Vercel deploy.
…ayment-links-and-provider-sheet feat(paycraft): v2 mode-duality + nested payment-links + provider sheet + paywall-v2 epic (cmp-paycraft 2.1.0)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Auto-opened by /paycraft-deploy Phase 4 PROMOTE.
Source: origin/development @ fbe10c8
Target: origin/main @ 288983c
Diff: 21 commits
This PR is fast-forward-only — main is kept as an exact replica of development at promote time. No manual edits should land on main.