Coding the Checkout Process

Learn how to code the application’s checkout process with Stripe.

InitPayment in Stripe

This lesson walks through the application’s checkout process. Take a look at the <ShoppingCart/> component inside imports/ui/Shoppingcart.jsx. On lines 195–203, we should notice that we’re pushing the user to the /init_payment route. This route renders the <InitPayment/> component.

Open the imports/ui/InitPayment.jsx file. This is the component that’s rendered after clicking the “Checkout” button. The InitPayment component is used to make a Meteor call to the server, which, in turn, makes an API request to Stripe to create a paymentIntent.

The component receives the authenticated and user object props. We want to display two input textboxes for users that are authenticated so that we can collect their names and email addresses. This is done in lines 93–121. The total number of books in the cart is calculated by the sumItems function. Remember that the cart is stored in React context, so we have access to it on our component.

On lines 141–149, there’s a “submit” button that submits the form. Take a look at the onSubmit form function called handlePaymentCheckout. We prevent the default action of a form, which is to submit the content of the form, thereby refreshing the page. On line 22, we set the loading state by using the setLoading function. This is done to prevent the user from clicking the “submit” button again. The disabled attribute of the form button is equated to the loading state. So, if loading is true, the form button is disabled and a spinner is shown, preventing double submission.

We have a try catch block in which we build up the books purchased. If the user is authenticated, we retrieve the username and email from the user prop. If they’re not authenticated, we get the value entered by them from the customerEmail and customerName states.

We create a bookObject from the books contained in the cart on lines 46–51 inside the imports/ui/InitPayment.jsx file. The created object is modeled in the same shape as the BookSale collection schema. Open imports/api/bookSales/bookSales.js to view the schema. The bookObjects are added into an array named booksBoughtArray. In line 59, we create a Promise object. We can await the Promise execution because the handlePaymentCheckout is marked as async. When we await a Promise, it makes the execution feel as though it’s synchronous, even though a Promise is asynchronous.

Inside the Promise body, we have a call to a Meteor Method called stripe.initPayment. We pass in the amount and the buyDetails, which contain an object in the shape of what the BooksSaleCollection is expecting. The Meteor Method call returns, and if successful, the Promise is resolved with the result from the call. Remember that the return value of the Meteor Method, stripe.initPayment, is the clientSecret that’s returned from Stripe.

If the call fails, we can call the setErrorMessage function that sets the errorMessage state and reject the Promise. In line 74, we set the loading state to false. In line 75, we use the setClientSecret function, which is destructured from the useStripeContext on line 14. This saves the clientSecret inside the StripeContext. We also save the clientSecret on local storage in line 76. On line 77, we push the user to the component where they’ll make the payment.

Updating the clientSecret

Open the App.jsx file and notice that line 62 is where we extract the clientSecret from the useStripeContext. We check if we have a clientSecret and, if we don’t have one, we retrieve the clientSecret stored in local storage.

const { clientSecret } = useStripeContext();
  let clientSecretFromStorage;
  if (!clientSecret) {
    //get it from the local storage
    clientSecretFromStorage = localStorage.getItem("client_secret");
  }

A ternary operation is performed in line 70. If there’s no clientSecret present, we retrieve the clientSecret stored on the browser’s local storage.

stripe.initPayment Method

The Meteor Method, stripe.initPayment, is defined in the imports/server/startup/stripe.js file. Let’s walk through this file’s functions once more. The function accepts an amount and the saleDetails object that comes from the client. A check is performed on the input variables. The salesDetails.dateOfPurchase value is set to use the server date instead of the date coming from the client.

The salesDetails is saved inside the BookSalesCollection and the returned _id is saved in a variable called orderId. An asynchronous call is made to Stripe API using our secret key. We multiply the amount by a factor of 100 because Stripe saves the collected amount in the smallest denomination of a currency.

We define payment_method_types as a card. Here, we can set other accepted payment methods. View the Stripe documentation for other payment methods allowed. The metadata is an object that we can attach here for Stripe to display on our dashboard. This helps to attach a payment on the dashboard to a document in our BookSaleCollection.

We await the call to Stripe and, if we’re successful, we update the BookSaleCollection with the returned client_secret and return the client_secret to the caller of the Meteor Method. If the call to Stripe fails, we throw an Error in line 26.

We pick up the Method call on the client by saving the returned value in local storage. Open the imports/ui/InitPayment.jsx file. Take a look at line 76. This is where the returned result from the server method is saved to local storage. On line 77, the user is pushed to the /make_payment route to complete the payment.

Task

Run the application, buy some books, and click the “Checkout” button to take you to the initPayment component. Fill in the details on the form, and click the “initiate payment” button.

Get hands-on with 1200+ tech skills courses.