├── .env.example ├── .gitignore ├── README.md ├── app ├── .env.example ├── .expo-shared │ └── assets.json ├── .gitignore ├── App.tsx ├── app.config.js ├── app.json ├── assets │ ├── adaptive-icon.png │ ├── favicon.png │ ├── icon.png │ └── splash.png ├── babel.config.js ├── components │ ├── Auth.tsx │ └── Payment.tsx ├── lib │ └── supabase.ts ├── package.json ├── tsconfig.json └── yarn.lock ├── demo.gif ├── expo-stripe-payments-with-supabase-functions.code-workspace ├── schema.sql └── supabase ├── config.toml ├── functions ├── .vscode │ └── settings.json ├── _utils │ ├── db_types.ts │ ├── stripe.ts │ └── supabase.ts ├── hello │ └── index.ts └── payment-sheet │ └── index.ts └── migrations └── 20220330083059_init.sql /.env.example: -------------------------------------------------------------------------------- 1 | # Supabase API keys not needed as they are injected via the CLI 2 | 3 | # Stripe API keys - see https://stripe.com/docs/development/quickstart#api-keys 4 | STRIPE_PUBLISHABLE_KEY=pk_test 5 | STRIPE_SECRET_KEY=sk_test 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | 3 | # Supabase 4 | **/supabase/.branches 5 | **/supabase/.temp 6 | **/supabase/.env 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Expo Stripe Payments with Supabase Functions 2 | 3 | This is a Expo React Native Supabase example app, showing how to process payments with Supabase Functions for authenticated customers. 4 | 5 | ![Demo gif](./demo.gif) 6 | 7 | ## Setup 8 | 9 | ### Create new Supabase project 10 | 11 | - [Create a new Supabase project](https://app.supabase.io/) 12 | - Navigate to the [Auth settings](https://app.supabase.io/project/_/auth/settings) and turn off the toggle next to "Enable email confirmations". (Note: this is only for testing. In production please enable this setting!) 13 | - Navigate to the [SQL Editor](https://app.supabase.io/project/_/sql) and run the SQL from the [schema.sql](./schema.sql) file. 14 | 15 | ### Setup env vars 16 | 17 | - Set up env vars for Supabase Functions: 18 | - `cp .env.example .env` 19 | - Fill in your Stripe API keys from https://stripe.com/docs/development/quickstart#api-keys 20 | - Set up env vars for the Expo app: 21 | - `cp app/.env.example app/.env` 22 | - Fill in your _public_ Supabase keys from https://app.supabase.io/project/_/settings/api 23 | 24 | ### Supabase Edge Functions 25 | 26 | [Supabase Edge Functions](https://supabase.com/edge-functions) are written in TypeScript, run via Deno, and deployed with the Supabase CLI. Please [download](https://github.com/supabase/cli#install-the-cli) the latest version of the Supabase CLI, or [upgrade](https://github.com/supabase/cli#install-the-cli) it if you have it already installed. 27 | 28 | ### Develop locally 29 | 30 | - Run `supabase start` (make sure your Docker daemon is running.) 31 | - Run `supabase functions serve --env-file .env payment-sheet` 32 | - NOTE: no need to specify `SUPABASE_URL` and `SUPABASE_ANON_KEY` as they are automatically supplied for you from the linked project. 33 | - Run the Expo app in a separate terminal window: 34 | - `cd app` 35 | - `yarn` 36 | - `yarn start` or `yarn ios` or `yarn android` 37 | - Make some test moneys 💰🧧💵 38 | - Stop local development 39 | - Kill the "supabase functions serve watcher" (ctrl + c) 40 | - Run `supabase stop` to stop the Docker containers. 41 | 42 | ### Deploy 43 | 44 | - Generate access token and log in to CLI 45 | - Navigate to https://app.supabase.io/account/tokens 46 | - Click "Generate New Token" 47 | - Copy newly created token 48 | - run `supabase login` 49 | - Input your token when prompted 50 | - Link your project 51 | - Within your project root run `supabase link --project-ref your-project-ref` 52 | - Set up your secrets 53 | - Run `supabase secrets set --env-file .env` to set the env vars from your `.env` file. 54 | - You can run `supabase secrets list` to check that it worked and also to see what other env vars are set by default. 55 | - Deploy the function 56 | - Within your project root run `supabase functions deploy payment-sheet` 57 | - In youre [`./app/.env`](./app/.env) file remove the `SUPA_FUNCTION_LOCALHOST` variable and restart your Expo app. 58 | 59 | ## 👁⚡️👁 60 | 61 | \o/ That's it, you can now invoke your Supabase Function via the [`supabase-js`](https://supabase.com/docs/reference/javascript/invoke) and [`supabase-dart`](https://supabase.com/docs/reference/dart/invoke) client libraries. (More client libraries coming soon. Check the [supabase-community](https://github.com/supabase-community#client-libraries) org for details). 62 | 63 | For more info on Supabase Functions, check out the [docs](https://supabase.com/docs/guides/functions) and the [examples](https://github.com/supabase/supabase/tree/master/examples/edge-functions). 64 | -------------------------------------------------------------------------------- /app/.env.example: -------------------------------------------------------------------------------- 1 | # WARNING: these are included in your build. Don't expose secrets here! 2 | # https://reactnative.dev/docs/security#storing-sensitive-info 3 | 4 | # NOTE: You will have to restart your Expo app after changing this file! 5 | 6 | # Remove this variable once you've deployed your Supabase Function! 7 | SUPA_FUNCTION_LOCALHOST=true 8 | 9 | # Get these from your Supabase Dashboard: https://app.supabase.io/project/_/settings/api 10 | EXPO_PUBLIC_SUPABASE_URL= 11 | EXPO_PUBLIC_SUPABASE_ANON_KEY= -------------------------------------------------------------------------------- /app/.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | .env 5 | npm-debug.* 6 | *.jks 7 | *.p8 8 | *.p12 9 | *.key 10 | *.mobileprovision 11 | *.orig.* 12 | web-build/ 13 | 14 | # macOS 15 | .DS_Store 16 | -------------------------------------------------------------------------------- /app/App.tsx: -------------------------------------------------------------------------------- 1 | import "react-native-url-polyfill/auto"; 2 | import { useState, useEffect } from "react"; 3 | import { supabase } from "./lib/supabase"; 4 | import Auth from "./components/Auth"; 5 | import PaymentScreen from "./components/Payment"; 6 | import { View } from "react-native"; 7 | import { Session } from "@supabase/supabase-js"; 8 | 9 | export default function App() { 10 | const [session, setSession] = useState(null); 11 | 12 | useEffect(() => { 13 | supabase.auth 14 | .getSession() 15 | .then(({ data: { session } }) => setSession(session)); 16 | 17 | supabase.auth.onAuthStateChange((_event, session) => { 18 | setSession(session); 19 | }); 20 | }, []); 21 | 22 | return {session && session.user ? : }; 23 | } 24 | -------------------------------------------------------------------------------- /app/app.config.js: -------------------------------------------------------------------------------- 1 | import "dotenv/config"; 2 | 3 | export default { 4 | name: "Expo Stripe Payments with Supabase FUnctions", 5 | version: "1.0.0", 6 | extra: { 7 | // WARNING: these are included in your build. Don't expose secrets here! 8 | // https://reactnative.dev/docs/security#storing-sensitive-info 9 | EXPO_PUBLIC_SUPABASE_URL: process.env.SUPA_FUNCTION_LOCALHOST 10 | ? "http://localhost:54321" 11 | : process.env.EXPO_PUBLIC_SUPABASE_URL, 12 | EXPO_PUBLIC_SUPABASE_ANON_KEY: process.env.SUPA_FUNCTION_LOCALHOST 13 | ? "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs" 14 | : process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /app/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "expo-stripe-payments-with-supabase-functions", 4 | "slug": "expo-stripe-payments-with-supabase-functions", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": ["**/*"], 17 | "ios": { 18 | "supportsTablet": true 19 | }, 20 | "android": { 21 | "adaptiveIcon": { 22 | "foregroundImage": "./assets/adaptive-icon.png", 23 | "backgroundColor": "#FFFFFF" 24 | } 25 | }, 26 | "web": { 27 | "favicon": "./assets/favicon.png" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/expo-stripe-payments-with-supabase-functions/5462873f714aa118292ed5f36de1af5fadf28c53/app/assets/adaptive-icon.png -------------------------------------------------------------------------------- /app/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/expo-stripe-payments-with-supabase-functions/5462873f714aa118292ed5f36de1af5fadf28c53/app/assets/favicon.png -------------------------------------------------------------------------------- /app/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/expo-stripe-payments-with-supabase-functions/5462873f714aa118292ed5f36de1af5fadf28c53/app/assets/icon.png -------------------------------------------------------------------------------- /app/assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/expo-stripe-payments-with-supabase-functions/5462873f714aa118292ed5f36de1af5fadf28c53/app/assets/splash.png -------------------------------------------------------------------------------- /app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /app/components/Auth.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Alert, StyleSheet, View } from "react-native"; 3 | import { supabase } from "../lib/supabase"; 4 | import { Button, Input } from "react-native-elements"; 5 | 6 | export default function Auth() { 7 | const [email, setEmail] = useState(""); 8 | const [password, setPassword] = useState(""); 9 | const [loading, setLoading] = useState(false); 10 | 11 | async function signInWithEmail() { 12 | setLoading(true); 13 | const { error } = await supabase.auth.signInWithPassword({ 14 | email: email, 15 | password: password, 16 | }); 17 | 18 | if (error) { 19 | Alert.alert(error.message); 20 | setLoading(false); 21 | } 22 | } 23 | 24 | async function signUpWithEmail() { 25 | setLoading(true); 26 | const { error } = await supabase.auth.signUp({ 27 | email: email, 28 | password: password, 29 | }); 30 | 31 | if (error) { 32 | Alert.alert(error.message); 33 | setLoading(false); 34 | } 35 | } 36 | 37 | return ( 38 | 39 | 40 | setEmail(text)} 44 | value={email} 45 | placeholder="email@address.com" 46 | autoCapitalize={"none"} 47 | /> 48 | 49 | 50 | setPassword(text)} 54 | value={password} 55 | secureTextEntry={true} 56 | placeholder="Password" 57 | autoCapitalize={"none"} 58 | /> 59 | 60 | 61 |