Skip to content

release: promote development → main (2026-06-19T05:13:28Z) - #97

Merged
therajanmaurya merged 19 commits into
mainfrom
development
Jun 19, 2026
Merged

release: promote development → main (2026-06-19T05:13:28Z)#97
therajanmaurya merged 19 commits into
mainfrom
development

Conversation

@therajanmaurya

Copy link
Copy Markdown
Collaborator

Auto-opened by /paycraft-deploy Phase 4 PROMOTE.

Source: origin/development @ 483a5ec
Target: origin/main @ ab2fb8c
Diff: 19 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.

MobileByteLabsCommunity and others added 19 commits June 18, 2026 21:40
When PayCraft.initialize(apiKey, Cloud) runs, it sets PayCraft.apiKey and
PayCraft.backend synchronously. However, PayCraft.config is only populated
AFTER the async cloud SuiteConfig fetch completes.

The PayCraftModule Koin singleton for SupabaseClient was calling
requireConfig() at materialization time, assuming config was populated.
In reality, Koin materializes the singleton (e.g. when resolving the
PayCraftPaywallViewModel) before the cloud fetch finishes, throwing
'PayCraft.initialize(apiKey) must be called before use'.

Fix: Read PayCraft.backend (known synchronously) and PayCraft.apiKey
(set synchronously) instead of requiring config. Both are available
immediately after initialize() returns.

This unblocks Cloud-mode initialization and allows the BillingManager
to start its cache+sync flow without blocking on the config fetch.

Verified: app launches on Android, PayCraft logs 'Backend=cloud', no
Koin DI crashes, BillingManager initializes cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… placeholder config

Three related fixes for the v2 Cloud SDK initialization path:

1. Placeholder config in initialize() — Cloud/SelfHosted backends now populate
   PayCraft.config synchronously with an empty-list PayCraftConfig before
   kicking off the async cloud fetch. This means requireConfig() callers
   (PayCraftBanner.kt:138, PaywallScreen consumers, etc.) never throw
   IllegalStateException between init and the first SuiteConfig arrival.

2. Auto-fetch SuiteConfig on init — fetchAndApplySuiteConfig() runs in
   sdkScope at the tail of initialize() for non-Mock backends. Issues
   GET $backend.configUrl?apiKey=…, decodes the SuiteConfig response,
   and publishes via applySuiteConfig(). On HTTP non-success / network
   failure, logs and leaves the placeholder in place so UI shows empty
   state instead of crashing.

3. Koin DI race fix — PayCraftModule's SupabaseClient singleton now reads
   PayCraft.backend (statically known for Cloud) and PayCraft.apiKey (set
   synchronously by initialize()) instead of going through requireConfig()
   which depends on the async fetch.

Plus: deploy.sh Phase 3.5 FUNCTIONS DEPLOY — wires up the Edge Function
deploy step using framework-supabase-access-token from the vault.

Verified on Android (CPH2423): app launches, PayCraft logs initialize +
GET to /functions/v1/config, no Koin or requireConfig crashes,
BillingManager initializes cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…l anon key

Two related fixes for Cloud-mode SuiteConfig fetch:

1. Add Authorization + apikey headers to GET /functions/v1/config.
   Supabase Edge Functions require Authorization: Bearer <anon-key>
   by default (verify_jwt=true platform-level). Without these headers
   the function returns 401 UNAUTHORIZED_NO_AUTH_HEADER and the SDK
   never loads its plans/products.

2. Replace PUBLIC_PAYCRAFT_ANON_KEY_PLACEHOLDER in PayCraftBackend.Cloud
   with the real framework-supabase anon key. Anon key is publishable
   by design (same security model as pk_live_*) — RLS prevents data
   exposure. This unblocks every Cloud-mode SDK consumer.

Verified end-to-end on Android (CPH2423):
  Backend = cloud
  GET …/functions/v1/config?apiKey=pk_live_…
  ══ PayCraft SuiteConfig applied ═══════════════
    Source = Cloud, Products = 4, Locale = US
  [loadConfig] cloud fetch ok — 4 products
  No crashes. BillingManager + paywall render with real cloud data.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refines the SDK paywall composables so the rendered Free state matches the
dashboard's Paywall designer reference mockup, while preserving all 6 state
paths (Loading / Free / Premium / Error / DeviceConflict / OwnershipVerified)
the SDK already handled.

Changes:

1. PayCraftPaywallHeader — hero icon container is now a 64dp rounded-square
   tile (was 72dp circle), default icon switched from Star to PlayArrow,
   accepts an optional subtitle string rendered below the title in
   bodyMedium/onSurfaceVariant.

2. PayCraftPlanCard — drops the leading radio button (border alone signals
   selection), moves the POPULAR badge to a center-top pill that overlaps
   the card's top edge (matches the designer's 'MOST POPULAR' pill).

3. PaywallLegalFooter (new) — minimal 'PRIVACY · TERMS · RESTORE' row
   rendered at the foot of the Free state. Privacy/Terms open URLs from
   paywall.themeJsonb (privacy_url / terms_url) with paycraft.mobilebytesensei.com
   defaults; Restore dispatches PayCraftPaywallAction.RestoreSubscription.

4. PayCraftPaywall.kt Free state — replaces the 'What you get' benefits
   list + 'Choose your plan' header + 'Get Premium' CTA + BrandingFooter
   with the designer's layout: hero header (with subtitle from
   paywall.themeJsonb['headline_subtitle']), plan cards, 'Continue' CTA,
   PaywallLegalFooter. The other 5 state paths are unchanged.

5. Strings — adds paycraft_most_popular_badge, paycraft_continue_cta,
   paycraft_legal_{privacy,terms,restore}.

Builds clean: assembleDemoDebug ok against reels-downloader composite.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two regressions from the v2.0.5 designer-UI refactor + an existing dead-end:

1. BrandingFooter (Powered by PayCraft by MobileByteSensei) was inadvertently
   dropped from the Free state when the legacy benefits list was removed.
   Add it back below PaywallLegalFooter — renders on every paywall state
   per the dashboard's attribution toggle (auto-hides for Pro+ tier).

2. The legal-footer RESTORE link silently no-op'd for not-logged-in users:
   onRestoreClick dispatched RestoreSubscription(email = userEmail.orEmpty())
   which the ViewModel bails on (if email.isBlank() return).
   Fix: when state.userEmail is null/blank, dispatch the new
   OpenRestoreSheet action instead, which shows the SDK's PayCraftRestore
   modal bottom sheet (email input + OAuth).

3. New OpenRestoreSheet + CloseRestoreSheet actions + isRestoreSheetVisible
   state field. PayCraftPaywallContent renders PayCraftRestore(visible, …)
   at the same dialog layer as ProviderBottomSheet.

Verified on Android (CPH2423) with cmp-paycraft local composite:
  - Branding 'Powered by PayCraft by MobileByteSensei' visible at foot
  - PRIVACY/TERMS open URLs via PayCraftPlatform.openUrl as before
  - RESTORE now opens the SDK restore sheet instead of doing nothing

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updates BrandingFooter from 'Powered by PayCraft by MobileByteSensei' to
the dashboard's reference wordmark style:

    Powered by  PAYCRAFT

Uses AnnotatedString to render 'Powered by' in regular weight at the
default muted onSurfaceVariant tint, then 'PAYCRAFT' in ExtraBold uppercase
with 1.5sp letter-spacing as the brand wordmark. Matches the Premium-state
mockup in the dashboard's Paywall designer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-only consumer init

Architectural fix: consumer apps should never have to call
PayCraftPlatform.init(applicationContext) from their androidMain. Context
handoff is now done inside the SDK via an androidx-startup Initializer:

- New PayCraftInitializer (androidMain) — calls PayCraftPlatform.init at
  app start, before Application.onCreate runs.
- New AndroidManifest.xml (androidMain) — registers the Initializer via
  the androidx.startup InitializationProvider merge node.
- Added androidx.startup:startup-runtime:1.2.0 dependency.

Consumer impact: drop PayCraftPlatform.init(applicationContext) from
your Application.onCreate. Call PayCraft.initialize(apiKey) from
commonMain only — the Android Context handoff is handled by the SDK.
Opt-out (tools:node='remove') documented in PayCraftInitializer KDoc.

Also reverts the v2.0.7-WIP InitOptions.showTestProducts client flag +
ProductDto.isTestOnly filter (test-only product visibility is being
moved to a dashboard-driven server-side filter using a test_devices
registry — see follow-up release).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…stry

Production-grade visibility control for internal QA products. The
dashboard marks a product is_test_only=true; the server only includes
it in /config responses when the requesting device's fingerprint is
in the tenant's test_devices allow-list. Signed prod APKs whose
fingerprint isn't registered physically cannot see the product.

SDK (PayCraft 2.0.9):
- expect/actual DeviceFingerprint (Android SHA-256(SSAID+package),
  iOS FNV1a(identifierForVendor+bundle), JVM persisted UUID, macOS
  NSUserDefaults UUID, wasmJs localStorage UUID).
- PayCraft.deviceId lazy property + logged at initialize().
- /config GET now carries ?device_id=<fingerprint>.

Server (migration 067):
- tenant_products.is_test_only column.
- test_devices table + tenant-admin RLS.
- RPCs: test_devices_{list,register,revoke,is_registered}.
- /config Edge Function calls is_registered, filters is_test_only
  products for unregistered devices.

End-to-end verified:
  - registered device (CPH2423) → 5 products incl. Testing Trial
  - unregistered device         → 4 products
  - no device_id                → 4 products

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dashboard UI for cmp-paycraft 2.0.9's server-enforced test product
visibility:

- /settings/test-devices: list + register + revoke device fingerprints
  for the current tenant. Form validates the 16-hex-char device ID
  shape; helper text explains how to grab it from adb logcat / Xcode
  console at app start.

- Product editor (new + edit) grows a 'Test devices only' checkbox
  alongside the existing 'Active' toggle. Visual treatment + helper
  text spell out the server-enforcement guarantee + link to the
  Testing Devices page.

- New API routes:
    GET    /api/test-devices       — list rows
    POST   /api/test-devices       — register (validates hex shape)
    DELETE /api/test-devices/{id}  — revoke
  All call SECURITY DEFINER RPCs from migration 067; both register +
  revoke emit audit_log entries.

- Sidebar: new 'Testing devices' link under the Account group, between
  API keys and Settings.

- Migration 068 extends tenant_products_upsert to thread the
  is_test_only flag through INSERT and UPDATE so the toggle actually
  persists. Applied to framework-supabase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@therajanmaurya
therajanmaurya merged commit b4cd615 into main Jun 19, 2026
7 of 10 checks passed
@vercel

vercel Bot commented Jun 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pay-craft Ready Ready Preview, Comment Jun 19, 2026 5:13am

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: be36cd6e-097c-4bb7-b263-9a6284d4f3df

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch development

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants