Giter Site home page Giter Site logo

use-shopping-cart's Introduction

use-shopping-cart

All Contributors

NPM

A React Hook that handles shopping cart state and logic for Stripe.

https://useshoppingcart.com

Product JSON example Front-End code example Serverless code example Click to open gist.

Documentation

View our comprehensive documentation website. โœจ๐Ÿ“š

Frequently Asked Questions

This is a list of questions that you might have about use-shopping-cart once you get started.

Why am I getting an error about formatToParts not being a function on older browsers?

You need to polyfill formatToParts if you want to support older browsers. You can find more info on manually polyfilling formatToParts in issue #158.

Why am I getting an SSR error about text content not matching?

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

Contributing to use-shopping-cart

If you're working on this project please check out the CONTRIBUTING.md file.

Contributors โœจ

Thanks goes to these wonderful people (emoji key):

Kevin Cunningham
Kevin Cunningham

โš ๏ธ ๐Ÿ’ป
Ian Jones
Ian Jones

โš ๏ธ
Nick DeJesus
Nick DeJesus

โš ๏ธ ๐Ÿ“ ๐Ÿ’ผ ๐Ÿ› ๐Ÿ”ฃ ๐Ÿ“– ๐Ÿ” ๐Ÿš‡ ๐Ÿ“† ๐Ÿ’ฌ ๐Ÿ‘€ ๐Ÿ›ก๏ธ
Shodipo Ayomide
Shodipo Ayomide

๐Ÿ“–
Anders Bech Mellson
Anders Bech Mellson

๐Ÿ’ป
Thor ้›ท็ฅž
Thor ้›ท็ฅž

๐Ÿ“– ๐Ÿ’ป โš ๏ธ
Ryan Warner
Ryan Warner

๐Ÿ“–
Horacio Herrera
Horacio Herrera

๐Ÿ“–
Brian Douglas
Brian Douglas

๐Ÿ“–
Brittney Postma
Brittney Postma

๐Ÿ“–
Prince Wilson
Prince Wilson

๐Ÿ“–
Eric Howey
Eric Howey

๐Ÿ“– ๐Ÿ”Œ
Hidetaka Okamoto
Hidetaka Okamoto

๐Ÿ’ป
Andria Brown
Andria Brown

โš ๏ธ ๐Ÿ’ป ๐Ÿ“– ๐Ÿ› ๐Ÿ’ก ๐Ÿš‡ ๐Ÿšง ๐Ÿค” ๐Ÿ’ฌ ๐Ÿ‘€
Konnor Rogers
Konnor Rogers

๐Ÿ’ป
Larissa Pissurno
Larissa Pissurno

๐Ÿ“–
Lucy Macartney
Lucy Macartney

๐Ÿ“– ๐Ÿ–‹ ๐Ÿ’ก
brendanmorrell
brendanmorrell

๐Ÿ’ป
Tom
Tom

๐Ÿ’ป
Milind Goel
Milind Goel

๐Ÿ“–
Rajat Bhatt
Rajat Bhatt

๐Ÿ’ก

This project follows the all-contributors specification. Contributions of any kind welcome!

License

MIT ยฉ dayhaysoos

use-shopping-cart's People

Contributors

allcontributors[bot] avatar andria-dev avatar bdougie avatar bhattrajat avatar brendanmorrell avatar brittneypostma avatar ciruz avatar dayhaysoos avatar dependabot[bot] avatar developerayo avatar doingandlearning avatar ehowey avatar hideokamoto-stripe avatar jh3y avatar jlengstorf avatar ksloan avatar larissapissurno avatar lesleh avatar levindixon avatar lmac-1 avatar mellson avatar milindgoel15 avatar molebox avatar muescha avatar osnodegeoffrey avatar ryanwarner avatar samejima-san avatar theianjones avatar thorsten-stripe avatar upepo-mwindaji avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

use-shopping-cart's Issues

Deploying the example site

I just spent a LOT of time trying to figure out how to deploy this but also keep the dev experience friendly.

I think the answer will be setting up a Yarn Workspace. If anyone has any ideas on a convenient way to host the example feel free to chime in.

What I plan on doing is making a workspace that will help us to continue using the local package that is use-stripe-cart while we work on it but also allow us to maintain an npm published version.

Right now the issues I'm facing with deployment are keeping this example package nested and being able to get it hosted properly on netlify

Extra option for redirectToCheckout

I've come across someone who doesn't actually need the shopping cart, but needs the checkout from a single click of an item.

I propose that we give an option to pass a sku object to redirect to checkout.

If the sku property is passed, it immediately calls Stripe's redirect to checkout with that one product formatted the way it needs to be in line_items

What do you think @ChrisBrownie55 ?

I actually kinda need this for a site today so I'm going to get started, if you have any objections or a more reasonable approach please let me know

Calls to Intl should use the user's or developer's provided language

In the following code block the call to Intl.NumberFormat is hardcoded to 'en-US' which is inaccessible to those that use other symbols for decimal and thousand separators.

https://github.com/dayhaysoos/use-stripe-cart/blob/5e24747a2837e8d94db68f30ae0bb17c20df9dd2/src/util.js#L2

It's possible to use navigator.language to infer the user's preferred language. One could also do this in combination with allowing the developer to pass in a value for the language to allow the developer to utilize their own internationalization system if they have one.

Session Creation (Serverless)

The Problem

Right now, the library assumes that you have a bunch of Stripe products already created via Stripe's Dashboard. In the example, we use already made sku IDs that are from my test account. We need a way to allow those who don't have products already made to create session IDs for Stripe.

https://stripe.com/docs/api/checkout/sessions/object

The Approach

This needs a server, so we'll need an example to work with Netlify.

We first need to establish a way for developers to choose that they want to pass sessionID to redirectToCheckout or send the items array to it instead.

We'll at least need to send a display_items key with an amount/quantity for each item (which we can get in cartDetails along with the required values for creating a session

We then would need a place to do stripe.session.create() to make the call and create the session just before passing the session id to redirectToCheckout

All of the above will be done within a netlify function

Feature: Debugging method to display cart data quickly

Was thinking we can provide CartDisplay to the library, that way people won't have to build out a whole interface or log everything happening just to see if the lib is working. Similar to how formik has a component just for showing form state

Provide Types Definition

Hey there ! :)

It would be nice to provide types definition, to fix this error :

Could not find a declaration file for module "use-shopping-cart". '/Users/corentin/coding/cityfox/app/node_modules/use-shopping-cart/dist/index.js' implicitly has an 'any' type.
Try 'npm install @types/use-shopping-cart' if it exists or add a new declaration (.d.ts) file containing 'declare module 'use-shopping-cart';

It can easily be bypassed by using const { CartProvider } = require('use-shopping-cart') instead of import { CartProvider } from 'use-shopping-cart' but making the TypeScript definitions are useful, especially when using a Linter.

Shopping Cart Display Panel doesn't restore cart contents.

Screenshot 2020-04-04 at 12 52 38

What I did
Using the example, I added items to the cart, refreshed page and added more items to the cart.

What I expected
For the cart contents to be remembered between refreshes.

What happened?
The quantity was retrieved from localStorage but the contents were not.

Documentation feedback/best practices

I've been straight up pushing directly to the documentation branch instead of submitting pull requests, will be submitting PRs from now on. Just wanted to put stuff on the board. Can we come up with a sort of standard for handling documentation?

Here's what we have on the site so far:

https://use-stripe-cart.netlify.app/usage/addItem()

Can I get some feedback on the example code being shown? And also anything else on this site so far.

@Developerayo @ChrisBrownie55 @doingandlearning

Does this work in Gatsby?

Is this meant to work in Gatsby or only client side React?

I'm asking because I'm setting up my first Stripe integration and I'm trying to use this.
But I'm getting into some "fun" errors with Gatsby because of Async functions, TypeScript and the global, window, Stripe.

So I'm not sure if this is a battle I can win or I should give up ๐Ÿ˜Š

Refactor totalPrice

We are returning the totalPrice from the hook. It is a formatted string that takes an Integer in cents and returns a string dollar amount.

So 100 -> '$1.00'

When I went to test this attribute, I wrongly assumed two things:

  • that it would give me an integer back
  • that it I wouldn't have to call a function to use it

I think we could update the name of this variable name to formattedDisplayPrice (or whatever else makes sense). I think we could also call this method and pass the attribute to the user or prepend get to whatever we name this attribute to signify that its a function.

What do y'all think?

Documentation

The README.md should document the features that come with this library and how to use it and how to set it up in a development environment

Test: Server-side environment

We recently ran into an error that dealt with accessing navigator.language without ensuring we were in a client environment (the browser, or a place with access to a window). In light of this, we should add tests that ensure this doesn't happen again.

What needs to be done

Essentially, we just need to call every method and try to cause every edge case with the Window object disabled.

Sidenote:

This also makes me think that we might need to turn isClient into a function so that it can detect a change between server-side and client-side instead of it being a fixed value for testing purposes.

Create an event listening function any time the cart changes

Every single time the cart updates, we allow the user to pass their own function that would update their db.

To do this, we'll create a prop on CartProvider that would look like:

function sendCartDetailsToServer(cartDetails) {
  // Update DB
}

<CartProvider onUpdateCartDetails={sendCartDetailsToServer} />

In this example, sendCartDetailsToServer will be the method the user passes to reflect the changes to their DB.

There needs to be some logic that checks if a function has been passed in via the onUpdateCartDetails prop.

If it exists, call that function with the user cart data (cartDetails), if it doesn't, just skip it.

reduceItemByOne doesn't seem to cause rerender when the button is clicked

What I expect:

When there are cartItems in the sku, I am expecting that I can click the - button inside the Cart Display are to decrease the amount of items by 1. For some reason, nothing is decreased, however, when I click on the + sign to add, the amount of items that should have decreased seems to rectify itself.

Very strange, would love help with this one @doingandlearning @ChrisBrownie55. I was trying to figure it out and got nowhere

Rebrand of this library

I had a conversation with a Stripe developer advocate. While we don't HAVE to change the name, I was advised that it might be better, in the long run, to change the name to get away from potential branding issues.

instead of use-stripe-cart

Going to name it use-shopping-cart

I secured the name right here:

https://www.npmjs.com/package/use-shopping-cart

Would love your input @ChrisBrownie55 @doingandlearning @Developerayo. I know this is going to be really inconvenient as far as URLs and what not, I'm not actually sure what goes into renaming a repo tbh lol.

If anyone has any questions lemme know!

Development experience

So we finally have an example site going!

https://use-stripe-cart.netlify.com/

There's one issue, if we want to work directly on the library and see the changes on the example site, we'd have to change the path for use-stripe-cart in example/package.json to go back to "link:.."

This isn't really convenient, not sure how to approach this. This is why I wanted to set up yarn work space to manage this sorta thing.

Any thoughts @ChrisBrownie55 @doingandlearning ?

Data generated in useStripeCart() should be memoized

If the data generated inside the function body of useStripeCart() isn't memoized, whenever a developer utilizes one of the generated pieces of data (i.e. cartDetails, totalPrice), any re-render caused in the <CartProvider> causes entirely new data and methods to be generated when nothing has changed.

Refactor cartDetails to sync up with "skus" object

Problem: when addItem/deleteItem is fired, skus updates just fine. cartDetails seems to keep the data that derives from cartItems, though.

Possible solution: refactoring deleteItem to remove from cartItems so that cartDetails properly reflects what it should.

Feature: Event Listeners

Would love to get y'all feedback on what we think should go into phase 2 of this lib. In my opinion, I think the best direction to go would be adding event handling (or life cycle methods? Not sure what to call it)

For example:

onAddToCart

A function that fires when an item is added to the cart. This would allow devs with databases to use this space to store/update data on their users (or anything they want, really).

Functions that should have triggers would be functions that interact with the cart items. We'd have to add more actions to be dispatched.

What do y'all think?

@ChrisBrownie55 @doingandlearning

Are deleteItem and removeCartItem the same thing?

I'm currently working on updating the README with comprehensive explanations on how to use everything and I noticed that deleteItem and removeCartItem both seem to simply remove an item from cartItems.

If they are the same, should we remove deleteItem?

Optional quantity in addItem()

Hello!
It would be useful to add an optional quantity parameter for addItem() :
In my case, I fetch the products from the DB then add them in the cart using addItem().
However, the quantity is saved in my DB, I therefore need to add multiple times the same product in the cart.

Instead of doing a loop to do so, I think that addItem(product, quantity?) is better.
quantity would be an optional parameter, with 1 as default.

I'll open a PR with this :)

Refactor out cartItems (Discussion)

increaseItemByOne for the same reason we have reduceItemByOne

As the code currently stands, we have a reduceItemByOne(sku) that handles removing one item from the cartItems subsequently causing cartDetails to update. This makes decreasing the number of items in a cart very intuitive from a developer standpoint. By contrast, it's not very intuitive that you're supposed to re-use addItem(product) since at this point in the code you don't have access to your original product object and instead you're likely operating on a cartEntry (entry in cartDetails).

This could be easily implemented as it's only to improve DX, however, I also have another proposal:

Replace cartItems with cartDetails entirely?

Currently, cartItems is only used to generate cartDetails, totalPrice, and cartCount. I'm proposing that we do away with cartItems in favor of a more event based approach to creating and modifying the above pieces of data.

For example, instead of adding an item to the cart and generating our data from cartItems with formatDetailedCart, calculateTotalValue, and cartCount we could use reducers that handle the events in different ways.

For the calculation of cartCount it would be something similar to the following:

function cartCountReducer(value, action) {
  switch (action.type) {
    case 'addItemToCart':
    case 'incrementItemByOne':
      return value + 1
    case 'reduceItemByOne':
      return value - 1
    default:
       return value
  }
}

And of course, we need to handle certain things like if reduceItemByOne or `incrementItemByOne are called with an invalid sku. This can be handled inside of the combined dispatch function like so:

function dispatch(action) {
  // increment or reduce item by one only if there's a matching `sku` in `cartDetails`
  if ((action.type === 'incrementItemByOne' || action.type === 'reduceItemByOne') && !(action.sku in cartDetails)) {
    return
  }

  cartCountDispatch(action)
}

Testing

Testing library is already set up, just need to do the things

Things to test:

  • Skus maintains this shape
{
    sku_abc123: 1,
    sku_xyz456: 4,
}

This is the source of truth formatCheckoutData. When addItems is fired, the related sku ID updates the quantity (the number next to it)

  • Checkout data is formatted to maintain this shape:
[
    {
        sku: 'sku_abc123',
        quantity: 1
    }
]

The function formatCheckoutCart is what handles this.This might mean writing tests for formatCheckoutCart as well, which actually relies on the skus object

  • Delete Item deletes specific item from cart.skus

  • Store last clicked Stores the value of the last clicked item (saves skuID)

  • redirectToCheckout gets the required data to go to Stripe's checkout experience. The items array, successUrl after successful purchase and cancelUrl for cancelled payments.

  • Handle quantity change handles updating the skus object after an item is added or removed via updateQuantity function. If the quantity drops to 0 or if quantity doesn't exist, the sku is removed from the skus object

  • toggleRightMenu should be refactored to something like, "shouldDisplayCart" or something. This would be a boolean

  • Total Price totals up all prices of all products properly

Serverless support was never a thing (lol)

redirectToCheckout never took in the sessionID, it only referred to the products from my stripe API keys.

I'm going to add a bool for configuring for sessiondIds to be passed to CartProvider.

maybe uuhh..

withSessionId: true

True will be default since it's the preferred way to work with it. redirectoToCheckout will check for this value, if true, wait for a sessionId to get passed to it.

Handling cart details

Cart Details is supposed to be the reference to the info about the items in the cart. It should be storing data, like quantity, image for said item and the price of that item.

Right now, cart details is an object that works similarly to the skus object, but it has more information per sku (the information stated above)

Trying to figure out the best way to approach this. Should this be an array of items, similar to cartItems, where all of that data is reduced to an element per item (with cartItems, it just adds to the array with no way to organize/group it all together) or should we actually refactor cartItems to handle all that?

Would love your input @theianjones and @doingandlearning. Let me know if I need to make things more clear

LocalStorage integration

There are some objects that should be persisted through local storage. Right now, the only object that gets written to local storage is the "checkoutCart" (but it's key is skus).

First of all, I think there could be a more thorough approach for using local storage, I'm straight up just writing to LS manually, I know there is a hooks library for handling local storage and stuff, maybe that'll help.

After a better approach is used for handling local storage, the ideal values that should be stored would be checkoutData and cartDetails.

New examples welcome here!

I'd like to start off with two examples, one with Create React App (I already did this in the master branch) and one with Gatsby. I think I'm going to bake it into the actual documentation branch.

Symlinks: "File name too long" on MacOS

Hello !
Whenever I try to clone the last version of the repo,
I got an error :

$ git clone https://github.com/dayhaysoos/use-shopping-cart
Cloning into 'use-shopping-cart'...
remote: Enumerating objects: 193, done.
remote: Counting objects: 100% (193/193), done.
remote: Compressing objects: 100% (124/124), done.
remote: Total 2559 (delta 95), reused 116 (delta 55), pack-reused 2366
Receiving objects: 100% (2559/2559), 14.86 MiB | 632.00 KiB/s, done.
Resolving deltas: 100% (1396/1396), done.
error: unable to create symlink use-shopping-cart/README.md: File name too long
fatal: unable to checkout working tree
warning: Clone succeeded, but checkout failed.
You can inspect what was checked out with 'git status'
and retry with 'git restore --source=HEAD :/'

Therefore, when it's cloned :

$ yarn
yarn install v1.22.4
error An unexpected error occurred: "ENOENT: no such file or directory, stat '/Users/me/coding/use-shopping-cart/README.md'".
info If you think this is a bug, please open a bug report with the information provided in "/Users/me/coding/use-shopping-cart/yarn-error.log".
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.

TypeScript example

In need of an example of using this package built with TypeScript to be able to ensure that future versions stay TypeScript compatible.

Preferably to be hosted in this monorepo, however, could also be a link to CodeSandbox or Glitch.

Move important values into reducer

Items such as:

  • checkoutData
  • cartDetails
  • totalPrice
    -cartCount

These things should probably be within the reducer themselves, rather than variables that are sourced by the reducers. I had trouble trying to create these values within the reducer originally which is why I am doing it this way.

Phase 2 - Discussion

So far things have been great! What I believe should be the goal of Phase 2 is to make this library better support backend use cases of shopping cart libraries.

Event trigger functions:

This means stuff like on and off methods for almost every interaction with the cart:

  • onAddItem()
  • onIncrementItem()
  • onDecrementItem()
  • onSetItemQuantity()
  • onRemoveItem()
  • onClearCart()
  • onRedirectToCheckout() (Should we have this??)

This will allow any developer who needs to update a back end to send the appropriate data to the database if the user is logged in.

Is there anything missing from here? Would love input especially from @thorsten-stripe

This issue is to decide on what needs to be done, after we have an agreed-upon list of to-dos, I will create issues for each task and add them to the road map. We should also open up another issue on the implementation details on this after.

Server config support (Session Id)

Feature: Supporting Session Ids with redirectToCheckout

@jlengstorf proposed the idea of having a "serverConfig" option that would handle items like sessionId (optional)

Are there other things in serverConfig besides Stripe Session Id that we should consider?

Make url props optional

image

When we use server-side checkout we don't need to provide the success and cancel urls to the cartprovider.

We also should update the docs to reflect the difference of client-side and server-side checkout.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.