Conversation
WalkthroughThe changes update the billing and payment flow in the frontend to support platform-aware checkout handling, especially for Tauri and iOS environments. The billing API functions now detect Tauri and use its opener plugin to open external URLs. The pricing page dynamically builds success and cancellation URLs based on the detected platform, removes iOS-specific payment restrictions, and displays user feedback for payment status. The deep link handler is enhanced to process both authentication and payment deep links, routing users accordingly. Minor formatting changes are made in login and signup routes without affecting logic. New routes for payment success and cancellation are added to redirect users appropriately. The apple-app-site-association file is updated to support these new deep link paths. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant PricingPage
participant billingApi
participant TauriOpener
participant Browser
User->>PricingPage: Clicks "Subscribe" or payment button
PricingPage->>billingApi: createCheckoutSession / createZapriteCheckoutSession
billingApi->>billingApi: Detect Tauri environment
alt In Tauri
billingApi->>TauriOpener: Open checkout_url externally
TauriOpener-->>User: Opens URL in external browser
else Not in Tauri or error
billingApi->>Browser: window.location.href = checkout_url
Browser-->>User: Navigates to checkout page
end
sequenceDiagram
participant User
participant ExternalPaymentProvider
participant DeepLinkHandler
participant AppRouter
User->>ExternalPaymentProvider: Completes payment (success/cancel)
ExternalPaymentProvider->>User: Redirects via deep link or query params
User->>DeepLinkHandler: App receives deep link or URL with params
DeepLinkHandler->>AppRouter: Parses link, updates tokens/status
AppRouter-->>User: Navigates to appropriate page (e.g., /pricing)
Possibly related PRs
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (11)
💤 Files with no reviewable changes (2)
🚧 Files skipped from review as they are similar to previous changes (9)
⏰ Context from checks skipped due to timeout of 90000ms (1)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Deploying maple with
|
| Latest commit: |
47fc54d
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://259f8c98.maple-ca8.pages.dev |
| Branch Preview URL: | https://apple-ios-pricing-page.maple-ca8.pages.dev |
There was a problem hiding this comment.
PR Summary
Added comprehensive iOS support for payment processing and authentication, focusing on native Apple Sign-In integration and deep linking capabilities.
- Added payment deep link handling in
/frontend/src/components/DeepLinkHandler.tsxfor iOS-specific payment success/cancel flows - Implemented native Apple Sign-In with secure nonce handling in
/frontend/src/routes/login.tsxand/frontend/src/routes/signup.tsx - Added Tauri environment detection and iOS-specific URL handling in
/frontend/src/billing/billingApi.tsfor payment flows - Removed iOS restrictions on paid plans and added deep linking support in
/frontend/src/routes/pricing.tsx
5 file(s) reviewed, 5 comment(s)
Edit PR Review Bot Settings | Greptile
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
frontend/src/billing/billingApi.ts (1)
145-160: Consider extracting Tauri-aware navigation into a shared helper to remove duplication
Almost identical blocks (Tauri detection +open_urlinvocation + fallback) are present here and again at lines 205-226. Repeating the dynamic import, detection and error-handling logic in two places increases bundle size and maintenance effort.- try { - // Check if we're in a Tauri environment - const isTauri = await import("@tauri-apps/api/core") - .then((m) => m.isTauri()) - .catch(() => false); - - if (isTauri) { - // Use the opener plugin... - const { invoke } = await import("@tauri-apps/api/core"); - await invoke("plugin:opener|open_url", { url: checkout_url }).catch((error: Error) => { - console.error("Failed to open external browser:", error); - window.location.href = checkout_url; - }); - return; - } - } catch (error) { - console.error("Error opening URL with Tauri:", error); - } + if (await openUrlExternallyIfTauri(checkout_url)) { + return; // already handled + }and add once, near the top of the module:
async function openUrlExternallyIfTauri(url: string): Promise<boolean> { try { const isTauri = await import("@tauri-apps/api/core") .then((m) => m.isTauri()) .catch(() => false); if (isTauri) { const { invoke } = await import("@tauri-apps/api/core"); await invoke("plugin:opener|open_url", { url }).catch(() => { window.location.href = url; }); return true; } } catch (err) { console.error("Tauri open_url failed:", err); } return false; }This reduces duplication and keeps Tauri–specific logic in a single, testable place.
frontend/src/routes/pricing.tsx (1)
349-361: Repeated platform-detection logic – extract to utility
The nestedisTauri/isTauriIOSchecks here duplicate earlier logic inbillingApi.ts. Moving them to a shared util (e.g.platform.ts) will cut bundle size and avoid divergent behaviour as the app evolves.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
frontend/src/billing/billingApi.ts(2 hunks)frontend/src/components/DeepLinkHandler.tsx(3 hunks)frontend/src/routes/login.tsx(0 hunks)frontend/src/routes/pricing.tsx(5 hunks)frontend/src/routes/signup.tsx(0 hunks)
💤 Files with no reviewable changes (2)
- frontend/src/routes/signup.tsx
- frontend/src/routes/login.tsx
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: build-macos (universal-apple-darwin)
- GitHub Check: build-linux
- GitHub Check: build-ios
- GitHub Check: Cloudflare Pages
🔇 Additional comments (2)
frontend/src/billing/billingApi.ts (1)
154-160: Verify that theopenerplugin is bundled on all Tauri targets
A runtimeinvoke("plugin:opener|open_url", …)will throw if the plugin is missing or not registered (e.g. Android/iOS builds where the plugin wasn’t added totauri.conf.json). Please double-check the Rust side and CI build matrix.If you’re not 100 % certain, add a defensive check:
await invoke("plugin:opener|open_url", { url }).catch((e: unknown) => { console.warn("opener plugin unavailable, falling back:", e); window.location.href = url; });frontend/src/routes/pricing.tsx (1)
780-783: UI disables Team + Bitcoin, but handler can still fire via auto-checkout
disabledprevents clicks, yetuseEffect(lines 510-516) programmatically triggershandleButtonClick, which in turn may reachnewHandleSubscribeeven for a Team-plan/Bitcoin combo.
Guard insidehandleButtonClickas well:if (useBitcoin && product.name === "Team") { return; }
cd97219 to
22b10d3
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (6)
frontend/src/components/DeepLinkHandler.tsx (3)
27-28:⚠️ Potential issuePotential bug in URL path parsing logic.
The current implementation uses
pathParts[0]to extract the first path segment, which may not be reliable with custom URL schemes. For deep links likecloud.opensecret.maple://v1/auth?...,pathnameis/v1/auth. Splitting and taking[0]gives"v1", causing"auth"to become the second segment and potentially failing to match.Consider using the last segment instead:
- const firstPathPart = pathParts[0] || ""; + const firstPathPart = pathParts.at(-1) ?? "";
43-45: 🛠️ Refactor suggestionUsing window.location.href causes full page reload.
Direct URL manipulation with
window.location.hrefforces a complete page reload, losing application state and creating a less smooth user experience. Your app is using TanStack Router which provides better navigation methods.Use the
navigate()function from TanStack Router for consistent routing behavior:- window.location.href = "/"; // Reload the app + navigate({ to: "/" });
63-73:⚠️ Potential issueQuery parameters will be filtered out by the validation.
In
pricing.tsx, thevalidateSearchfunction expectssuccessandcanceledto be boolean values, but here they're set as string values in the URL. This means the status banners in the pricing page will never show.Either pass the parameters as strings and update the validator, or pass them directly as string values:
- window.location.href = "/pricing?success=true"; + window.location.href = "/pricing?success=true";Also, use the navigator for consistent routing:
- window.location.href = "/pricing?success=true"; + navigate({ to: "/pricing", search: { success: "true" } });frontend/src/routes/pricing.tsx (3)
244-246:⚠️ Potential issueSuccess and canceled parameters validation issue.
The routing system will serialize boolean values as strings in the URL, but your validator expects boolean types. This means the success and canceled banners will never show.
Update the validator to accept string values or modify how you pass the parameters:
// At the bottom of the file in validateSearch - success: typeof search.success === "boolean" ? search.success : undefined, - canceled: typeof search.canceled === "boolean" ? search.canceled : undefined + success: search.success === "true" ? true : + typeof search.success === "boolean" ? search.success : undefined, + canceled: search.canceled === "true" ? true : + typeof search.canceled === "boolean" ? search.canceled : undefined
362-377: 🛠️ Refactor suggestionDeep link URLs should be centralized as constants.
The deep link URLs are hardcoded in multiple places. This creates a risk of inconsistencies and makes future changes more error-prone.
Create constants for these URLs at the top of the file:
+ // Define URL constants + const IOS_SUCCESS_URL = "cloud.opensecret.maple://payment?payment_success=true"; + const IOS_CANCEL_URL = "cloud.opensecret.maple://payment?payment_canceled=true";Then reference these constants in your code.
396-412: 🛠️ Refactor suggestionDuplicate URL generation code creates maintenance issues.
The code that generates checkout URLs is duplicated between the platform-specific section and the fallback section. This violates the DRY principle and makes maintenance more difficult.
Extract the URL generation into a separate function:
const generateCheckoutUrls = (baseUrl = window.location.origin) => { return { successUrl: `${baseUrl}/pricing?success=true`, cancelUrl: `${baseUrl}/pricing?canceled=true` }; };Then use it in both places:
const { successUrl, cancelUrl } = generateCheckoutUrls(); await billingService.createCheckoutSession(email, productId, successUrl, cancelUrl);
🧹 Nitpick comments (2)
frontend/src/components/DeepLinkHandler.tsx (1)
53-58: Success and canceled parameters have inconsistent parsing logic.The code checks for both
success=trueandpayment_success=truefor success detection, and bothcanceled=trueandpayment_canceledfor cancellation, with different approaches between them. This inconsistency makes the code harder to maintain.Standardize how you check for these parameters:
- const success = - urlObj.searchParams.get("success") === "true" || - urlObj.searchParams.get("payment_success") === "true"; - const canceled = - urlObj.searchParams.get("canceled") === "true" || - urlObj.searchParams.has("payment_canceled"); + const success = + urlObj.searchParams.get("success") === "true" || + urlObj.searchParams.get("payment_success") === "true"; + const canceled = + urlObj.searchParams.get("canceled") === "true" || + urlObj.searchParams.get("payment_canceled") === "true";frontend/src/routes/pricing.tsx (1)
497-504: Unused dependency in useCallback.The
isIOSvariable is included in the dependency array forhandleButtonClickbut is no longer referenced within the function. This could cause unnecessary re-renders.Remove the unused dependency:
[ isLoggedIn, isTeamPlanAvailable, freshBillingStatus, navigate, portalUrl, newHandleSubscribe, - isIOS ]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
frontend/src/billing/billingApi.ts(2 hunks)frontend/src/components/DeepLinkHandler.tsx(1 hunks)frontend/src/routes/login.tsx(0 hunks)frontend/src/routes/pricing.tsx(5 hunks)frontend/src/routes/signup.tsx(0 hunks)
💤 Files with no reviewable changes (2)
- frontend/src/routes/signup.tsx
- frontend/src/routes/login.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/billing/billingApi.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
frontend/src/routes/pricing.tsx (1)
frontend/src/routes/__root.tsx (1)
Route(18-28)
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: build-linux
- GitHub Check: build-macos (universal-apple-darwin)
- GitHub Check: build-ios
- GitHub Check: Cloudflare Pages
🔇 Additional comments (3)
frontend/src/components/DeepLinkHandler.tsx (1)
74-76: Good error handling approach.The change from logging errors to warnings for unknown deep link types is a good practice, as it's less alarming for unexpected but potentially benign conditions.
frontend/src/routes/pricing.tsx (2)
627-647: Good UI feedback for payment status.The addition of clear visual feedback for payment success and cancellation states improves the user experience significantly. The color-coding and icon choices appropriately convey the status messages.
778-782: Improved iOS support for payments.Removing the iOS-specific disabling of payment buttons while maintaining appropriate platform-specific handling is a good approach. This ensures iOS users can access paid plans while still ensuring the payment flow works correctly for their platform.
970f2c4 to
33bb4b3
Compare
33bb4b3 to
47fc54d
Compare
Summary by CodeRabbit
New Features
Improvements
Style