diff --git a/.all-contributorsrc b/.all-contributorsrc index 3b63a3a1..279193d4 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -26,7 +26,7 @@ }, { "login": "ChrisBrownie55", - "name": "Christopher Brown", + "name": "Chris Brown", "avatar_url": "https://avatars2.githubusercontent.com/u/19195374?v=4", "profile": "https://chrisbrownie.dev/", "contributions": [ diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..b13d6717 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020–2021 Nick DeJesus and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index ca5fe251..daba8551 100644 --- a/README.md +++ b/README.md @@ -1,567 +1,43 @@ -https://useshoppingcart.com # use-shopping-cart - -[![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors-) - - > A React Hook that handles shopping cart state and logic for Stripe. -[![NPM](https://img.shields.io/npm/v/use-shopping-cart.svg)](https://www.npmjs.com/package/use-shopping-cart) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) - - - -[![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors-) - - - -## Installation - -```bash -# With Yarn -yarn add @stripe/stripe-js use-shopping-cart - -# With NPM -npm install --save @stripe/stripe-js use-shopping-cart -``` - -## Usage - -### Initialization - -At the root level of your application (or the highest point you'll be using Stripe from), wrap your components in a ``. - -`` comes with several props that allow you to interact with the Stripe API and customize the Stripe experience. - -When loading up Stripe, don't forget to use your public Stripe API key with it. If you need help setting up your environment variables for this, [view a list of environment variable tutorials.](#Environment-Variable-Tutorials) - -#### [CheckoutSession mode](https://useshoppingcart.com/usage/cartprovider#checkoutsession-mode) - -Creating a [CheckoutSession](https://stripe.com/docs/payments/checkout/one-time#create-checkout-session) server-side allows for a more flexible and powerful integration but requires a server component (e.g. a Netlify Function). - -At the root level of your app, wrap your Root app in the `` - -```jsx -import ReactDOM from 'react-dom' - -import { loadStripe } from '@stripe/stripe-js' -import { CartProvider } from 'use-shopping-cart' - -import App from './App' - -// Remember to add your public Stripe key -const stripePromise = loadStripe(process.env.STRIPE_API_PUBLIC) - -ReactDOM.render( - - - , - document.getElementById('root') -) -``` - -When using CheckoutSessions your product object must adhere to the following shape: - -```js -const products = [ - { - // Line item name to be shown on the Stripe Checkout page - name: 'Bananas', - // Optional description to be shown on the Stripe Checkout page - description: 'Yummy yellow fruit', - // A unique identifier for this item (stock keeping unit) - sku: 'sku_banana001', - // price in smallest currency unit (e.g. cent for USD) - price: 400, - currency: 'USD', - // Optional image to be shown on the Stripe Checkout page - image: 'https://my-image.com/image.jpg' - } - /* ... more products */ -] -``` - -Additionally, you must verify the cartItems on the server-side before creating the CheckoutSession. For this you can use the [validateCartItems() helper](). - -#### [Client-only Checkout mode](https://useshoppingcart.com/usage/cartprovider#client-only-checkout-mode) - -To operate a checkout page without any server component you need to enable client-only checkout mode and insert your - product information in your Stripe Dashboard: - -- [Enable client-only Checkout](https://stripe.com/docs/payments/checkout/client#enable-checkout) -- [Create your products](https://stripe.com/docs/payments/checkout/client#create-products) - -At the root level of your app, wrap your Root app in the `` - -```jsx -import ReactDOM from 'react-dom' - -import { loadStripe } from '@stripe/stripe-js' -import { CartProvider } from 'use-shopping-cart' - -import App from './App' - -// Remember to add your public Stripe key -const stripePromise = loadStripe(process.env.STRIPE_API_PUBLIC) - -ReactDOM.render( - - - , - document.getElementById('root') -) -``` - -When operating in client-only mode you must set the `successUrl` and `cancelUrl` props on the `CartProvider` component, and the product object must adhere to the following shape: - -```js -const products = [ - { - name: 'Bananas', - // sku ID from your Stripe Dashboard - sku: 'sku_GBJ2Ep8246qeeT', - // price in smallest currency unit (e.g. cent for USD) - price: 400, - currency: 'USD', - // Optional image to be shown on the Stripe Checkout page - image: 'https://my-image.com/image.jpg' - } - /* ... more products */ -] -``` - -### Using the hook - -The hook `useShoppingCart()` provides several utilities and pieces of data for you to use in your application. The examples below won't cover every part of the `useShoppingCart()` API but you can [look at the API](#API) below. - -```jsx -import { useShoppingCart } from 'use-shopping-cart' -import { Product } from './Product' -import { CartItems } from './CartItems' - -const productData = [ - { - name: 'Bananas', - sku: 'sku_GBJ2Ep8246qeeT', - price: 400, - image: 'https://www.fillmurray.com/300/300', - currency: 'USD' - }, - { - name: 'Tangerines', - sku: 'sku_GBJ2WWfMaGNC2Z', - price: 100, - image: 'https://www.fillmurray.com/300/300', - currency: 'USD' - } -] - -export function App() { - /* Gets the totalPrice and a method for redirecting to stripe */ - const { totalPrice, redirectToCheckout, cartCount } = useShoppingCart() - - return ( -
- {/* Renders the products */} - {productData.map((product) => ( - - ))} - - {/* This is where we'll render our cart */} -

Number of Items: {cartCount}

-

Total: {totalPrice()}

- - - {/* Redirects the user to Stripe */} - -
- ) -} -``` - -#### How do I add an item to the user's cart? - -To add a product to the cart, use `useShoppingCart()`'s `addItem(product)` method. It takes in your product object, which must have a `sku` and a `price`, and adds it to the cart. - -```jsx -import { useShoppingCart, formatCurrencyString } from 'use-shopping-cart' - -export function Product({ product }) { - const { addItem } = useShoppingCart() - - /* A helper function that turns the price into a readable format */ - const price = formatCurrencyString({ - value: product.price, - currency: product.currency, - language: navigator.language - }) - - return ( -
-
- {`Image -
{product.name}
-
-

{price}

- - {/* Adds the item to the cart */} - -
- ) -} -``` - -#### Now how do I display the cart to the user? - -Once the user has added their items to the cart, you can use the `cartDetails` object to display the different data about each product in their cart. - -Each product in `cartDetails` contains the same data you provided when you called `addItem(product)`. In addition, `cartDetails` also provides the following properties: - - - - - - - - - - - - - - - - - - -
NameValue
quantityNumber of that product added to the cart
valueThe price * quantity
formattedValueA currency formatted version of value
- -```jsx -import { useShoppingCart } from 'use-shopping-cart' - -export function CartItems() { - const { - cartDetails, - decrementItem, - incrementItem, - removeItem - } = useShoppingCart() - - const cart = [] - // Note: Object.keys().map() takes 2x as long as a for-in loop - for (const sku in cartDetails) { - const cartEntry = cartDetails[sku] - - // all of your basic product data still exists (i.e. name, image, price) - cart.push( -
- {/* image here */} - {/* name here */} - {/* formatted total price of that item */} -

Line total: {cartEntry.formattedValue}

- - {/* What if we want to remove one of the item... or add one */} - -

Quantity: {cartEntry.quantity}

- - - {/* What if we don't want this product at all */} - -
- ) - } - - return cart -} -``` - -Note that in the above code, to reduce the quantity of a product in the user's cart, you must pass an SKU to `decrementItem()` like so (note that you can also decrease by more than one): - -```js -decrementItem(cartEntry.sku) - -// or decrease by a count -decrementItem(cartEntry.sku, 3) -``` - -This also works the same when trying to increase the quantity of a product: - -```js -incrementItem(cartEntry) - -// increase by a count -incrementItem(cartEntry.sku, 4) -``` - -Just like you can reduce or increase the quantity of a product you can remove the product entirely with `removeItem()`: - -```js -removeItem(cartEntry.sku) -``` - -#### How could the user set the quantity of an item to a specific number? - -This is achievable with the `setItemQuantity(sku, quantity)` method (introduced in 2.0.0). It takes an SKU of a product in the cart and the quantity of that product you wish to have. Here is a very simple example: - -```jsx -export function GiveMeFiveDonutsPlease() { - const { setItemQuantity } = useShoppingCart() - return ( - - ) -} -``` - -For a real-world robust example visit the [documentation for `setItemQuantity`](). - -## API - -You can [view the full API](https://useshoppingcart.com/) on our documentation page. - -### `` - -Props for this component in Client-only mode: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameType
mode"client-only"
stripeStripe | undefined
successUrlstring
cancelUrlstring
currencystring
languagestring
billingAddressCollectionboolean
allowedCountriesnull | string[]
- -And now, CheckoutSession mode: - - - - - - - - - - - - - - - - - - - - - - -
NameType
mode"checkout-session"
stripeStripe | undefined
currencystring
languagestring
- -### `useShoppingCart()` - -Returns an object with all the following properties and methods: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameType/ArgsReturn Type
addItem()product: ObjectN/A
incrementItem()sku: stringN/A
decrementItem()sku: stringN/A
removeItem()sku: stringN/A
setItemQuantity()sku: string, quantity: numberN/A
totalPriceN/Anumber
formattedTotalPriceN/Astring
cartCountnumberN/A
cartDetailsObject of cart entriesN/A
redirectToCheckout()sessionId?: stringError (if one occurrs)
clearCart()N/AN/A
- -### `formatCurrencyString(options)` - -This function takes one options argument, these are the options for this function: - - - - - - - - - - - - - - - - - - -
NameType
valuenumber
currencystring
languagestring (optional)
- -## Environment Variable Tutorials +https://useshoppingcart.com -The following tutorials teach how to set up your custom environment variables for your project. -- [create-react-app](https://create-react-app.dev/docs/adding-custom-environment-variables/) -- [gatsby.js](https://www.gatsbyjs.org/docs/environment-variables/) -- [next.js](https://nextjs.org/docs/api-reference/next.config.js/environment-variables) -- [react-static](https://github.com/react-static/react-static/blob/master/docs/concepts.md#environment-variables) +[![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors-) [![NPM](https://img.shields.io/npm/v/use-shopping-cart.svg)](https://www.npmjs.com/package/use-shopping-cart) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) -## License +[ +![Product JSON example](/assets/products.png) +![Front-End code example](/assets/front-end.png) +![Serverless code example](/assets/serverless.png) +Click to open gist. +](https://gist.github.com/ChrisBrownie55/f4d395b104a06e8df44e009440247856) -MIT © [dayhaysoos](https://github.com/dayhaysoos) ---- +## Documentation -## Working on this project: +[View our comprehensive documentation website.](https://useshoppingcart.com) ✨📚 -If you're working on this project don't forget to check out -[the CONTRIBUTING.md file](https://github.com/dayhaysoos/use-shopping-cart/blob/master/use-shopping-cart/CONTRIBUTING.md). +## Frequently Asked Questions -Before you run any of the examples be sure to set your environment variables at the root of -the project in a `.env.development` file (or `documentation/.env.development` for the documentation workspace). There is a `.env.example` and a `documentation/.env.example` file with the example variables you'll need to run the examples and documentation workspaces in this project. You'll need to fill them in with your own API keys from Stripe. +This is a list of questions that you might have about use-shopping-cart once you get started. -```dotenv -# .env.example -STRIPE_API_PUBLIC= -STRIPE_API_SECRET= +### Why am I getting an error about `formatToParts` not being a `function` on older browsers? -# documentation/.env.example -GATSBY_STRIPE_PUBLISHABLE_KEY= -``` +You need to polyfill `formatToParts` if you want to support older browsers. You can find more [info on manually polyfilling `formatToParts` in issue #158](https://github.com/dayhaysoos/use-shopping-cart/issues/158). -Here are a couple commands to get you started in development: +### Why am I getting an SSR error about text content not matching? -```bash -# Run the development environment which builds use-shopping-cart in watch-mode -# and starts the CRA example with Netlify functions -yarn dev +It is likely that you are using a value like `cartCount` that is loaded from LocalStorage which doesn't exist on the server. More info in [issue #122](https://github.com/dayhaysoos/use-shopping-cart/issues/122) -# Runs tests in watch-mode -yarn test -# Runs the documentation page locally -yarn dev:docs -``` +## Contributing to use-shopping-cart -### Warning -Please make all README edits to `/use-shopping-cart/README.md`. All edits to `/README.md` will be overwritten on - commit by `/use-shopping-cart/README.md`. Consider `/README.md` read-only. +If you're working on this project **please check out +[the CONTRIBUTING.md file](https://github.com/dayhaysoos/use-shopping-cart/blob/master/use-shopping-cart/CONTRIBUTING.md)**. -We created this hook with [create-react-hook](https://github.com/hermanya/create-react-hook). ## Contributors ✨ @@ -574,7 +50,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Kevin Cunningham

⚠ī¸ đŸ’ģ
Ian Jones

⚠ī¸ -
Christopher Brown

⚠ī¸ đŸ’ģ 📖 +
Chris Brown

⚠ī¸ đŸ’ģ 📖
Nick DeJesus

đŸ’ģ ⚠ī¸
Shodipo Ayomide

📖
Anders Bech Mellson

đŸ’ģ @@ -592,7 +68,11 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d - This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! + + +## License + +MIT Š [dayhaysoos](https://github.com/dayhaysoos) \ No newline at end of file diff --git a/assets/front-end.png b/assets/front-end.png new file mode 100644 index 00000000..cd59bd30 Binary files /dev/null and b/assets/front-end.png differ diff --git a/assets/products.png b/assets/products.png new file mode 100644 index 00000000..6bff2cf3 Binary files /dev/null and b/assets/products.png differ diff --git a/assets/serverless.png b/assets/serverless.png new file mode 100644 index 00000000..4b40fe67 Binary files /dev/null and b/assets/serverless.png differ diff --git a/use-shopping-cart/CONTRIBUTING-SERVICES.md b/use-shopping-cart/CONTRIBUTING-SERVICES.md new file mode 100644 index 00000000..0a3c88a9 --- /dev/null +++ b/use-shopping-cart/CONTRIBUTING-SERVICES.md @@ -0,0 +1,59 @@ +# Adding a service other than Stripe to use-shopping-cart + +In an effort to make this project ready for contributions that allow other payment services to be utilized with it, we created the `getCheckoutData` methods and the `checkoutHandler` function factory. + +This is a guide on how to add another payment service to use-shopping-cart. + +## Updating `getCheckoutData` to get your options for your service's API + +https://github.com/dayhaysoos/use-shopping-cart/blob/master/use-shopping-cart/src/util.js#L46 + +The idea behind this abstraction is that you can make a function in `util.js`'s `getCheckoutData` object that will take in the `cart` and produce most if not all of the options that the service, such as Stripe, will need to go to or do a checkout. This function you create will need to match the name of your service as it is on the `cart` object, for `stripe` it'll be `stripe(cart) {/* your code */}` + +## Updating `checkoutHandler` to recognize your service's API + +Then, inside of `checkoutHandler` you'll need to modify it to recognize your provider. Right now we only have one, Stripe, so it looks like this: + +https://github.com/dayhaysoos/use-shopping-cart/blob/master/use-shopping-cart/src/util.js#L75 + +When you write yours it might look like this: +```js +if (cart.stripe) serviceProperty = 'stripe' +else if (cart.paypal) serviceProperty = 'paypal' +``` + +## My oops, necessary fix before it'll work: + +Then, as I forgot to do this in development myself, you'll need to change this line: +https://github.com/dayhaysoos/use-shopping-cart/blob/master/use-shopping-cart/src/util.js#L97 + +To say: +```js +if (needsCheckoutData) options = getCheckoutData[serviceProperty](cart) +``` + +## Updating a "checkout handler" with your service's API + +By this, I mean updating or creating a call to `checkoutHandler()` with the appropriate options and function for your service. In this instance, you'll likely just be updating `redirectToCheckout()` and `checkoutSingleItem()`. The format is as follows: + +https://github.com/dayhaysoos/use-shopping-cart/blob/master/use-shopping-cart/src/index.js#L136-L141 + +It needs the `cart` as the first as the first argument, then we can get to the options. + +In the options, the `mode` property determines what modes should be allowed for use with this function, therefore, if a mode not in this list is used, an error will be thrown. Note: this might be better refactored to be specific to each service but we'll cross that bridge if we get there. + +Then below that, you can define a method/function that matches the `serviceProperty` name for your service, _`paypal` most likely._ The parameters for this function are `serviceObject, options, parameters`: + +- `serviceObject` - your service itself that you would use to do the checkout (`stripe.redirectToCheckout()`) +- `options` - you created this via `getCheckoutData` and if you're in `checkout-session` mode you'll have a `sessionId` on it as well. +- `parameters` - this is any extra options that you might need from the developer before you can go to the checkout, example below: + +https://github.com/dayhaysoos/use-shopping-cart/blob/master/use-shopping-cart/src/index.js#L143-L149 + +### Other things to do: + +- Allow the developer to pass in the service object. It can be either the object itself or a promise resolving to that object. +- Update TypeScript type definitions in the `.d.ts` file. +- Update the documentation to explain how to use the added service with this library. + +And don't forget to make a Draft PR while you're working on all this so it's easier to track the progress 👍 \ No newline at end of file diff --git a/use-shopping-cart/CONTRIBUTING.md b/use-shopping-cart/CONTRIBUTING.md index 56a31870..e9ac380c 100644 --- a/use-shopping-cart/CONTRIBUTING.md +++ b/use-shopping-cart/CONTRIBUTING.md @@ -1,21 +1,64 @@ # Contribution Guidelines -## Ensure your pull request adheres to the following guidelines: +**Please ensure your pull request adheres to the following guidelines:** - Search previous suggestions before making a new one, as yours may be a duplicate. - Make an individual pull request for each suggestion. - Create a branch with yourname/feature. For example: -> `git checkout -b dayhaysoos/testing-cartItems` - -- Keep descriptions short and simple, but descriptive. + - `git checkout -b dayhaysoos/testing-cartItems` +- Keep descriptions short and simple, but descriptive of your changes. - Start the description with a capital and end with a full stop/period. - ℹī¸ You can use an emoji, only before the Title-Cased Description. - Check your spelling and grammar. + - [Grammarly](https://www.grammarly.com/) is a nice free tool for this and has browser extensions. - New categories or improvements to the existing categorization are welcome. - Pull requests should have a useful title. -- The body of your commit message should contain a link to the repository. -## Updating your PR +### Updating your PR A lot of times, making a PR adhere to the standards above can be difficult. If the maintainers notice anything that we'd like changed, we'll ask you to edit your PR before we merge it. There's no need to open a new PR, just edit the existing one. If you're not sure how to do that. Submit a PR and wait for approval/comments from the maintainers + +## Getting your environment set up + +Before you run any of the examples be sure to set your environment variables at the root of +the project in a `.env.development` file (or `documentation/.env.development` for the documentation workspace). There is a `.env.example` and a `documentation/.env.example` file with the example variables you'll need to run the examples and documentation workspaces in this project. You'll need to fill them in with your own API keys from Stripe. + +```dotenv +# .env.example +STRIPE_API_PUBLIC= +STRIPE_API_SECRET= + +# documentation/.env.example +GATSBY_STRIPE_PUBLISHABLE_KEY= +``` + +The following tutorials teach how to set up your custom environment variables for your project for their respective frameworks. + +- [create-react-app](https://create-react-app.dev/docs/adding-custom-environment-variables/) +- [gatsby.js](https://www.gatsbyjs.org/docs/environment-variables/) +- [next.js](https://nextjs.org/docs/api-reference/next.config.js/environment-variables) +- [react-static](https://github.com/react-static/react-static/blob/master/docs/concepts.md#environment-variables) + + +Here are a couple commands to get you started in development: + +```bash +# Run the development environment which builds use-shopping-cart in watch-mode +# and starts the CRA example with Netlify functions +yarn dev + +# Runs tests in watch-mode +yarn test + +# Runs the documentation page locally +yarn dev:docs +``` + +### Warning about editing the README + +Please make all README edits to `/README.md` and when you're done copy them to `/use-shopping-cart/README.md`. This is done so that once the README updates are merged and published to NPM, you can see the README appear on both GitHub and NPM. + +### Looking to add a payment service to use-shopping-cart + +If you're looking to add a new payment service like Square, Amazon Pay, PayPal, or even Shopify's Billing API, please [view this guide](https://github.com/dayhaysoos/use-shopping-cart/blob/master/use-shopping-cart/CONTRIBUTING-SERVICES.md) on how to do so. \ No newline at end of file diff --git a/use-shopping-cart/README.md b/use-shopping-cart/README.md index 5ef5d502..90abc4ad 100644 --- a/use-shopping-cart/README.md +++ b/use-shopping-cart/README.md @@ -1,569 +1,43 @@ -https://useshoppingcart.com # use-shopping-cart - - -[![All Contributors](https://img.shields.io/badge/all_contributors-8-orange.svg?style=flat-square)](#contributors-) - - - > A React Hook that handles shopping cart state and logic for Stripe. -[![NPM](https://img.shields.io/npm/v/use-shopping-cart.svg)](https://www.npmjs.com/package/use-shopping-cart) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) - - - -[![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors-) - - - -## Installation - -```bash -# With Yarn -yarn add @stripe/stripe-js use-shopping-cart - -# With NPM -npm install --save @stripe/stripe-js use-shopping-cart -``` - -## Usage - -### Initialization - -At the root level of your application (or the highest point you'll be using Stripe from), wrap your components in a ``. - -`` comes with several props that allow you to interact with the Stripe API and customize the Stripe experience. - -When loading up Stripe, don't forget to use your public Stripe API key with it. If you need help setting up your environment variables for this, [view a list of environment variable tutorials.](#Environment-Variable-Tutorials) - -#### [CheckoutSession mode](https://use-shopping-cart.netlify.app/usage/cartprovider#checkoutsession-mode) - -Creating a [CheckoutSession](https://stripe.com/docs/payments/checkout/one-time#create-checkout-session) server-side allows for a more flexible and powerful integration but requires a server component (e.g. a Netlify Function). - -At the root level of your app, wrap your Root app in the `` - -```jsx -import ReactDOM from 'react-dom' - -import { loadStripe } from '@stripe/stripe-js' -import { CartProvider } from 'use-shopping-cart' - -import App from './App' - -// Remember to add your public Stripe key -const stripePromise = loadStripe(process.env.STRIPE_API_PUBLIC) - -ReactDOM.render( - - - , - document.getElementById('root') -) -``` - -When using CheckoutSessions your product object must adhere to the following shape: - -```js -const products = [ - { - // Line item name to be shown on the Stripe Checkout page - name: 'Bananas', - // Optional description to be shown on the Stripe Checkout page - description: 'Yummy yellow fruit', - // A unique identifier for this item (stock keeping unit) - sku: 'sku_banana001', - // price in smallest currency unit (e.g. cent for USD) - price: 400, - currency: 'USD', - // Optional image to be shown on the Stripe Checkout page - image: 'https://my-image.com/image.jpg' - } - /* ... more products */ -] -``` - -Additionally, you must verify the cartItems on the server-side before creating the CheckoutSession. For this you can use the [validateCartItems() helper](). - -#### [Client-only Checkout mode](https://use-shopping-cart.netlify.app/usage/cartprovider#client-only-checkout-mode) - -To operate a checkout page without any server component you need to enable client-only checkout mode and insert your - product information in your Stripe Dashboard: - -- [Enable client-only Checkout](https://stripe.com/docs/payments/checkout/client#enable-checkout) -- [Create your products](https://stripe.com/docs/payments/checkout/client#create-products) - -At the root level of your app, wrap your Root app in the `` - -```jsx -import ReactDOM from 'react-dom' - -import { loadStripe } from '@stripe/stripe-js' -import { CartProvider } from 'use-shopping-cart' - -import App from './App' - -// Remember to add your public Stripe key -const stripePromise = loadStripe(process.env.STRIPE_API_PUBLIC) - -ReactDOM.render( - - - , - document.getElementById('root') -) -``` - -When operating in client-only mode you must set the `successUrl` and `cancelUrl` props on the `CartProvider` component, and the product object must adhere to the following shape: - -```js -const products = [ - { - name: 'Bananas', - // sku ID from your Stripe Dashboard - sku: 'sku_GBJ2Ep8246qeeT', - // price in smallest currency unit (e.g. cent for USD) - price: 400, - currency: 'USD', - // Optional image to be shown on the Stripe Checkout page - image: 'https://my-image.com/image.jpg' - } - /* ... more products */ -] -``` - -### Using the hook - -The hook `useShoppingCart()` provides several utilities and pieces of data for you to use in your application. The examples below won't cover every part of the `useShoppingCart()` API but you can [look at the API](#API) below. - -```jsx -import { useShoppingCart } from 'use-shopping-cart' -import { Product } from './Product' -import { CartItems } from './CartItems' - -const productData = [ - { - name: 'Bananas', - sku: 'sku_GBJ2Ep8246qeeT', - price: 400, - image: 'https://www.fillmurray.com/300/300', - currency: 'USD' - }, - { - name: 'Tangerines', - sku: 'sku_GBJ2WWfMaGNC2Z', - price: 100, - image: 'https://www.fillmurray.com/300/300', - currency: 'USD' - } -] - -export function App() { - /* Gets the totalPrice and a method for redirecting to stripe */ - const { totalPrice, redirectToCheckout, cartCount } = useShoppingCart() - - return ( -
- {/* Renders the products */} - {productData.map((product) => ( - - ))} - - {/* This is where we'll render our cart */} -

Number of Items: {cartCount}

-

Total: {totalPrice()}

- - - {/* Redirects the user to Stripe */} - -
- ) -} -``` - -#### How do I add an item to the user's cart? - -To add a product to the cart, use `useShoppingCart()`'s `addItem(product)` method. It takes in your product object, which must have a `sku` and a `price`, and adds it to the cart. - -```jsx -import { useShoppingCart, formatCurrencyString } from 'use-shopping-cart' - -export function Product({ product }) { - const { addItem } = useShoppingCart() - - /* A helper function that turns the price into a readable format */ - const price = formatCurrencyString({ - value: product.price, - currency: product.currency, - language: navigator.language - }) - - return ( -
-
- {`Image -
{product.name}
-
-

{price}

- - {/* Adds the item to the cart */} - -
- ) -} -``` - -#### Now how do I display the cart to the user? - -Once the user has added their items to the cart, you can use the `cartDetails` object to display the different data about each product in their cart. - -Each product in `cartDetails` contains the same data you provided when you called `addItem(product)`. In addition, `cartDetails` also provides the following properties: - - - - - - - - - - - - - - - - - - -
NameValue
quantityNumber of that product added to the cart
valueThe price * quantity
formattedValueA currency formatted version of value
- -```jsx -import { useShoppingCart } from 'use-shopping-cart' - -export function CartItems() { - const { - cartDetails, - decrementItem, - incrementItem, - removeItem - } = useShoppingCart() - - const cart = [] - // Note: Object.keys().map() takes 2x as long as a for-in loop - for (const sku in cartDetails) { - const cartEntry = cartDetails[sku] - - // all of your basic product data still exists (i.e. name, image, price) - cart.push( -
- {/* image here */} - {/* name here */} - {/* formatted total price of that item */} -

Line total: {cartEntry.formattedValue}

- - {/* What if we want to remove one of the item... or add one */} - -

Quantity: {cartEntry.quantity}

- - - {/* What if we don't want this product at all */} - -
- ) - } - - return cart -} -``` - -Note that in the above code, to reduce the quantity of a product in the user's cart, you must pass an SKU to `decrementItem()` like so (note that you can also decrease by more than one): - -```js -decrementItem(cartEntry.sku) - -// or decrease by a count -decrementItem(cartEntry.sku, 3) -``` - -This also works the same when trying to increase the quantity of a product: - -```js -incrementItem(cartEntry) - -// increase by a count -incrementItem(cartEntry.sku, 4) -``` - -Just like you can reduce or increase the quantity of a product you can remove the product entirely with `removeItem()`: - -```js -removeItem(cartEntry.sku) -``` - -#### How could the user set the quantity of an item to a specific number? - -This is achievable with the `setItemQuantity(sku, quantity)` method (introduced in 2.0.0). It takes an SKU of a product in the cart and the quantity of that product you wish to have. Here is a very simple example: - -```jsx -export function GiveMeFiveDonutsPlease() { - const { setItemQuantity } = useShoppingCart() - return ( - - ) -} -``` - -For a real-world robust example visit the [documentation for `setItemQuantity`](). - -## API - -You can [view the full API](https://use-shopping-cart.netlify.app/) on our documentation page. - -### `` - -Props for this component in Client-only mode: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameType
mode"client-only"
stripeStripe | undefined
successUrlstring
cancelUrlstring
currencystring
languagestring
billingAddressCollectionboolean
allowedCountriesnull | string[]
- -And now, CheckoutSession mode: - - - - - - - - - - - - - - - - - - - - - - -
NameType
mode"checkout-session"
stripeStripe | undefined
currencystring
languagestring
- -### `useShoppingCart()` - -Returns an object with all the following properties and methods: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameType/ArgsReturn Type
addItem()product: ObjectN/A
incrementItem()sku: stringN/A
decrementItem()sku: stringN/A
removeItem()sku: stringN/A
setItemQuantity()sku: string, quantity: numberN/A
totalPriceN/Anumber
formattedTotalPriceN/Astring
cartCountnumberN/A
cartDetailsObject of cart entriesN/A
redirectToCheckout()sessionId?: stringError (if one occurrs)
clearCart()N/AN/A
- -### `formatCurrencyString(options)` - -This function takes one options argument, these are the options for this function: - - - - - - - - - - - - - - - - - - -
NameType
valuenumber
currencystring
languagestring (optional)
- -## Environment Variable Tutorials +https://useshoppingcart.com -The following tutorials teach how to set up your custom environment variables for your project. -- [create-react-app](https://create-react-app.dev/docs/adding-custom-environment-variables/) -- [gatsby.js](https://www.gatsbyjs.org/docs/environment-variables/) -- [next.js](https://nextjs.org/docs/api-reference/next.config.js/environment-variables) -- [react-static](https://github.com/react-static/react-static/blob/master/docs/concepts.md#environment-variables) +[![All Contributors](https://img.shields.io/badge/all_contributors-12-orange.svg?style=flat-square)](#contributors-) [![NPM](https://img.shields.io/npm/v/use-shopping-cart.svg)](https://www.npmjs.com/package/use-shopping-cart) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com) -## License +[ +![Product JSON example](/assets/products.png) +![Front-End code example](/assets/front-end.png) +![Serverless code example](/assets/serverless.png) +Click to open gist. +](https://gist.github.com/ChrisBrownie55/f4d395b104a06e8df44e009440247856) -MIT © [dayhaysoos](https://github.com/dayhaysoos) ---- +## Documentation -## Working on this project: +[View our comprehensive documentation website.](https://useshoppingcart.com) ✨📚 -If you're working on this project don't forget to check out -[the CONTRIBUTING.md file](https://github.com/dayhaysoos/use-shopping-cart/blob/master/use-shopping-cart/CONTRIBUTING.md). +## Frequently Asked Questions -Before you run any of the examples be sure to set your environment variables at the root of -the project in a `.env` file (or `documentation/.env` for the documentation workspace). There is a `.env.example` and a `documentation/.env.example` file with the example variables you'll need to run the examples and documentation workspaces in this project. You'll need to fill them in with your own API keys from Stripe. +This is a list of questions that you might have about use-shopping-cart once you get started. -```dotenv -# .env.example -STRIPE_API_PUBLIC= -STRIPE_API_SECRET= +### Why am I getting an error about `formatToParts` not being a `function` on older browsers? -# documentation/.env.example -GATSBY_STRIPE_PUBLISHABLE_KEY= -``` +You need to polyfill `formatToParts` if you want to support older browsers. You can find more [info on manually polyfilling `formatToParts` in issue #158](https://github.com/dayhaysoos/use-shopping-cart/issues/158). -Here are a couple commands to get you started in development: +### Why am I getting an SSR error about text content not matching? -```bash -# Run the development environment which builds use-shopping-cart in watch-mode -# and starts the CRA example with Netlify functions -yarn dev +It is likely that you are using a value like `cartCount` that is loaded from LocalStorage which doesn't exist on the server. More info in [issue #122](https://github.com/dayhaysoos/use-shopping-cart/issues/122) -# Runs tests in watch-mode -yarn test -# Runs the documentation page locally -yarn dev:docs -``` +## Contributing to use-shopping-cart -### Warning -Please make all README edits to `/use-shopping-cart/README.md`. All edits to `/README.md` will be overwritten on - commit by `/use-shopping-cart/README.md`. Consider `/README.md` read-only. +If you're working on this project **please check out +[the CONTRIBUTING.md file](https://github.com/dayhaysoos/use-shopping-cart/blob/master/use-shopping-cart/CONTRIBUTING.md)**. -We created this hook with [create-react-hook](https://github.com/hermanya/create-react-hook). ## Contributors ✨ @@ -576,7 +50,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Kevin Cunningham

⚠ī¸ đŸ’ģ
Ian Jones

⚠ī¸ -
Christopher Brown

⚠ī¸ đŸ’ģ 📖 +
Chris Brown

⚠ī¸ đŸ’ģ 📖
Nick DeJesus

đŸ’ģ ⚠ī¸
Shodipo Ayomide

📖
Anders Bech Mellson

đŸ’ģ @@ -584,12 +58,20 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Ryan Warner

📖 +
Horacio Herrera

📖 +
Brian Douglas

📖 +
Brittney Postma

📖 +
Prince Wilson

📖 - This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! + + +## License + +MIT © [dayhaysoos](https://github.com/dayhaysoos) \ No newline at end of file