├── .expo-shared └── assets.json ├── .gitignore ├── App.js ├── README.md ├── app.json ├── assets ├── adaptive-icon.png ├── favicon.png ├── icon.png └── splash.png ├── babel.config.js ├── components ├── Button.js ├── CartItem.js ├── Header.js ├── Payment.js ├── ProductCard.js ├── ProductInfo │ ├── Image.js │ └── MetaInfo.js ├── RadioButton.js ├── ShippingAddress.js └── Toast.js ├── constants ├── stripe.js └── url.js ├── cover.png ├── package.json ├── screens ├── Cart.js ├── Checkout.js ├── ProductInfo.js └── Products.js └── yarn.lock /.expo-shared/assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, 3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import { Router, Scene, Stack } from "react-native-router-flux"; 2 | import Products from "./screens/Products"; 3 | import ProductInfo from "./screens/ProductInfo"; 4 | import axios from "axios"; 5 | import baseURL from "./constants/url"; 6 | import { useEffect } from "react"; 7 | import Cart from "./screens/Cart"; 8 | import Checkout from "./screens/Checkout"; 9 | import { Provider as PaperProvider } from "react-native-paper"; 10 | import AsyncStorage from "@react-native-async-storage/async-storage"; 11 | import { publishable_key } from "./constants/stripe"; 12 | import { StripeProvider } from "@stripe/stripe-react-native"; 13 | export default function App() { 14 | const getCartId = () => { 15 | axios.post(`${baseURL}/store/carts`).then((res) => { 16 | AsyncStorage.setItem("cart_id", res.data.cart.id); 17 | }); 18 | }; 19 | // Check cart_id 20 | const checkCartId = async () => { 21 | const cartId = await AsyncStorage.getItem("cart_id"); 22 | if (!cartId) { 23 | getCartId(); 24 | } 25 | }; 26 | 27 | useEffect(() => { 28 | checkCartId(); 29 | }, []); 30 | 31 | return ( 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## React Native Medusa 4 | ![Medusa Hackathon 2022](https://i.ibb.co/nPs84pQ/cover.png) 5 | 6 | ## About 7 | 8 | ### Participants 9 | Suhail - @SuhailKakar 10 | 11 | ### Description 12 | 13 | An open source ecommerce mobile application built using Medusa and React Native Expo. It includes products screen, cart, checkout and payment. 14 | 15 | ### Preview 16 | 17 | ![Demo](https://s5.gifyu.com/images/Screen_Recording_2022-09-01_at_11.13.49_PM_1-online-video-cutter.com-1-1.gif) 18 | 19 | 20 | ## Set up Project 21 | 22 | ### Prerequisites 23 | Before you start with the tutorial make sure you have 24 | 25 | - [Node.js](https://nodejs.org/en/) v14 or greater installed on your machine 26 | - [Expo CLI](https://expo.dev/) 27 | - [Medusa server](https://docs.medusajs.com/quickstart/quick-start/) v14 or greater installed on your machine 28 | - Stripe account 29 | - [Stripe plugin](https://docs.medusajs.com/add-plugins/stripe/) is required on the Medusa server 30 | 31 | ### Install Project 32 | 33 | 1. Clone the repository: 34 | 35 | ```bash 36 | git clone https://github.com/suhailkakar/react-native-medusajs 37 | ``` 38 | 39 | 2. Change directory and install dependencies: 40 | 41 | ```bash 42 | cd react-native-medusajs 43 | npm install 44 | ``` 45 | 4. Start the app 46 | ``` 47 | expo start 48 | ``` 49 | 50 | ## Resources 51 | - [Medusa’s GitHub repository](https://github.com/medusajs/medusa) 52 | - [Medusa Admin Panel](https://github.com/medusajs/admin) 53 | - [Medusa Documentation](https://docs.medusajs.com/) 54 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "medusa-store", 4 | "slug": "medusa-store", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "userInterfaceStyle": "light", 9 | "splash": { 10 | "image": "./assets/splash.png", 11 | "resizeMode": "contain", 12 | "backgroundColor": "#ffffff" 13 | }, 14 | "updates": { 15 | "fallbackToCacheTimeout": 0 16 | }, 17 | "assetBundlePatterns": [ 18 | "**/*" 19 | ], 20 | "ios": { 21 | "supportsTablet": true 22 | }, 23 | "android": { 24 | "adaptiveIcon": { 25 | "foregroundImage": "./assets/adaptive-icon.png", 26 | "backgroundColor": "#FFFFFF" 27 | } 28 | }, 29 | "plugins": [ 30 | [ 31 | "@stripe/stripe-react-native", 32 | { 33 | "enableGooglePay": false 34 | } 35 | ] 36 | ], 37 | "web": { 38 | "favicon": "./assets/favicon.png" 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/react-native-medusajs/2fe82f3c566e9ac44164aa99954b25652a73fb03/assets/adaptive-icon.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/react-native-medusajs/2fe82f3c566e9ac44164aa99954b25652a73fb03/assets/favicon.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/react-native-medusajs/2fe82f3c566e9ac44164aa99954b25652a73fb03/assets/icon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhailkakar/react-native-medusajs/2fe82f3c566e9ac44164aa99954b25652a73fb03/assets/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ["babel-preset-expo"], 5 | plugins: ["react-native-reanimated/plugin"], 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /components/Button.js: -------------------------------------------------------------------------------- 1 | import { View, Text, StyleSheet } from "react-native"; 2 | import React from "react"; 3 | import { widthToDp } from "rn-responsive-screen"; 4 | 5 | export default function Button({ title, onPress, style, textSize, large }) { 6 | return ( 7 | 8 | 16 | {title} 17 | 18 | 19 | ); 20 | } 21 | 22 | const styles = StyleSheet.create({ 23 | container: { 24 | backgroundColor: "#C37AFF", 25 | padding: 5, 26 | width: widthToDp(20), 27 | alignItems: "center", 28 | justifyContent: "center", 29 | borderRadius: 59, 30 | }, 31 | large: { 32 | width: "100%", 33 | marginTop: 10, 34 | height: widthToDp(12), 35 | }, 36 | text: { 37 | color: "#fff", 38 | fontWeight: "bold", 39 | }, 40 | }); 41 | -------------------------------------------------------------------------------- /components/CartItem.js: -------------------------------------------------------------------------------- 1 | import { View, Text, StyleSheet, Image } from "react-native"; 2 | import React from "react"; 3 | import { heightToDp, width, widthToDp } from "rn-responsive-screen"; 4 | 5 | export default function CartItem({ product }) { 6 | return ( 7 | 8 | 9 | 10 | 11 | {product.title} 12 | 13 | {product.description} • ${product.unit_price / 100} 14 | 15 | 16 | 17 | ${product.total / 100} 18 | x{product.quantity} 19 | 20 | 21 | 22 | ); 23 | } 24 | 25 | const styles = StyleSheet.create({ 26 | container: { 27 | marginTop: 20, 28 | flexDirection: "row", 29 | borderBottomWidth: 1, 30 | paddingBottom: 10, 31 | borderColor: "#e6e6e6", 32 | width: widthToDp("90%"), 33 | }, 34 | image: { 35 | width: widthToDp(30), 36 | height: heightToDp(30), 37 | borderRadius: 10, 38 | }, 39 | title: { 40 | fontSize: widthToDp(4), 41 | fontWeight: "bold", 42 | }, 43 | footer: { 44 | flexDirection: "row", 45 | justifyContent: "space-between", 46 | }, 47 | info: { 48 | marginLeft: widthToDp(3), 49 | flexDirection: "column", 50 | justifyContent: "space-between", 51 | marginVertical: heightToDp(2), 52 | width: widthToDp(50), 53 | }, 54 | description: { 55 | fontSize: widthToDp(3.5), 56 | color: "#8e8e93", 57 | marginTop: heightToDp(2), 58 | }, 59 | 60 | price: { 61 | fontSize: widthToDp(4), 62 | }, 63 | quantity: { 64 | fontSize: widthToDp(4), 65 | }, 66 | }); 67 | -------------------------------------------------------------------------------- /components/Header.js: -------------------------------------------------------------------------------- 1 | import { View, Image, StyleSheet, Text } from "react-native"; 2 | import React from "react"; 3 | import { widthToDp } from "rn-responsive-screen"; 4 | 5 | export default function Header({ title }) { 6 | return ( 7 | 8 | 14 | {title} 15 | 16 | ); 17 | } 18 | const styles = StyleSheet.create({ 19 | container: { 20 | flexDirection: "row", 21 | alignItems: "center", 22 | justifyContent: "center", 23 | width: widthToDp(100), 24 | marginBottom: 10, 25 | }, 26 | title: { 27 | fontSize: 20, 28 | fontWeight: "500", 29 | }, 30 | logo: { 31 | width: 50, 32 | height: 50, 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /components/Payment.js: -------------------------------------------------------------------------------- 1 | import { View, Text } from "react-native"; 2 | import React from "react"; 3 | import { 4 | CreditCardInput, 5 | LiteCreditCardInput, 6 | } from "react-native-credit-card-input"; 7 | export default function Payment({ onChange }) { 8 | return ( 9 | 19 | 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /components/ProductCard.js: -------------------------------------------------------------------------------- 1 | import { View, Text, Image, StyleSheet } from "react-native"; 2 | import React from "react"; 3 | import { widthToDp, heightToDp } from "rn-responsive-screen"; 4 | import Button from "./Button"; 5 | 6 | export default function ProductCard({ key, product }) { 7 | return ( 8 | 9 | 15 | {product.title} 16 | {product.handle} 17 | 18 | 19 | ${product.variants[0].prices[1].amount / 100} 20 | 21 | 22 |