diff --git a/examples/with-stripe-typescript/README.md b/examples/with-stripe-typescript/README.md index fd91735e4b0a343..43dab5b83022262 100644 --- a/examples/with-stripe-typescript/README.md +++ b/examples/with-stripe-typescript/README.md @@ -1,7 +1,7 @@ -# Example using Stripe with TypeScript and react-stripe-js 🔒💸 +# Example using Stripe with TypeScript and react-stripe-js - Demo: https://nextjs-typescript-react-stripe-js.now.sh/ -- CodeSandbox: https://codesandbox.io/s/nextjs-typescript-react-stripe-js-rqrss +- CodeSandbox: https://codesandbox.io/s/github/stripe-samples/nextjs-typescript-react-stripe-js - Tutorial: https://dev.to/thorwebdev/type-safe-payments-with-next-js-typescript-and-stripe-4jo7 This is a full-stack TypeScript example using: @@ -13,8 +13,27 @@ This is a full-stack TypeScript example using: - Next.js [API routes](https://nextjs.org/docs/api-routes/introduction) - [stripe-node with TypeScript](https://github.com/stripe/stripe-node#usage-with-typescript) +**Demo** + +See the sample [live](https://nextjs-typescript-react-stripe-js.now.sh/) or [fork](https://codesandbox.io/s/github/stripe-samples/nextjs-typescript-react-stripe-js) on CodeSandbox. + +The demo is running in test mode -- use `4242424242424242` as a test card number with any CVC + future expiration date. + +Use the `4000000000003220` test card number to trigger a 3D Secure challenge flow. + +Read more about testing on Stripe at https://stripe.com/docs/testing. + +
Checkout Demo +A gif of the Checkout payment page. +
+ +
Elements Demo +A gif of the custom Elements checkout page. +
+ ### Included functionality +- [Global CSS styles](https://nextjs.org/blog/next-9-2#built-in-css-support-for-global-stylesheets) - Making `.env` variables available to next: [next.config.js](next.config.js) - **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. - 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). @@ -116,3 +135,8 @@ now secrets add stripe_webhook_secret whsec_*** ``` 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. + +### Authors + +- [@thorsten-stripe](https://twitter.com/thorwebdev) +- [@lfades](https://twitter.com/luis_fades) diff --git a/examples/with-stripe-typescript/components/CheckoutForm.tsx b/examples/with-stripe-typescript/components/CheckoutForm.tsx index 4e7de2c956bb07c..3844523570ec79e 100644 --- a/examples/with-stripe-typescript/components/CheckoutForm.tsx +++ b/examples/with-stripe-typescript/components/CheckoutForm.tsx @@ -9,7 +9,9 @@ import * as config from '../config' import { useStripe } from '@stripe/react-stripe-js' const CheckoutForm: React.FunctionComponent = () => { - const [input, setInput] = useState({ customDonation: config.MIN_AMOUNT }) + const [input, setInput] = useState({ + customDonation: Math.round(config.MAX_AMOUNT / config.AMOUNT_STEP), + }) const stripe = useStripe() const handleInputChange: React.ChangeEventHandler = e => @@ -46,6 +48,7 @@ const CheckoutForm: React.FunctionComponent = () => { return (
{ currency={config.CURRENCY} onChange={handleInputChange} /> - diff --git a/examples/with-stripe-typescript/components/CustomDonationInput.tsx b/examples/with-stripe-typescript/components/CustomDonationInput.tsx index 67e1d7a117530ff..ffc64839807c4c2 100644 --- a/examples/with-stripe-typescript/components/CustomDonationInput.tsx +++ b/examples/with-stripe-typescript/components/CustomDonationInput.tsx @@ -9,6 +9,7 @@ type Props = { currency: string step: number onChange: (e: React.ChangeEvent) => void + className?: string } const CustomDonationInput: React.FunctionComponent = ({ @@ -19,11 +20,13 @@ const CustomDonationInput: React.FunctionComponent = ({ currency, step, onChange, + className, }) => ( ) diff --git a/examples/with-stripe-typescript/components/ElementsForm.tsx b/examples/with-stripe-typescript/components/ElementsForm.tsx index 6686e1fab73fae3..0b02f10ec1f37f3 100644 --- a/examples/with-stripe-typescript/components/ElementsForm.tsx +++ b/examples/with-stripe-typescript/components/ElementsForm.tsx @@ -9,9 +9,33 @@ import * as config from '../config' import { CardElement, useStripe, useElements } from '@stripe/react-stripe-js' +const CARD_OPTIONS = { + iconStyle: 'solid' as const, + style: { + base: { + iconColor: '#6772e5', + color: '#6772e5', + fontWeight: '500', + fontFamily: 'Roboto, Open Sans, Segoe UI, sans-serif', + fontSize: '16px', + fontSmoothing: 'antialiased', + ':-webkit-autofill': { + color: '#fce883', + }, + '::placeholder': { + color: '#6772e5', + }, + }, + invalid: { + iconColor: '#ef2961', + color: '#ef2961', + }, + }, +} + const ElementsForm: React.FunctionComponent = () => { const [input, setInput] = useState({ - customDonation: config.MIN_AMOUNT, + customDonation: Math.round(config.MAX_AMOUNT / config.AMOUNT_STEP), cardholderName: '', }) const [payment, setPayment] = useState({ status: 'initial' }) @@ -36,7 +60,7 @@ const ElementsForm: React.FunctionComponent = () => { return ( <>

Error 😭

-

{errorMessage}

+

{errorMessage}

) @@ -53,6 +77,8 @@ const ElementsForm: React.FunctionComponent = () => { const handleSubmit: React.FormEventHandler = async e => { e.preventDefault() + // Abort if form isn't valid + if (!e.currentTarget.reportValidity()) return setPayment({ status: 'processing' }) // Create a PaymentIntent with the specified amount. @@ -95,6 +121,7 @@ const ElementsForm: React.FunctionComponent = () => { <>
{ currency={config.CURRENCY} onChange={handleInputChange} /> -
+
Your payment details: - - +
+ { + if (e.error) { + setPayment({ status: 'error' }) + setErrorMessage(e.error.message ?? 'An unknown error occured') + } + }} + /> +