Connect Stripe to Vue.js
Tags: vue, stripe, tutorial, javascript
Published: 2022-07-06 12:06:00

Hi 🖐🏼, in this guide, I will show you how to connect Stripe to Vue. This integration is for shopping carts only, not subscriptions. Our working environment is:

- Stripe latest 2022
- Vue.js options API

Due to the extensiveness of this tutorial, I'm going to link my website blog; It has better visual representations.

{% embed https://codingpr.com/stripe-vue-blog/ %}

-  If you don't have a project, you can copy mine here:
[django_store](https://github.com/Denisse-AB/django_store.git)

## 1. Set Up Stripe.

- To get started with Stripe, you need to open a Stripe account; After opening your account, go to the developer's section and access your API keys.
In the developer's section, you will find two keys: publishable key y secret key; copy them in your .env file.

```bash

      VUE_APP_STRIPE_KEY=<YOUR-PUBLISHABLE-KEY>
      
```
## 2. Integration.

- Once you have opened your account with Stripe, copy the script tag into the head of index.html.

```html

    <head>
      <script src="https://js.stripe.com/v3/"></script>
    </head>
    
```

- Immediately after getting the subtotal from the customer, create the onclick event to call the server and request the payment intent. Use the paymentIntent function in different parts of your app, place it in the login form and the guest form.

```html

    <!-- template -->
    <v-btn
      v-if="isCart >= 1"
      block
      color="#385F73"
      class="mt-3"
      elevation="2"
      :loading=loading
      rounded
      dark
      @click="paymentIntent"
    >
      Subtotal
    </v-btn>
    
```

```javascript

      // place this function in different parts of your app
      // like the login form and the guest form
      // methods
      paymentIntent () {
        if (this.isLoggedIn) {
          this.$store.dispatch('paymentIntent')
            .catch((_err) => {
              this.loading = false
              const show = true
              const color = 'red darken-3'
              const text = 'Server Error, try again later!'
              this.$store.commit('cartSnack', {
                show, color, text
              })
            })
        } else {
          this.$router.push('/pre-checkout')
        }
      }
      
```

- To send the payment intent from Vue to the server, we are going to use Vuex and Axios. Vuex is the state management for Vuejs applications, and Axios is the http requests manager. In the following code, retrieve the user object and the shopping cart. Send the shopping cart to calculate the transaction total based on the product id. After sending these objects, get the response from the server with the client secret and the tax calculation, send them to the mutations and store them in the state.

```javascript

    // vuex actions
    async paymentIntent ({ commit, state }) {
      const res = await axios.post('users/secret/', {
        user: state.user,
        cart: state.cart
      })
      if (res.status === 201) {
        commit('CLIENT_SECRET', {
          client_secret: res.data.client_secret,
          tax: res.data.tax
        })
        router.push('/checkout')
      }
    },
    
```

```javascript

    // vuex mutations
    CLIENT_SECRET (state, payload) {
      const secret = state.secret
      secret.clientSecret = payload.client_secret
      secret.tax = payload.tax
    },
    
```
- Initialize the state with the client secret in null and tax in 0.

```javascript

    // vuex state
    state: {
      secret: {
        clientSecret: null,
        tax: 0
      },
      user: {},
      cart: []
    },
    
```

## 3. Mounting The Card Element.

- Once we have the client secret store in the state, we can use it to mount the Stripe card element. This card element will show to the customer the Stripe form in which the customer will insert his credit card number. Remember Stripe has the transaction total. With this card element, the customer is authorizing and finalizing the transaction.

```html

    <!-- template -->
    <form
      id="payment-form"
      v-if="isCart >= 1"
    >
      <br>
      <div ref="paymentElement" id="payment-element">
        <!-- Elements will create form elements here -->
      </div>
      <br>
      <div id="error-message">
        <!-- Display error message to your customers -->
      </div>
    </form>
    <!-- end -->
    <v-card-actions>
      <v-btn
        v-if="isCart >= 1"
        id="submit"
        :loading="loading"
        block
        color="success"
        class="mt-3"
        elevation="2"
        rounded
        dark
        @click="Submit"
      >
        Pay
      <v-icon class="ml-2">mdi-basket</v-icon>
      </v-btn>
    </v-card-actions>
    
```

- Inside the script tag of Vue, import the store from vuex so we can access the client secret. Use your public secret key and window.Stripe() function to get the form element from Stripe. Your public secret key must be stored in the .env file; never put it directly in the code! Once we get the payment element, we can modify its appearance Stripe appearance-api.

```javascript

      // import mapState and mapGetters to access
      // aditional customer variables
      // script
      import store from '../store'

      const stripe = window.Stripe(
        process.env.VUE_APP_STRIPE_KEY
      )
      const options = {
        clientSecret: store.state.secret.clientSecret,
        // Stripe themes are fully customizable
        appearance: { theme: 'stripe' }
      }

      const elements = stripe.elements(options)

      const paymentElement = elements.create('payment')
      
```

- Inside export default, mount the Stripe Element using Vue's mounted function.

```javascript

      // export default
      mounted () {
        paymentElement.mount(this.$refs.paymentElement)
        paymentElement.on('change', (event) => {
          this.displayError(event)
        })
      },
      
```

- The DisplayError() function will show the customer if his card has errors such as insufficient funds.

```javascript

      // methods
      displayError (event) {
        const displayError = document.getElementById(
          'error-message'
        )
        if (event.error) {
          displayError.textContent = event.error.message
        } else {
          displayError.textContent = ''
        }
      },
      
```
- Finally, send the transaction through the Submit function. It is important to note that /thankyou/ is the URL parameter of our last route. This is located in the directory under Thankyou.vue. this project uses Vue Router for easy moving between pages. Use mapState to extract properties from the state and send them with the address object.

```javascript

      // methods
      async Submit () {
        this.loading = true
        const { error } = await stripe.confirmPayment({
          elements,
          confirmParams: {
            // return_url: project final route thankyou.vue
            return_url: 'http://localhost:8080/#/thankyou/',
            shipping: {
              address: {
                city: this.city,
                line1: this.address,
                postal_code: this.zipcode,
                state: this.state,
                country: 'USA'
              },
              name: `${this.first_name} ${this.last_name}`,
              phone: this.phone
            }
          }
        })
        if (error) {
          this.loading = false
          const messageContainer = document.querySelector(
            '#error-message'
          )
          messageContainer.textContent = error.message
        } else {
          // Customer redirection to return_url.
        }
      }
      
```
## 4. Extracting Properties from Stripe.

- If you need to retrieve customer information from Stripe, you should do so at the bottom of the Thankyou.vue page. Through the javascript function URLSearchParams(), we retrieve the payment_intent_client_secret from the URL, this will be used by the Stripe retrievePaymentIntent() function to access the transaction object and be able to send the properties to our server. Once the values have been sent, retrieve the response, and if everything is ok, use COMMIT_CHECKOUT and CLIENT_SECRET environment variables to clear the state.

```javascript

      // mounted
      async mounted () {
        const stripe = window.Stripe(
          process.env.VUE_APP_STRIPE_KEY
        )
        const clientSecret = new URLSearchParams(
          window.location.search
        ).get(
          'payment_intent_client_secret'
        )
        const {
          paymentIntent, error
        } = await stripe.retrievePaymentIntent(
          clientSecret
        )
        if (error) {
          console.log(error)
          this.message = 'An error has ocurred'
        }
        if (paymentIntent.status === 'succeeded') {
          this.message = paymentIntent.status
          const payment_id = paymentIntent.id
          this.$store.dispatch('checkout', { payment_id })
            .catch((_err) => {
              this.loading = false
              const show = true
              const color = 'red darken-3'
              const text = 'An error has ocurred'
              this.$store.commit('cartSnack',
                {
                  show, color, text
                })
            })
        } else {
          this.message = paymentIntent.status
        }
      }
      
```

```javascript

      // actions
      async checkout ({ commit, state, getters }, payload) {
        const res = await axios.post('orders/order/', {
          order: {
            stripe_id: payload.payment_id,
            subtotal: getters.subTotal,
            total: getters.grandTotal,
            tax: state.secret.tax,
            cart: state.cart,
            rating: state.stars,
            first_name: state.user.first_name,
            last_name: state.user.last_name,
            email: state.user.email,
            phone: state.user.phone,
            address: state.user.address,
            city: state.user.city,
            state: state.user.state,
            zipcode: state.user.zipcode
          }
        })
        if (res.status === 200) {
          // Clear the state except for the user
          commit('CHECKOUT_SUCCESS')
          commit('CLIENT_SECRET', {
            client_secret: null,
            tax: 0
          })
        }
      },
      
```
- Clear the state on susccesful checkout. For the server-side code of this Stripe implementation checkout this blog: [How to Connect Stripe to Django Rest Framework?](https://codingpr.com/stripe-django-blog/)

```javascript

      // mutations
      CHECKOUT_SUCCESS (state) {
        state.stars = []
        state.cart = []
      },
      
```