skip to Main Content

Ok. So. I’m building a simple e-commerce site using Next.js (version 13.4) and React; I am using the app router and server actions.

I’ve successfully integrated the PayPal SDK for payments. Right now, I’m using react-paypal-js to render the buttons on the frontend, and I define the createOrder and onApprove functions in my actions folder (so they are server actions, not routes). My thinking was that, as per Next.js., do the data fetching/posting on the server or in server components, that way it can keep sensitive data (like API keys) away from the browser.

However, the PayPalScriptProvider must be used in a client component, plus I have to pass initial options; the clientId is included in these options. This essentially has exposed my Paypal_Client_Id environment variable

Ideally, I would like to keep the Paypal_Client_Id secret from the browser, but I’m not sure this is possible. I tried defining my initialOptions in a server component and passing it as a prop to the client component, but the console log in the client still shows the Paypal_Client_Id. So, I’d appreciate thoughts on either one of these two things:

  1. How significant is it to have the PayPal Client Id exposed? Like, someone has the Id and now my life is going to be completely ruined, or is it more like someone just trying to use my id to get into a bar?

  2. Any thoughts on how to keep my Client Id from being exposed on the frontend? Perhaps a route.ts file, instead? But even in that case, the PayPalScriptProvider has to be used in a client component, and it needs those initial options.

Here are my files, for reference, though I’m not sure if you need to see them to address the issue. It’s just the bare bones so far, lots of console logging errors at the moment:

app>cart>page.tsx: …like I said, I tried passing the environment variable from a SC into a Client Component, so this is the Server Component

import CartPay from "./CartPay";

export default function Cart() {
    
  const client = process.env.PAYPAL_CLIENT_ID || "";
  console.log(client) //logs on the server, not on the client

  const initialOptions = {
    clientId: client,
    currency: "USD",
    intent: "capture",
  };



  return (
    <CartPay initialOptions={initialOptions}/>

  );
}

this is the CartPay.tsx:

'use client'
import React from 'react'
import { useCart } from "../components/CartContext";
import Product from "../products/[id]/Product";
import { PayPalButtons, PayPalScriptProvider } from '@paypal/react-paypal-js';
import { createOrder, payOrder } from '../actions';
interface CartPayProps {
    initialOptions: {
      clientId: string,
      currency: string,
      intent: string
    }
}


const CartPay = ({initialOptions} : CartPayProps) => {
  console.log(initialOptions) //logs the client id and other information in browser

    const {state} = useCart();
    const {cart} = state
  return  (
    <PayPalScriptProvider options={initialOptions}>
    <div>
      {cart.map((item) => (
        <Product key={item.id} product={item} />
      ))}
    </div>
    <div>
      <PayPalButtons
        createOrder={async (data, actions) => {
          let response = await createOrder();
          if (response.success === "true") {
            return response.orderId; //success
          } else {
            //  error from createOrder function
            console.error("Error creating order:", response.error);
            return undefined; // undefined if createOrder failed
          }
        }}
        onApprove={async (data, actions) => {
          let response = await payOrder(data.orderID);
          if (response && response.success === "true") {
            console.log("Payment successful");
          } else {
            console.error("Payment failed:", response.error);
          }
        }}
      />
    </div>
  </PayPalScriptProvider>
  );
}

export default CartPay

2

Answers


  1. All PayPal JS SDK integrations use the client-id as a required config or query string parameter. There is no way to integrate them without a client-id.

    The value that needs to be kept out of the browser isn’t the client-id; rather, it is the REST app’s secret.

    Login or Signup to reply.
  2. I’m facing a similar issue trying to use serverActions with Paypal. In my case I need to handle subscriptions and one-time payments and conditionally render the button passing different methods and initialOptions.

    It would be very helpful if you could share the GitHub repository for your server actions or perhaps expand on your createOrder and payOrder actions here.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search