2

I'm trying to add PayPal buttons to an existing checkout page. However, I've had quite a bit of trouble because our project uses Vue.js class components, while examples that I've come across don't (the official PayPal documentation does not use class components: https://developer.paypal.com/docs/business/checkout/configure-payments/single-page-app/).

I have come across the workaround of using the mounted() hook to inject the PayPal SDK script into the DOM, and I have gotten success in making the buttons appear, but there is the problem where I can't make the payment details dynamic (order info like total amount, item description, etc. are stored in the Vue component's state, and I have not found a way to pass that state to a static JS script in the DOM).

I am currently trying to adapt the official PayPal documentation to work with class components, and my code is as follows (with non-PayPal-related parts cut out):

Index.html:

<!DOCTYPE html>
<html class="no-js" lang="en" dir="ltr">
    <head>
        <script defer src="https://www.paypal.com/sdk/js?client-id=abc123&disable-funding=credit,card"></script>
    </head>
    <body>
        <div id="app">
        <!-- The shop component is put inside here -->
        </div>
    </body>
</html>

shop.vue:

<template>
    <paypal-buttons :on-approve="onApprove" :create-order="createOrder" />
</template>
<script lang="ts" src="./shop.component.ts"></script>

shop.component.ts:

const PayPalButton = paypal.Buttons.driver("vue", window.Vue);

@Component({
  components: {
    PayPalButton,
  },
})
export default class Shop extends Vue {

  public total = '10.0'; // Will be dynamic

  public mounted(): void {
    ...
  }

  public createOrder(data, actions) {
    return actions.order.create({
        purchase_units : [{
            amount: {
              value: total
            }
        }]
    });
  }
  public onApprove(data, actions) {
    return actions.order.capture().then(function (details) {
        console.log(details)
    })
  }
}

This code will build successfully, but I am not able to actually open the page as there is an error. In the browser console, I see the following error:

TypeError: Cannot read property 'component' of undefined

Upon further debugging, I have found that the line paypal.Buttons.driver("vue", window.Vue); causes the error, and this is because paypal is undefined. I am fairly certain that the PayPal script in index.html loaded properly, and I also do not think this is due to a missing npm package or import. One of the few resources I found online was this: Vue PayPal implementation with vue-head, paypal not defined

Unfortunately, this link's solution uses the mounted() hook, which is what I have tried before, and which does not fix the problem of offering the dynamic total that I want.

Does anybody have experience with PayPal SDK & Vue.js class components? Any help would be greatly appreciated!

1 Answer 1

0

After more testing, I have arrived at this conclusion: so apparently it is possible to have a dynamic order sent to PayPal, even when you use the mounted() hook to inject the SDK script into the DOM. It just turns out that when I first tried to do this, I had a flawed understanding of Vue class components and thus referenced the component state incorrectly.

In my final code, I took out the PayPal SDK <script> tag from index.html. My code from the other 2 files are revised as follows:

shop.vue:

<template>
    <div id="paypal-button"></div>
</template>
<script lang="ts" src="./shop.component.ts"></script>

shop.component.ts:

export default class Shop extends Vue {

  public total = '10.0'; // Will be dynamic

  public mounted(): void {
    const script = document.createElement('script');
    const clientId = 'abc123';
    script.src = `https://www.paypal.com/sdk/js?client-id=${clientId}&disable-funding=credit,card`;
    script.addEventListener('load', this.paypalSetLoaded);
    document.body.appendChild(script);
  }

  public paypalSetLoaded() {
    window.paypal
      .Buttons({
        style: {
          color: 'blue',
          shape: 'pill',
        },
        createOrder: this.paypalCreateOrder,
        onApprove: this.paypalOnApprove,
      })
      .render('#paypal-button');
  }

  public paypalCreateOrder(data, actions) {
    return actions.order.create({
      purchase_units: [{
        amount: {
          currency_code: 'USD',
          value: this.total,
        }
      }],
    });
  }

  public paypalOnApprove(data, actions) {
    return actions.order.capture().then(details => {
      console.log(details);
    });
  }
}

Even if the value of total changes during execution of the program (due to user interaction), an order for the correct amount of money will always be sent to PayPal.

I am not sure whether this is best practice, but the code works and is both efficient & scalable based on what I can forsee. Hope this helps anybody else who encountered a similar problem 😎

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.