├── .eslintrc.json
├── src
└── app
│ ├── globals.css
│ ├── favicon.ico
│ ├── layout.js
│ ├── success
│ └── page.jsx
│ ├── products.js
│ ├── api
│ ├── checkout
│ │ └── route.js
│ └── webhook
│ │ └── route.js
│ └── page.jsx
├── jsconfig.json
├── next.config.mjs
├── postcss.config.js
├── .gitignore
├── tailwind.config.js
├── package.json
├── public
├── vercel.svg
└── next.svg
└── README.md
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FaztWeb/nextjs-stripe-webhook/HEAD/src/app/favicon.ico
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./src/*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/src/app/layout.js:
--------------------------------------------------------------------------------
1 | import { Inter } from "next/font/google";
2 | import "./globals.css";
3 |
4 | const inter = Inter({ subsets: ["latin"] });
5 |
6 | export const metadata = {
7 | title: "Create Next App",
8 | description: "Generated by create next app",
9 | };
10 |
11 | export default function RootLayout({ children }) {
12 | return (
13 |
14 |
{children}
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/src/app/success/page.jsx:
--------------------------------------------------------------------------------
1 | function Page() {
2 | return (
3 |
14 | );
15 | }
16 | export default Page;
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env
30 | .env*.local
31 |
32 | # vercel
33 | .vercel
34 |
35 | # typescript
36 | *.tsbuildinfo
37 | next-env.d.ts
38 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
5 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
7 | ],
8 | theme: {
9 | extend: {
10 | backgroundImage: {
11 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
12 | "gradient-conic":
13 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
14 | },
15 | },
16 | },
17 | plugins: [],
18 | };
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-stripe-webhook",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "next": "14.1.0",
13 | "react": "^18",
14 | "react-dom": "^18",
15 | "stripe": "^14.17.0"
16 | },
17 | "devDependencies": {
18 | "autoprefixer": "^10.0.1",
19 | "eslint": "^8",
20 | "eslint-config-next": "14.1.0",
21 | "postcss": "^8",
22 | "tailwindcss": "^3.3.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/products.js:
--------------------------------------------------------------------------------
1 | export const products = [
2 | {
3 | id: 1,
4 | name: 'Product 1',
5 | price: 1000,
6 | image: 'https://via.placeholder.com/150'
7 | },
8 | {
9 | id: 2,
10 | name: 'Product 2',
11 | price: 2000,
12 | image: 'https://via.placeholder.com/150'
13 | },
14 | {
15 | id: 3,
16 | name: 'Product 3',
17 | price: 3000,
18 | image: 'https://via.placeholder.com/150'
19 | },
20 | {
21 | id: 4,
22 | name: 'Product 4',
23 | price: 4000,
24 | image: 'https://via.placeholder.com/150'
25 | },
26 | {
27 | id: 5,
28 | name: 'Product 5',
29 | price: 5000,
30 | image: 'https://via.placeholder.com/150'
31 | },
32 | ]
--------------------------------------------------------------------------------
/src/app/api/checkout/route.js:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import Stripe from "stripe";
3 |
4 | const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
5 |
6 | export async function POST(request) {
7 | const body = await request.json();
8 |
9 | const session = await stripe.checkout.sessions.create({
10 | success_url: `${process.env.DOMAIN_URL}/success`,
11 | line_items: [
12 | {
13 | price_data: {
14 | currency: "usd",
15 | product_data: {
16 | name: body.name,
17 | images: [body.image],
18 | },
19 | unit_amount: body.price,
20 | },
21 | quantity: 1,
22 | },
23 | ],
24 | metadata: {
25 | productId: body.id
26 | },
27 | mode: "payment",
28 | });
29 |
30 |
31 | return NextResponse.json(session);
32 | }
33 |
--------------------------------------------------------------------------------
/src/app/api/webhook/route.js:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import { headers } from "next/headers";
3 | import Stripe from "stripe";
4 |
5 | const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
6 |
7 | const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
8 |
9 | export async function POST(request) {
10 | const body = await request.text();
11 | const headersList = headers();
12 | const sig = headersList.get("stripe-signature");
13 |
14 | let event;
15 |
16 | try {
17 | event = stripe.webhooks.constructEvent(body, sig, endpointSecret);
18 | } catch (error) {
19 | console.log(error);
20 | return NextResponse.json({ error: error.message }, { status: 400 });
21 | }
22 |
23 | switch (event.type) {
24 | case "checkout.session.completed":
25 | const checkoutSessionCompleted = event.data.object;
26 |
27 | // guardar en una base de datos
28 | console.log(
29 | "Consultado producto con id",
30 | checkoutSessionCompleted.metadata.productId
31 | );
32 |
33 | // enviar un correo
34 |
35 | console.log({ checkoutSessionCompleted });
36 | break;
37 | default:
38 | console.log(`Evento no manejado: ${event.type}`);
39 | }
40 |
41 | return new Response(null, { status: 200 });
42 | }
43 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/app/page.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { products } from "./products";
3 |
4 | function App() {
5 |
6 | const handlePay = async (product) => {
7 | const res = await fetch('/api/checkout', {
8 | method: "POST",
9 | body: JSON.stringify(product),
10 | headers: {
11 | "Content-Type": "application/json"
12 | }
13 | })
14 | const session = await res.json()
15 | window.location = session.url
16 | }
17 |
18 | return (
19 |
20 |
Productos
21 |
22 |
23 | {products.map((product, i) => (
24 |
28 |
{product.name}
29 |
${product.price / 100}
30 |
31 |
handlePay(product)}
33 | >
34 | Pagar
35 |
36 |
37 | ))}
38 |
39 |
40 | );
41 | }
42 |
43 | export default App;
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------