├── apps ├── native │ ├── app-env.d.ts │ ├── .env.example │ ├── tsconfig.json │ ├── babel.config.js │ ├── index.js │ ├── Button.tsx │ ├── metro.config.js │ ├── package.json │ ├── app.config.ts │ ├── App.tsx │ └── .gitignore └── web │ ├── app-env.d.ts │ ├── app │ ├── favicon.ico │ ├── checkout_redirect │ │ ├── cancel │ │ │ └── page.tsx │ │ ├── success │ │ │ └── page.tsx │ │ └── layout.tsx │ ├── api │ │ └── stripe │ │ │ ├── stripe.ts │ │ │ ├── checkout-session │ │ │ └── route.ts │ │ │ └── webhook │ │ │ └── route.ts │ ├── layout.tsx │ ├── styles-provider.tsx │ ├── .well-known │ │ └── apple-app-site-association │ │ │ └── route.ts │ ├── globals.css │ └── page.tsx │ ├── next-env.d.ts │ ├── .env.example │ ├── .gitignore │ ├── package.json │ ├── tsconfig.json │ ├── public │ └── vercel.svg │ └── next.config.js ├── packages └── app │ ├── features │ ├── auth │ │ ├── persistence.ts │ │ ├── persistence.native.ts │ │ ├── server.ts │ │ └── client.ts │ ├── checkout │ │ ├── cancel │ │ │ └── screen.tsx │ │ └── success │ │ │ └── screen.tsx │ ├── user │ │ └── detail-screen.tsx │ └── home │ │ └── screen.tsx │ ├── provider │ ├── safe-area │ │ ├── index.native.tsx │ │ ├── use-safe-area.native.ts │ │ ├── index.tsx │ │ └── use-safe-area.ts │ ├── index.tsx │ └── navigation │ │ ├── index.tsx │ │ └── index.native.tsx │ ├── index.ts │ ├── package.json │ ├── components │ ├── h1.tsx │ └── button.tsx │ ├── env │ ├── server-env.ts │ └── public-env.ts │ ├── rnw-overrides.d.ts │ └── navigation │ └── native │ └── index.tsx ├── .yarnrc.yml ├── tsconfig.json ├── turbo.json ├── package.json ├── .gitignore ├── .vscode └── settings.json └── readme.md /apps/native/app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/web/app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /packages/app/features/auth/persistence.ts: -------------------------------------------------------------------------------- 1 | export default undefined 2 | -------------------------------------------------------------------------------- /apps/web/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vercel/ios-web-payments/HEAD/apps/web/app/favicon.ico -------------------------------------------------------------------------------- /apps/native/.env.example: -------------------------------------------------------------------------------- 1 | EXPO_PUBLIC_APP_URL= 2 | EXPO_PUBLIC_BUNDLE_IDENTIFIER= 3 | EXPO_PUBLIC_FIREBASE_CONFIG_JSON= -------------------------------------------------------------------------------- /packages/app/provider/safe-area/index.native.tsx: -------------------------------------------------------------------------------- 1 | export { SafeAreaProvider as SafeArea } from 'react-native-safe-area-context' 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableGlobalCache: false 4 | 5 | nodeLinker: node-modules 6 | 7 | yarnPath: .yarn/releases/yarn-4.7.0.cjs 8 | -------------------------------------------------------------------------------- /packages/app/index.ts: -------------------------------------------------------------------------------- 1 | // leave this blank 2 | // don't re-export files from this workspace. it'll break next.js tree shaking 3 | // https://github.com/vercel/next.js/issues/12557 4 | export {} 5 | -------------------------------------------------------------------------------- /apps/native/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig", 3 | "compilerOptions": { 4 | "paths": { 5 | "@firebase/auth": ["./node_modules/@firebase/auth/dist/index.rn.d.ts"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/web/app/checkout_redirect/cancel/page.tsx: -------------------------------------------------------------------------------- 1 | import { CheckoutErrorScreen } from 'app/features/checkout/cancel/screen' 2 | 3 | export default function CheckoutCancelPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/app/checkout_redirect/success/page.tsx: -------------------------------------------------------------------------------- 1 | import { CheckoutSuccessScreen } from 'app/features/checkout/success/screen' 2 | 3 | export default function CheckoutSuccessPage() { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /apps/native/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true) 3 | return { 4 | presets: [['babel-preset-expo', { jsxRuntime: 'automatic' }]], 5 | plugins: ['react-native-reanimated/plugin'], 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/web/app/api/stripe/stripe.ts: -------------------------------------------------------------------------------- 1 | import { SERVER_ENV } from 'app/env/server-env' 2 | import Stripe from 'stripe' 3 | 4 | export const getStripe = () => { 5 | return new Stripe(SERVER_ENV.STRIPE_SECRET_KEY, { 6 | typescript: true, 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strictNullChecks": true, 4 | "noUncheckedIndexedAccess": true, 5 | "paths": { 6 | "app/*": ["./packages/app/*"] 7 | }, 8 | "baseUrl": ".", 9 | "jsx": "react-jsx" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apps/web/.env.example: -------------------------------------------------------------------------------- 1 | APPLE_TEAM_ID= 2 | FIREBASE_SERVICE_ACCOUNT_JSON= 3 | NEXT_PUBLIC_APP_URL= 4 | NEXT_PUBLIC_BUNDLE_IDENTIFIER= 5 | NEXT_PUBLIC_FIREBASE_CONFIG_JSON= 6 | NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY= 7 | STRIPE_PRICE_ID= 8 | STRIPE_SECRET_KEY= 9 | STRIPE_WEBHOOK_SECRET= -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | 4 | "tasks": { 5 | "build": { 6 | "dependsOn": ["^build"], 7 | "outputs": [".next/**", "!.next/cache/**"], 8 | "inputs": ["$TURBO_DEFAULT$", ".env"] 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/app/provider/safe-area/use-safe-area.native.ts: -------------------------------------------------------------------------------- 1 | import { useSafeAreaInsets } from 'react-native-safe-area-context' 2 | 3 | const useSafeArea = useSafeAreaInsets 4 | 5 | // `export { useSafeAreaInsets as useSafeArea }` breaks autoimport, so do this instead 6 | export { useSafeArea } 7 | -------------------------------------------------------------------------------- /packages/app/features/auth/persistence.native.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore works on native 2 | import { getReactNativePersistence } from 'firebase/auth' 3 | import ReactNativeAsyncStorage from '@react-native-async-storage/async-storage' 4 | 5 | export default getReactNativePersistence(ReactNativeAsyncStorage) 6 | -------------------------------------------------------------------------------- /apps/native/index.js: -------------------------------------------------------------------------------- 1 | import { registerRootComponent } from 'expo' 2 | 3 | import App from './App' 4 | 5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App); 6 | // It also ensures that whether you load the app in Expo Go or in a native build, 7 | // the environment is set up appropriately 8 | registerRootComponent(App) 9 | -------------------------------------------------------------------------------- /packages/app/features/checkout/cancel/screen.tsx: -------------------------------------------------------------------------------- 1 | import { H1 } from 'app/components/h1' 2 | import { View, Text } from 'react-native' 3 | 4 | export function CheckoutErrorScreen() { 5 | return ( 6 | 7 |

Checkout canceled!

8 |
9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /packages/app/provider/index.tsx: -------------------------------------------------------------------------------- 1 | import { SafeArea } from 'app/provider/safe-area' 2 | import { NavigationProvider } from './navigation' 3 | 4 | export function Provider({ children }: { children: React.ReactNode }) { 5 | return ( 6 | 7 | {children} 8 | 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /packages/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.0", 3 | "name": "app", 4 | "main": "index.ts", 5 | "dependencies": { 6 | "@react-navigation/native": "^7.0.15", 7 | "@react-navigation/native-stack": "^7.2.1", 8 | "dripsy": "^4.3.3", 9 | "moti": "^0.30.0", 10 | "solito": "4.4.1" 11 | }, 12 | "sideEffects": false 13 | } 14 | -------------------------------------------------------------------------------- /packages/app/provider/navigation/index.tsx: -------------------------------------------------------------------------------- 1 | // on Web, we don't use React Navigation, so we avoid the provider altogether 2 | // instead, we just have a no-op here 3 | // for more, see: https://solito.dev/recipes/tree-shaking 4 | 5 | export const NavigationProvider = ({ 6 | children, 7 | }: { 8 | children: React.ReactElement 9 | }) => <>{children} 10 | -------------------------------------------------------------------------------- /packages/app/components/h1.tsx: -------------------------------------------------------------------------------- 1 | import { Text } from 'react-native' 2 | 3 | export const H1 = ({ children }: { children: React.ReactNode }) => { 4 | return ( 5 | 13 | {children} 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /packages/app/features/user/detail-screen.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text, Pressable } from 'react-native' 2 | import { useRouter } from 'solito/navigation' 3 | 4 | export function UserDetailScreen() { 5 | const router = useRouter() 6 | return ( 7 | 8 | router.back()}> 9 | 👈 Go Home 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /packages/app/features/checkout/success/screen.tsx: -------------------------------------------------------------------------------- 1 | import { H1 } from 'app/components/h1' 2 | import { View, Text } from 'react-native' 3 | 4 | export function CheckoutSuccessScreen() { 5 | return ( 6 | 14 |

15 |

Checkout successful!

16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /apps/web/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { StylesProvider } from './styles-provider' 2 | import './globals.css' 3 | 4 | export const metadata = { 5 | title: 'Create Solito App', 6 | description: 'Generated by create Solito app', 7 | } 8 | 9 | export default function RootLayout({ 10 | children, 11 | }: { 12 | children: React.ReactNode 13 | }) { 14 | return ( 15 | 16 | 17 | {children} 18 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /apps/web/app/styles-provider.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { useServerInsertedHTML } from 'next/navigation' 3 | import { StyleSheet } from 'react-native' 4 | 5 | export function StylesProvider({ children }: { children: React.ReactNode }) { 6 | useServerInsertedHTML(() => { 7 | // @ts-ignore 8 | const sheet = StyleSheet.getSheet() 9 | return ( 10 |