Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update ecommerce search scaffold #77

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,7 @@ dist
sw.*

# Mac OSX
.DS_Store
.DS_Store
# Local Netlify folder
.netlify
.history
67 changes: 48 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,59 @@
# 🛍 Ecommerce Store with Netlify Functions and Stripe
# 🛍 E-Commerce Store with Nuxt, Netlify Functions, Tigris Cloud and Stripe

> A serverless function to process stripe payments with Nuxt, Netlify, and Lambda
> This is forked from [sdras/ecommerce-netlify](https://github.com/sdras/ecommerce-netlify).
>
> We have made the e-commerce site dynamic by using
> [Tigris](https://www.tigrisdata.com/jamstack) as the backend and also
> added search capability. Tigris is responsible for storing the product
> catalog and providing querying and search capabilities. It is integrated
> with Netlify Functions.

Demo site is here: [E-Commerce Store](https://ecommerce-netlify.netlify.com/)
Demo site is here: [E-Commerce Store](https://nuxt-ecommerce-netlify.netlify.app/)

![screenshot of site](https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/ecommerce-screenshot.jpg "E-Commerce Netlify Site")

There are two articles explaining how this site is set up:
* Explanation of Netlify Functions and Stripe: [Let's Build a JAMstack E-Commerce Store with Netlify Functions](https://css-tricks.com/lets-build-a-jamstack-e-commerce-store-with-netlify-functions/)
* Explanation of dynamic routing in Nuxt for the individual product pages: [Creating Dynamic Routes in Nuxt Application](https://css-tricks.com/creating-dynamic-routes-in-a-nuxt-application/)
# ⚙️ Deploying the site

## Build Setup
All you need is a [Github](https://github.com),
[Netlify](https://www.netlify.com/) and Tigris account
([sign up for a free account](https://www.tigrisdata.com/jamstack#signup-form)).
Now, Hit "Deploy" and follow instructions to deploy app to your Netlify account.

``` bash
# install dependencies
$ yarn install or npm run install
[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/tigrisdata/tigris-netlify-ecommerce&utm_source=github)

# serve with hot reload at localhost:3000
$ yarn dev or npm run dev
## 📖 Building & Running locally

# build for production and launch server
$ yarn build or npm run build
$ yarn start or npm run start
### Prerequisites

# generate static project
$ yarn generate or npm run generate
```
1. Tigris installed on your dev computer
- For **macOS**: `brew install tigrisdata/tigris/tigris-cli`
- Other operating systems: [See installation instructions here](https://docs.tigrisdata.com/cli/installation)
2. Netlify CLI installed on your dev computer
```shell
npm install netlify-cli -g
```
3. Node.js version 16+

For detailed explanation on how things work, checkout [Nuxt.js docs](https://nuxtjs.org).
### Instructions

1. Install dependencies
```shell
npm install
```
2. Start Tigris local development environment
```shell
tigris dev start
```
3. Run the app server locally with hot reload to preview your site
```shell
netlify dev
```

:tada: All done. Feel free to play around. :tada:

## Helpful links

- Getting started with Tigris: [Quickstart](https://docs.tigrisdata.com/quickstart)
- Explanation of Netlify Functions and Stripe: [Let's Build a Jamstack E-Commerce Store with Netlify Functions](https://css-tricks.com/lets-build-a-jamstack-e-commerce-store-with-netlify-functions/)
- Explanation of dynamic routing in Nuxt for the individual product pages:
[Creating Dynamic Routes in Nuxt Application](https://css-tricks.com/creating-dynamic-routes-in-a-nuxt-application/)
1 change: 1 addition & 0 deletions components/AppFeaturedProducts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
export default {
computed: {
featuredProducts() {
console.log(this.$store.getters.featuredProducts)
return this.$store.getters.featuredProducts;
}
}
Expand Down
39 changes: 39 additions & 0 deletions db/models/products.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
Field,
PrimaryKey,
TigrisCollection,
TigrisDataTypes,
} from "@tigrisdata/core";

@TigrisCollection("products")
export class Product {
@PrimaryKey(TigrisDataTypes.INT32, { order: 1, autoGenerate: true })
id!: number;

@Field(TigrisDataTypes.STRING)
color: string;

@Field(TigrisDataTypes.STRING)
description: string;

@Field(TigrisDataTypes.STRING)
gender: string;

@Field(TigrisDataTypes.STRING)
name: string;

@Field(TigrisDataTypes.STRING)
reviews: string;

@Field(TigrisDataTypes.NUMBER)
startrating: number;

@Field(TigrisDataTypes.NUMBER)
price: number;

@Field(TigrisDataTypes.ARRAY, { elements: TigrisDataTypes.STRING })
sizes: Array<string>

@Field(TigrisDataTypes.STRING)
img: string;
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
// An endpoint that calculates the order total and creates a
// PaymentIntent on Stripe
// An endpoint that calculates the order total and creates a
// PaymentIntent on Stripe

import tigrisDB from "~/lib/tigris";
import { Product } from "../db/models/products";

require("dotenv").config();
const axios = require("axios");
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY),
const stripeCreatePayment = require("stripe")(process.env.STRIPE_SECRET_KEY),
headers = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Content-Type"
"Access-Control-Allow-Headers": "Content-Type",
};

exports.handler = async (event, context) => {
// CORS
if (event.httpMethod === "OPTIONS") {
return {
statusCode: 200,
headers
headers,
};
}

Expand All @@ -28,36 +31,41 @@ exports.handler = async (event, context) => {
statusCode: 400,
headers,
body: JSON.stringify({
status: "missing information"
})
status: "missing information",
}),
};
}

const productCollection = tigrisDB.getCollection<Product>(Product);

// Stripe payment processing begins here
try {
// Always calculate the order amount on your server to prevent customers
// from manipulating the order amount from the client
// Here we will use a simple json file to represent inventory
// but you could replace this with a DB lookup
const storeDatabase = await axios.get(
"https://ecommerce-netlify.netlify.app/storedata.json"
);
let amount = 0;
for (let item of data.items) {
const product = await productCollection.findOne({
filter: { id: item.id },
});

if (product == undefined) {
continue;
}

amount = amount + product.price * item.quantity;
}

const amount = data.items.reduce((prev, item) => {
// lookup item information from "database" and calculate total amount
const itemData = storeDatabase.data.find(
storeItem => storeItem.id === item.id
);
return prev + itemData.price * 100 * item.quantity;
}, 0);
console.log("amount charging ", amount);

// Create a PaymentIntent on Stripe
// A PaymentIntent represents your customer's intent to pay
// and needs to be confirmed on the client to finalize the payment
const paymentIntent = await stripe.paymentIntents.create({
const paymentIntent = await stripeCreatePayment.paymentIntents.create({
currency: "usd",
amount: amount,
description: "Order from store"
description: "Order from store",
});

// Send the client_secret to the client
Expand All @@ -67,8 +75,8 @@ exports.handler = async (event, context) => {
statusCode: 200,
headers,
body: JSON.stringify({
clientSecret: paymentIntent.client_secret
})
clientSecret: paymentIntent.client_secret,
}),
};
} catch (err) {
console.log(err);
Expand All @@ -77,8 +85,8 @@ exports.handler = async (event, context) => {
statusCode: 400,
headers,
body: JSON.stringify({
status: err
})
status: err,
}),
};
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// For more information read https://stripe.com/docs/webhooks
require("dotenv").config();

const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const stripeHandlePayment = require("stripe")(process.env.STRIPE_SECRET_KEY);

// Create your webhook in the Stripe dashboard at https://dashboard.stripe.com/webhooks
// Use the secret listed in the "Signing secret" section
Expand All @@ -15,7 +15,7 @@ exports.handler = async (event, context) => {

try {
// Verifies that the event was sent by Stripe and deserializes the event
stripeEvent = stripe.webhooks.constructEvent(
stripeEvent = stripeHandlePayment.webhooks.constructEvent(
event.body,
sig,
endpointSecret
Expand Down
26 changes: 26 additions & 0 deletions functions/read-all-products.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Handler } from "@netlify/functions";
import tigrisDB from "~/lib/tigris";
import { Product } from "~/db/models/products";

const handler: Handler = async (event, context) => {
const productCollection = tigrisDB.getCollection<Product>(Product);

try {
const productCursor = productCollection.findMany();
const products = await productCursor.toArray();
return {
statusCode: 200,
body: JSON.stringify(products)
};
} catch (err) {
console.log(err);
return {
statusCode: 500,
body: JSON.stringify({
status: err
})
}
}
};

export {handler};
3 changes: 3 additions & 0 deletions layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export default {
components: {
AppFooter,
AppNav
},
created() {
this.$store.dispatch("getAllProducts");
}
};
</script>
Expand Down
7 changes: 7 additions & 0 deletions lib/tigris.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Tigris } from "@tigrisdata/core";

const tigrisClient = new Tigris();
const tigrisDb = tigrisClient.getDatabase();

// export to share DB across modules
export default tigrisDb;
11 changes: 10 additions & 1 deletion netlify.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
[build]
functions = "functions"
functions = "functions"
[context.dev]
NODE_ENV = "development"
environment = {TIGRIS_URI = "localhost:8081"}
[template.environment]
TIGRIS_URI = "api.preview.tigrisdata.cloud"
TIGRIS_CLIENT_ID = "change me for your tigris client id"
TIGRIS_CLIENT_SECRET = "change me for your tigris client secret"
[functions]
external_node_modules = ["app-root-path", "@tigrisdata/core"]
25 changes: 8 additions & 17 deletions nuxt.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import data from './static/storedata.json'
let dynamicRoutes = () => {
return new Promise(resolve => {
resolve(data.map(el => `product/${el.id}`))
})
}

export default {
mode: 'universal',
/*
** Nuxt target
** See https://nuxtjs.org/api/configuration-target
*/
target: 'static',
/*
** Headers of the page
*/
Expand All @@ -23,18 +20,15 @@ export default {
content: process.env.npm_package_description || ''
}
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
{
rel: 'stylesheet',
href:
'https://fonts.googleapis.com/css?family=Montserrat:300,600|PT+Serif&display=swap'
}
]
},
generate: {
routes: dynamicRoutes
},
/*
** Customize the progress-bar color
*/
Expand All @@ -55,9 +49,6 @@ export default {
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend(config, ctx) {}
}
},
buildModules: ['@nuxt/typescript-build']
}
Loading