Skip to content

Add ios pricing#61

Merged
AnthonyRonning merged 1 commit intomasterfrom
apple-ios-pricing-page
May 5, 2025
Merged

Add ios pricing#61
AnthonyRonning merged 1 commit intomasterfrom
apple-ios-pricing-page

Conversation

@AnthonyRonning
Copy link
Contributor

@AnthonyRonning AnthonyRonning commented May 2, 2025

Summary by CodeRabbit

  • New Features

    • Added dedicated routes and pages for payment success and cancellation, redirecting users with clear status indicators.
    • Extended deep link support to handle multiple types, including payment-related links, improving navigation and user feedback.
    • Checkout sessions now open URLs externally in Tauri environments when available for a smoother payment experience.
  • Improvements

    • Removed iOS-specific payment restrictions, enabling full payment functionality across all platforms.
    • Enhanced payment button text and enabling logic based on login status, subscription, and payment provider.
    • Improved platform detection for checkout flows, supporting tailored success and cancellation URLs for iOS within Tauri.
    • Added visual feedback on pricing page for payment success and cancellation statuses.
    • Updated app site association and routing configuration to support new payment-related deep links and routes.
  • Style

    • Minor formatting adjustments for code consistency.

@coderabbitai
Copy link

coderabbitai bot commented May 2, 2025

Walkthrough

The 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

File(s) Change Summary
frontend/src/billing/billingApi.ts Updated createCheckoutSession and createZapriteCheckoutSession to detect Tauri environment and open checkout URLs using the Tauri opener plugin when available, with fallback to standard browser navigation. Wrapped logic in try-catch for error handling.
frontend/src/routes/pricing.tsx Refactored checkout session creation to use platform-aware success/cancel URLs (deep links for iOS Tauri, query params for web/desktop), removed iOS payment restrictions, enhanced button text and click logic, and added UI feedback for payment status. Updated getButtonText, handleButtonClick, and subscription handling logic accordingly.
frontend/src/components/DeepLinkHandler.tsx Enhanced to support both authentication and payment deep links. Parses URL to distinguish deep link types, processes tokens or payment statuses, updates localStorage, and navigates within the app as needed. Improved error and warning logging for unrecognized links.
frontend/src/routes/login.tsx
frontend/src/routes/signup.tsx
Removed extraneous blank lines before Apple Sign-In plugin invocation. No logic or control flow changes.
frontend/src/routeTree.gen.ts Added new routes /payment-success and /payment-canceled with full type declarations and integration into the route manifest and root route children.
frontend/src/routes/payment-success.tsx Added new route /payment-success that redirects immediately to /pricing?success=true.
frontend/src/routes/payment-canceled.tsx Added new route /payment-canceled that redirects immediately to /pricing?canceled=true.
frontend/public/.well-known/apple-app-site-association Updated Universal Links configuration to include new path patterns /payment-success*, /payment-canceled*, and /pricing* for the app ID to support payment-related deep links alongside existing auth paths.
frontend/src-tauri/capabilities/default.json
frontend/src-tauri/capabilities/mobile-ios.json
Expanded "opener:allow-open-url" permission URL patterns to include wildcard subdomains for "stripe.com", "stripe.network", and "zaprite.com" domains, allowing the app to open these URLs externally in Tauri environments.

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
Loading
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)
Loading

Possibly related PRs

  • OpenSecretCloud/Maple#7: Restructures and enhances type safety and documentation for existing routes in routeTree.gen.ts, related by affecting route definitions alongside the new payment routes added here.

Poem

In Maple’s warren, code hops anew,
Payment flows now know what to do!
Tauri or browser, each finds its way,
Deep links guide rabbits through checkout’s ballet.
Buttons are smarter, feedback is bright—
With every click, things just feel right.
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33bb4b3 and 47fc54d.

📒 Files selected for processing (11)
  • frontend/public/.well-known/apple-app-site-association (1 hunks)
  • frontend/src-tauri/capabilities/default.json (1 hunks)
  • frontend/src-tauri/capabilities/mobile-ios.json (1 hunks)
  • frontend/src/billing/billingApi.ts (2 hunks)
  • frontend/src/components/DeepLinkHandler.tsx (1 hunks)
  • frontend/src/routeTree.gen.ts (13 hunks)
  • frontend/src/routes/login.tsx (0 hunks)
  • frontend/src/routes/payment-canceled.tsx (1 hunks)
  • frontend/src/routes/payment-success.tsx (1 hunks)
  • frontend/src/routes/pricing.tsx (6 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 (9)
  • frontend/src/routes/payment-canceled.tsx
  • frontend/public/.well-known/apple-app-site-association
  • frontend/src/routes/payment-success.tsx
  • frontend/src-tauri/capabilities/mobile-ios.json
  • frontend/src-tauri/capabilities/default.json
  • frontend/src/components/DeepLinkHandler.tsx
  • frontend/src/billing/billingApi.ts
  • frontend/src/routeTree.gen.ts
  • frontend/src/routes/pricing.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Cloudflare Pages
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented May 2, 2025

Deploying maple with  Cloudflare Pages  Cloudflare Pages

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

View logs

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.tsx for iOS-specific payment success/cancel flows
  • Implemented native Apple Sign-In with secure nonce handling in /frontend/src/routes/login.tsx and /frontend/src/routes/signup.tsx
  • Added Tauri environment detection and iOS-specific URL handling in /frontend/src/billing/billingApi.ts for 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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_url invocation + 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 nested isTauri / isTauriIOS checks here duplicate earlier logic in billingApi.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

📥 Commits

Reviewing files that changed from the base of the PR and between 2255080 and cd97219.

📒 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 the opener plugin is bundled on all Tauri targets
A runtime invoke("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 to tauri.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
disabled prevents clicks, yet useEffect (lines 510-516) programmatically triggers handleButtonClick, which in turn may reach newHandleSubscribe even for a Team-plan/Bitcoin combo.
Guard inside handleButtonClick as well:

if (useBitcoin && product.name === "Team") {
  return;
}

@AnthonyRonning AnthonyRonning force-pushed the apple-ios-pricing-page branch from cd97219 to 22b10d3 Compare May 2, 2025 20:26
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (6)
frontend/src/components/DeepLinkHandler.tsx (3)

27-28: ⚠️ Potential issue

Potential 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 like cloud.opensecret.maple://v1/auth?..., pathname is /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 suggestion

Using window.location.href causes full page reload.

Direct URL manipulation with window.location.href forces 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 issue

Query parameters will be filtered out by the validation.

In pricing.tsx, the validateSearch function expects success and canceled to 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 issue

Success 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 suggestion

Deep 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 suggestion

Duplicate 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=true and payment_success=true for success detection, and both canceled=true and payment_canceled for 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 isIOS variable is included in the dependency array for handleButtonClick but 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

📥 Commits

Reviewing files that changed from the base of the PR and between cd97219 and 22b10d3.

📒 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.

@AnthonyRonning AnthonyRonning force-pushed the apple-ios-pricing-page branch 4 times, most recently from 970f2c4 to 33bb4b3 Compare May 2, 2025 21:37
@AnthonyRonning AnthonyRonning force-pushed the apple-ios-pricing-page branch from 33bb4b3 to 47fc54d Compare May 2, 2025 21:48
@AnthonyRonning AnthonyRonning merged commit 9a4a2a7 into master May 5, 2025
2 checks passed
@AnthonyRonning AnthonyRonning deleted the apple-ios-pricing-page branch May 5, 2025 15:10
@coderabbitai coderabbitai bot mentioned this pull request May 23, 2025
@coderabbitai coderabbitai bot mentioned this pull request Sep 19, 2025
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.

1 participant