Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit f3fd6d1

Browse files
committedFeb 13, 2020
Initial commit.
0 parents  commit f3fd6d1

32 files changed

+14509
-0
lines changed
 

‎.env.example

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Stripe keys
2+
STRIPE_PUBLISHABLE_KEY=pk_12345
3+
STRIPE_SECRET_KEY=sk_12345
4+
STRIPE_WEBHOOK_SECRET=whsec_1234

‎.gitignore

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.env
2+
.DS_Store
3+
.vscode
4+
5+
# Node files
6+
node_modules/
7+
8+
# Typescript
9+
dist
10+
11+
# Next.js
12+
.next
13+
.now

‎README.md

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Sample using Next.js, TypeScript, and react-stripe-js 🔒💸
2+
3+
- Demo: https://nextjs-typescript-react-stripe-js.now.sh/
4+
- CodeSandbox: https://codesandbox.io/s/github/stripe-samples/nextjs-typescript-react-stripe-js
5+
- Tutorial: https://dev.to/thorwebdev/type-safe-payments-with-next-js-typescript-and-stripe-4jo7
6+
7+
This is a full-stack TypeScript example using:
8+
9+
- Frontend:
10+
- Next.js and [SWR](https://github.com/zeit/swr)
11+
- [react-stripe-js](https://github.com/stripe/react-stripe-js) for [Checkout](https://stripe.com/checkout) and [Elements](https://stripe.com/elements)
12+
- Backend
13+
- Next.js [API routes](https://nextjs.org/docs/api-routes/introduction)
14+
- [stripe-node with TypeScript](https://github.com/stripe/stripe-node#usage-with-typescript)
15+
16+
### Included functionality
17+
18+
- Making `.env` variables available to next: [next.config.js](next.config.js)
19+
- **Note**: When deploying with Now you need to [add your secrets](https://zeit.co/docs/v2/serverless-functions/env-and-secrets) and specify a [now.json](/now.json) file.
20+
- Implementation of a Layout component that loads and sets up Stripe.js and Elements for usage with SSR via `loadStripe` helper: [components/Layout.tsx](components/Layout.tsx).
21+
- Stripe Checkout
22+
- Custom Amount Donation with redirect to Stripe Checkout:
23+
- Frontend: [pages/donate-with-checkout.tsx](pages/donate-with-checkout.tsx)
24+
- Backend: [pages/api/checkout_sessions/](pages/api/checkout_sessions/)
25+
- Checkout payment result page that uses [SWR](https://github.com/zeit/swr) hooks to fetch the CheckoutSession status from the API route: [pages/result.tsx](pages/result.tsx).
26+
- Stripe Elements
27+
- Custom Amount Donation with Stripe Elements & PaymentIntents (no redirect):
28+
- Frontend: [pages/donate-with-elements.tsx](pages/donate-with-checkout.tsx)
29+
- Backend: [pages/api/payment_intents/](pages/api/payment_intents/)
30+
- Webhook handling for [post-payment events](https://stripe.com/docs/payments/accept-a-payment#web-fulfillment)
31+
- By default Next.js API routes are same-origin only. To allow Stripe webhook event requests to reach our API route, we need to add `micro-cors` and [verify the webhook signature](https://stripe.com/docs/webhooks/signatures) of the event. All of this happens in [pages/api/webhooks/index.ts](pages/api/webhooks/index.ts).
32+
- Helpers
33+
- [utils/api-helpers.ts](utils/api-helpers.ts)
34+
- helpers for GET and POST requests.
35+
- [utils/stripe-helpers.ts](utils/stripe-helpers.ts)
36+
- Format amount strings properly using `Intl.NumberFormat`.
37+
- Format amount for usage with Stripe, including zero decimal currency detection.
38+
39+
## How to use
40+
41+
### Using `create-next-app`
42+
43+
Execute [`create-next-app`](https://github.com/zeit/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example:
44+
45+
```bash
46+
npm init next-app --example with-stripe-typescript with-stripe-typescript-app
47+
# or
48+
yarn create next-app --example with-stripe-typescript with-stripe-typescript-app
49+
```
50+
51+
### Download manually
52+
53+
Download the example:
54+
55+
```bash
56+
curl https://codeload.github.com/zeit/next.js/tar.gz/canary | tar -xz --strip=2 next.js-canary/examples/with-stripe-typescript
57+
cd with-stripe-typescript
58+
```
59+
60+
### Required configuration
61+
62+
Copy the `.env.example` file into a file named `.env` in the root directory of this project:
63+
64+
```bash
65+
cp .env.example .env
66+
```
67+
68+
You will need a Stripe account ([register](https://dashboard.stripe.com/register)) to run this sample. Go to the Stripe [developer dashboard](https://stripe.com/docs/development#api-keys) to find your API keys and replace them in the `.env` file.
69+
70+
```bash
71+
STRIPE_PUBLISHABLE_KEY=<replace-with-your-publishable-key>
72+
STRIPE_SECRET_KEY=<replace-with-your-secret-key>
73+
```
74+
75+
Now install the dependencies and start the development server.
76+
77+
```bash
78+
npm install
79+
npm run dev
80+
# or
81+
yarn
82+
yarn dev
83+
```
84+
85+
### Forward webhooks to your local dev server
86+
87+
First [install the CLI](https://stripe.com/docs/stripe-cli) and [link your Stripe account](https://stripe.com/docs/stripe-cli#link-account).
88+
89+
Next, start the webhook forwarding:
90+
91+
```bash
92+
stripe listen --forward-to localhost:3000/api/webhooks
93+
```
94+
95+
The CLI will print a webhook secret key to the console. Set `STRIPE_WEBHOOK_SECRET` to this value in your `.env` file.
96+
97+
### Deploy
98+
99+
Deploy it to the cloud with [ZEIT Now](https://zeit.co/new?filter=next.js&utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
100+
101+
**Note**: You must add your Stripe secrets using the ZEIT Now CLI ([Download here](https://zeit.co/download)):
102+
103+
```bash
104+
now secrets add stripe_publishable_key pk_***
105+
now secrets add stripe_secret_key sk_***
106+
now secrets add stripe_webhook_secret whsec_***
107+
```
108+
109+
After deploying, copy the deployment URL with the webhook path (`https://your-url.now.sh/api/webhooks`) and create a live webhook endpoint [in your Stripe dashboard](https://stripe.com/docs/webhooks/setup#configure-webhook-settings).
110+
111+
**Note**: Your live webhook will have a different secret. To update it in your deployed application you will need to first remove the existing secret and then add the new secret:
112+
113+
```bash
114+
now secrets rm stripe_webhook_secret
115+
now secrets add stripe_webhook_secret whsec_***
116+
```
117+
118+
As the secrets are set as env vars in the project at deploy time, we will need to redeploy our app after we made changes to the secrets.
119+
120+
### Author
121+
122+
- [@thorsten-stripe](https://twitter.com/thorwebdev)

‎components/CheckoutForm.tsx

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React, { useState } from 'react'
2+
3+
import CustomDonationInput from '../components/CustomDonationInput'
4+
5+
import { fetchPostJSON } from '../utils/api-helpers'
6+
import { formatAmountForDisplay } from '../utils/stripe-helpers'
7+
import * as config from '../config'
8+
9+
import { useStripe } from '@stripe/react-stripe-js'
10+
11+
const CheckoutForm: React.FunctionComponent = () => {
12+
const [input, setInput] = useState({
13+
customDonation: Math.round(config.MAX_AMOUNT / config.AMOUNT_STEP),
14+
})
15+
const stripe = useStripe()
16+
17+
const handleInputChange: React.ChangeEventHandler<HTMLInputElement> = e =>
18+
setInput({
19+
...input,
20+
[e.currentTarget.name]: e.currentTarget.value,
21+
})
22+
23+
const handleSubmit: React.FormEventHandler<HTMLFormElement> = async e => {
24+
e.preventDefault()
25+
// Create a Checkout Session.
26+
const response = await fetchPostJSON('/api/checkout_sessions', {
27+
amount: input.customDonation,
28+
})
29+
30+
if (response.statusCode === 500) {
31+
console.error(response.message)
32+
return
33+
}
34+
35+
// Redirect to Checkout.
36+
const { error } = await stripe!.redirectToCheckout({
37+
// Make the id field from the Checkout Session creation API response
38+
// available to this file, so you can provide it as parameter here
39+
// instead of the {{CHECKOUT_SESSION_ID}} placeholder.
40+
sessionId: response.id,
41+
})
42+
// If `redirectToCheckout` fails due to a browser or network
43+
// error, display the localized error message to your customer
44+
// using `error.message`.
45+
console.warn(error.message)
46+
}
47+
48+
return (
49+
<form onSubmit={handleSubmit}>
50+
<CustomDonationInput
51+
className="checkout-style"
52+
name={'customDonation'}
53+
value={input.customDonation}
54+
min={config.MIN_AMOUNT}
55+
max={config.MAX_AMOUNT}
56+
step={config.AMOUNT_STEP}
57+
currency={config.CURRENCY}
58+
onChange={handleInputChange}
59+
/>
60+
<button
61+
className="checkout-style-background"
62+
type="submit"
63+
disabled={!stripe}
64+
>
65+
Donate {formatAmountForDisplay(input.customDonation, config.CURRENCY)}
66+
</button>
67+
</form>
68+
)
69+
}
70+
71+
export default CheckoutForm

‎components/CustomDonationInput.tsx

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react'
2+
import { formatAmountForDisplay } from '../utils/stripe-helpers'
3+
4+
type Props = {
5+
name: string
6+
value: number
7+
min: number
8+
max: number
9+
currency: string
10+
step: number
11+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
12+
className?: string
13+
}
14+
15+
const CustomDonationInput: React.FunctionComponent<Props> = ({
16+
name,
17+
value,
18+
min,
19+
max,
20+
currency,
21+
step,
22+
onChange,
23+
className,
24+
}) => (
25+
<label>
26+
Custom donation amount ({formatAmountForDisplay(min, currency)}-
27+
{formatAmountForDisplay(max, currency)}):
28+
<input
29+
className={className}
30+
type="number"
31+
name={name}
32+
value={value}
33+
min={min}
34+
max={max}
35+
step={step}
36+
onChange={onChange}
37+
></input>
38+
<input
39+
type="range"
40+
name={name}
41+
value={value}
42+
min={min}
43+
max={max}
44+
step={step}
45+
onChange={onChange}
46+
></input>
47+
</label>
48+
)
49+
50+
export default CustomDonationInput

0 commit comments

Comments
 (0)
This repository has been archived.