DEV Community

Cover image for How to add PayPal checkout payments to your React app

How to add PayPal checkout payments to your React app

Pato on November 02, 2022

Have you always wondered how to monetize your web applications 🤑? Time to start making those benjamins 💸💸. In this how-to guide, you will learn how...
Collapse
 
esponges profile image
Fernando González Tostado

Shouldn't the client-id be imported from somewhere safe like the .env file with process.env.PAYPAL_CLIENT_ID. Or it's not a sensible id? I know that for the sake of simplicty here is hardcoded in the component, but might be worth pointing this out. Congrats on the useful post btw!

Collapse
 
devpato profile image
Pato • Edited

Client ID is Class 4 data.
Client id is non-sensitive data, but still be prudent about sharing or posting it. And yes I did it for the sake of simplicty! Thank you for your comment

Collapse
 
esponges profile image
Fernando González Tostado • Edited

Yes, I thought that I was not, however I think that any credential —regardless of its sensitiveness— should always be stored in .env. Just as a good practice. Thank you for the clarification.

Thread Thread
 
devpato profile image
Pato

I agree with you! I just didn’t explained that for the simplicity of the tutorial but I will add a note ;) thank you!

Collapse
 
thethirdrace profile image
TheThirdRace • Edited

My experience

As someone who actually updated their PayPal setup not long ago using this exact technique, I would recommend caution.

This article looks good, but the truth is the react-paypal-js package is not very flexible and will bring you many headaches.

Packaging

The package downloads a dynamically generated script at runtime, meaning the "versioning" of your code is not static, it's dynamic with all the problems from such method.

My website uses static site generation (SSG) to pre-compile each page as static HTML. Because of this, I can only use hashes for my Content Security Policy (CSP). Because PayPal change that dynamically generated script on the fly, my PROD environment stopped working on 2 occasions in the last 6 months... I had to adjust the hash to get back the functionality.

To avoid this, my options are very limited.

I could use a nonce, but I can kiss goodbye to my "perfect" reliability from static site generation (SSG) and I'll be forced to use server side generation (SSG).

The only other solution is to lower my Content Security Policy to pretty much accept anything from anyone...

So for security reasons, PayPal is kinda forcing me to go all SSR or remove the Content Security Policy altogether... All because PayPal decided to download at runtime a script instead of simply versioning their API like any normal and reliable service. It's a nightmare to manage, enough to look at other solutions...

Styling

You don't have much control on the styling. Everything is hidden away in an iframe, meaning those border radius ain't gonna fly.

It wouldn't be so bad if the PayPal button was actually styled correctly, but there are many glitches that just make your site look amateurish. You can't control the outline color or the button size much, it doesn't integrate well with your design, you have to design around PayPal instead. It's not a nice experience at all to be honest...

State

The onCreateOrder and onApproveOrder functions you pass are memoized within the PayPal iframe.

This means if you want dynamic pricing, like doing a "give what you want", then you're in for a lot of fun! If you want to select between 3 options and charge the according price, you're in for a lot of fun too! You kinda have to go through hoops and loops like a circus animal to make it work. It does work, but the problem is it doesn't work like anything you've ever seen in React. It's a very awkward developer experience.

Conclusion

I did upgrade using this setup and it works, as long as PayPal don't change anything on their side it's rock solid.

But if I had to redo it again, I would probably not choose this solution because of the heap of headaches it creates.

I would probably go with a payment processor like Stripe because it has both a wonderful developer experience (DX) and an awesome user experience (UX). It also offers a lot more flexibility for payment sources (GPay, Apple Pay, Credit card, Alipay, WeChat, etc.), styling and there's no shenanigan with how stuff is loaded or updated in React. Their doc is also top notch! It ain't PayPal, but then again PayPal is not the defacto payment source anymore either...

Do your research before jumping on the bandwagon

Collapse
 
devpato profile image
Pato • Edited

Hi! Thank you so much for you feedback :) If you have a few minutes of you time in the next few weeks I would love to set a quick call with you to see discuss this valuable feedback :) let me know! Thank you

Collapse
 
thethirdrace profile image
TheThirdRace

Hi,

I can definitely take some time to discuss with you.

Although my weeks are a bit crazy at the moment, so it might take a bit of time before we can set this up.

I'll send you a DM when I can schedule something.

Thread Thread
 
devpato profile image
Pato

Sounds like a plan, thank you!

Collapse
 
guiasdeprepago profile image
GuiasDePrepago

Hi, currently I'm having issues passing a state as the price on create order, do you have any solution for this?

Collapse
 
thethirdrace profile image
TheThirdRace • Edited

You can't pass state as the button is memoized.

What you can do is pass a function that checks a ref. You can adjust the value of the ref however you need.

It's completely crazy to have to use this kind of method, but that's the only work around I've found for dynamic pricing...

Collapse
 
vidipghosh profile image
Vidip Ghosh

Thank you so much for sharing. Very useful.

Collapse
 
chema profile image
José María CL

thank you so much!

Collapse
 
devpato profile image
Pato

Anytime :)))

Collapse
 
crishrpz profile image
crishrpz

Amazing!! Thanks

Collapse
 
colin_walker profile image
Colin Walker

Great post, thanks for the info!

Collapse
 
eunit profile image
Eunit

The initialOptions should be like so, instead of what you suggested @devpato. I think it was a typo from your end. Take note of the clientId

const initialOptions = {
  clientId: "YOUR-CLIENT-ID-HERE",
  currency: "USD",
  intent: "capture",
};
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jvph profile image
JVPH

What if you're fetching the client id from the server? It seems the client id is set as undefined no matter what (useState, useEffect, etc).

Collapse
 
immanuelcherui1 profile image
IMMANUEL CHERUIYOT

is create vite@latest working with this?