# PayPal JS PayPal JS is a collection of JavaScript libraries for integrating PayPal payments into web applications. The `@paypal/paypal-js` package provides a loading wrapper and TypeScript types for the PayPal JS SDK, enabling asynchronous script loading with a Promise-based API. The `@paypal/react-paypal-js` package builds on this foundation to provide React components and hooks that abstract the complexities of the PayPal V6 SDK, offering modern hooks-based APIs for managing payment sessions, eligibility checking, and button rendering. The V6 SDK introduces a component-based architecture with explicit component loading, built-in eligibility checking, and support for multiple payment methods including PayPal, Venmo, Pay Later, guest checkout (card payments), subscriptions, and payment vaulting. The React integration enforces best practices by default through a context-based provider pattern, ensuring buyers receive an optimal user experience while developers benefit from TypeScript support, SSR compatibility, and fine-grained control over payment flows. ## PayPalProvider Component The `PayPalProvider` is the entry point for the V6 SDK in React applications. It handles loading the PayPal SDK script, creating an SDK instance with the provided credentials, and running eligibility checks. All child components can access the SDK via context hooks. ```tsx import { PayPalProvider, PayPalOneTimePaymentButton, INSTANCE_LOADING_STATE, usePayPal, } from "@paypal/react-paypal-js/sdk-v6"; // Basic usage with client ID function App() { return ( ); } // With Promise-based client token (memoize to prevent re-fetching) function AppWithToken() { const tokenPromise = useMemo(() => fetchClientToken(), []); return ( ); } // Deferred loading pattern function AppDeferred() { const [clientId, setClientId] = useState(); useEffect(() => { fetchClientId().then(setClientId); }, []); return ( ); } // Show loading state while SDK initializes function CheckoutPage() { const { loadingStatus, error } = usePayPal(); if (loadingStatus === INSTANCE_LOADING_STATE.PENDING) { return
Loading PayPal...
; } if (loadingStatus === INSTANCE_LOADING_STATE.REJECTED) { return
Failed to load PayPal: {error?.message}
; } return ( { const response = await fetch("/api/create-order", { method: "POST" }); const { orderId } = await response.json(); return { orderId }; }} onApprove={async ({ orderId }) => { await fetch(`/api/capture/${orderId}`, { method: "POST" }); console.log("Payment captured!"); }} /> ); } ``` ## PayPalOneTimePaymentButton Component The `PayPalOneTimePaymentButton` renders a PayPal button for standard one-time payments. It internally uses the `usePayPalOneTimePaymentSession` hook to manage the payment session lifecycle. ```tsx import { PayPalProvider, PayPalOneTimePaymentButton, } from "@paypal/react-paypal-js/sdk-v6"; import type { OnApproveDataOneTimePayments, OnCancelDataOneTimePayments, OnErrorData, OnCompleteData } from "@paypal/react-paypal-js/sdk-v6"; function CheckoutForm() { const handleApprove = async ({ orderId }: OnApproveDataOneTimePayments) => { const response = await fetch(`/api/capture/${orderId}`, { method: "POST" }); const result = await response.json(); console.log("Payment captured:", result); }; const handleCancel = (data: OnCancelDataOneTimePayments) => { console.log("Payment cancelled by user"); }; const handleError = (data: OnErrorData) => { console.error("Payment error:", data); }; const handleComplete = (data: OnCompleteData) => { console.log("Payment flow completed"); }; return ( {/* With createOrder callback for deferred order creation */} { const response = await fetch("/api/create-order", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ amount: "99.99", currency: "USD" }), }); const { orderId } = await response.json(); return { orderId }; }} onApprove={handleApprove} onCancel={handleCancel} onError={handleError} onComplete={handleComplete} presentationMode="auto" type="checkout" /> {/* With static orderId when order is already created */} ); } ``` ## VenmoOneTimePaymentButton Component The `VenmoOneTimePaymentButton` renders a Venmo payment button. Requires `"venmo-payments"` in the provider's `components` array. ```tsx import { PayPalProvider, VenmoOneTimePaymentButton, } from "@paypal/react-paypal-js/sdk-v6"; function VenmoCheckout() { return ( { const response = await fetch("/api/create-order", { method: "POST" }); const { orderId } = await response.json(); return { orderId }; }} onApprove={({ orderId }) => { console.log("Venmo payment approved:", orderId); }} onCancel={() => console.log("Venmo payment cancelled")} onError={(data) => console.error("Venmo error:", data)} presentationMode="auto" /> ); } ``` ## PayLaterOneTimePaymentButton Component The `PayLaterOneTimePaymentButton` renders a Pay Later button for financing options. Country code and product code are automatically populated from eligibility data. ```tsx import { PayPalProvider, PayLaterOneTimePaymentButton, } from "@paypal/react-paypal-js/sdk-v6"; function PayLaterCheckout() { return ( { const response = await fetch("/api/create-order", { method: "POST", body: JSON.stringify({ amount: "500.00" }), }); const { orderId } = await response.json(); return { orderId }; }} onApprove={({ orderId }) => { console.log("Pay Later approved:", orderId); }} onCancel={() => console.log("Pay Later cancelled")} onError={(data) => console.error("Pay Later error:", data)} presentationMode="auto" /> ); } ``` ## PayPalGuestPaymentButton Component The `PayPalGuestPaymentButton` renders a guest checkout button for card payments without a PayPal account. Requires `"paypal-guest-payments"` in the provider's `components` array. ```tsx import { PayPalProvider, PayPalGuestPaymentButton, } from "@paypal/react-paypal-js/sdk-v6"; function GuestCheckout() { return ( { const response = await fetch("/api/create-order", { method: "POST" }); const { orderId } = await response.json(); return { orderId }; }} onApprove={({ orderId }) => { console.log("Guest payment approved:", orderId); }} onCancel={() => console.log("Guest payment cancelled")} onError={(data) => console.error("Guest payment error:", data)} /> ); } ``` ## PayPalSubscriptionButton Component The `PayPalSubscriptionButton` renders a button for subscription payments. Requires `"paypal-subscriptions"` in the provider's `components` array. ```tsx import { PayPalProvider, PayPalSubscriptionButton, } from "@paypal/react-paypal-js/sdk-v6"; function SubscriptionCheckout() { return ( { const response = await fetch("/api/create-subscription", { method: "POST", body: JSON.stringify({ planId: "PLAN-123" }), }); const { subscriptionId } = await response.json(); return { subscriptionId }; }} onApprove={(data) => { console.log("Subscription approved:", data); }} onCancel={() => console.log("Subscription cancelled")} onError={(data) => console.error("Subscription error:", data)} presentationMode="auto" type="subscribe" /> ); } ``` ## PayPalSavePaymentButton Component The `PayPalSavePaymentButton` renders a button for vaulting a payment method without making a purchase. ```tsx import { PayPalProvider, PayPalSavePaymentButton, } from "@paypal/react-paypal-js/sdk-v6"; import type { OnApproveDataSavePayments } from "@paypal/react-paypal-js/sdk-v6"; function SavePaymentMethod() { return ( { const response = await fetch("/api/create-vault-token", { method: "POST", }); const { vaultSetupToken } = await response.json(); return { vaultSetupToken }; }} onApprove={({ vaultSetupToken }: OnApproveDataSavePayments) => { console.log("Payment method saved:", vaultSetupToken); }} onCancel={() => console.log("Save payment cancelled")} onError={(data) => console.error("Save payment error:", data)} presentationMode="popup" /> ); } ``` ## usePayPal Hook The `usePayPal` hook returns the PayPal context including the SDK instance, loading status, eligibility data, and any errors. Must be used within a `PayPalProvider`. ```tsx import { usePayPal, INSTANCE_LOADING_STATE, } from "@paypal/react-paypal-js/sdk-v6"; function PaymentStatus() { const { sdkInstance, // The PayPal SDK instance eligiblePaymentMethods, // Eligible payment methods from eligibility check loadingStatus, // PENDING | RESOLVED | REJECTED error, // Any initialization error isHydrated, // SSR hydration status } = usePayPal(); if (loadingStatus === INSTANCE_LOADING_STATE.PENDING) { return
Loading PayPal SDK...
; } if (loadingStatus === INSTANCE_LOADING_STATE.REJECTED) { return
Failed to load: {error?.message}
; } if (!sdkInstance) { return
SDK not available
; } return (

PayPal SDK loaded successfully

Hydrated: {isHydrated ? "Yes" : "No"}

{eligiblePaymentMethods && (

PayPal eligible: {eligiblePaymentMethods.isEligible("paypal") ? "Yes" : "No"}

)}
); } ``` ## useEligibleMethods Hook The `useEligibleMethods` hook checks which payment methods are available for the current buyer. It handles both server-hydrated and client-fetch scenarios. ```tsx import { PayPalProvider, PayPalOneTimePaymentButton, VenmoOneTimePaymentButton, PayLaterOneTimePaymentButton, useEligibleMethods, } from "@paypal/react-paypal-js/sdk-v6"; function PaymentOptions() { const { eligiblePaymentMethods, isLoading, error } = useEligibleMethods({ payload: { purchase_units: [{ amount: { currency_code: "USD", value: "100.00" } }], }, }); if (isLoading) { return
Checking payment eligibility...
; } if (error) { return
Error checking eligibility: {error.message}
; } const isPayPalEligible = eligiblePaymentMethods?.isEligible("paypal"); const isVenmoEligible = eligiblePaymentMethods?.isEligible("venmo"); const isPayLaterEligible = eligiblePaymentMethods?.isEligible("paylater"); const createOrder = async () => { const response = await fetch("/api/create-order", { method: "POST" }); const { orderId } = await response.json(); return { orderId }; }; const onApprove = async ({ orderId }) => { await fetch(`/api/capture/${orderId}`, { method: "POST" }); }; return (
{isPayPalEligible && ( )} {isVenmoEligible && ( )} {isPayLaterEligible && ( )}
); } function App() { return ( ); } ``` ## usePayPalOneTimePaymentSession Hook The `usePayPalOneTimePaymentSession` hook provides fine-grained control over PayPal one-time payment sessions. Use this for advanced integrations with custom button implementations. ```tsx import { usePayPalOneTimePaymentSession, } from "@paypal/react-paypal-js/sdk-v6"; import type { OnApproveDataOneTimePayments } from "@paypal/react-paypal-js/sdk-v6"; function CustomPayPalButton() { const { isPending, error, handleClick, handleCancel, handleDestroy } = usePayPalOneTimePaymentSession({ createOrder: async () => { const response = await fetch("/api/create-order", { method: "POST" }); const { orderId } = await response.json(); return { orderId }; }, presentationMode: "auto", onApprove: async (data: OnApproveDataOneTimePayments) => { await fetch(`/api/capture/${data.orderId}`, { method: "POST" }); console.log("Approved:", data); }, onCancel: () => console.log("Cancelled"), onError: (data) => console.error("Error:", data), onComplete: (data) => console.log("Complete:", data), }); if (isPending) return
Loading...
; if (error) return
Error: {error.message}
; return ( ); } // With static orderId function CustomButtonWithOrderId() { const { handleClick, error } = usePayPalOneTimePaymentSession({ orderId: "EXISTING-ORDER-123", presentationMode: "popup", onApprove: (data) => console.log("Approved:", data), }); return ( ); } ``` ## useVenmoOneTimePaymentSession Hook The `useVenmoOneTimePaymentSession` hook provides control over Venmo payment sessions for custom button implementations. ```tsx import { useVenmoOneTimePaymentSession } from "@paypal/react-paypal-js/sdk-v6"; function CustomVenmoButton() { const { handleClick, isPending, error } = useVenmoOneTimePaymentSession({ createOrder: async () => { const response = await fetch("/api/create-order", { method: "POST" }); const { orderId } = await response.json(); return { orderId }; }, onApprove: (data) => console.log("Venmo approved:", data), onCancel: () => console.log("Venmo cancelled"), onError: (data) => console.error("Venmo error:", data), }); return ( ); } ``` ## PayPalCardFieldsProvider and Card Field Components The `PayPalCardFieldsProvider` wraps card field components and manages Card Fields sessions. Requires `"card-fields"` in the provider's `components` array. ```tsx import { PayPalProvider, PayPalCardFieldsProvider, PayPalCardNumberField, PayPalCardExpiryField, PayPalCardCvvField, usePayPalCardFields, usePayPalCardFieldsOneTimePaymentSession, } from "@paypal/react-paypal-js/sdk-v6"; import { useState, useEffect } from "react"; function CardPaymentForm() { const { error: cardFieldsError } = usePayPalCardFields(); const { submit, submitResponse, error: submitError } = usePayPalCardFieldsOneTimePaymentSession(); const [isProcessing, setIsProcessing] = useState(false); useEffect(() => { if (submitError) { console.error("Submit error:", submitError); setIsProcessing(false); } }, [submitError]); useEffect(() => { if (!submitResponse) return; const { orderId, message } = submitResponse.data; switch (submitResponse.state) { case "succeeded": console.log(`Payment succeeded: ${orderId}`); setIsProcessing(false); break; case "failed": console.error(`Payment failed: ${message}`); setIsProcessing(false); break; } }, [submitResponse]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setIsProcessing(true); const response = await fetch("/api/create-order", { method: "POST" }); const { orderId } = await response.json(); await submit(orderId); }; return (
); } function CardCheckout() { return ( console.log("Validity changed:", event)} cardtypechange={(event) => console.log("Card type:", event)} > ); } ``` ## usePayPalCardFieldsSavePaymentSession Hook The `usePayPalCardFieldsSavePaymentSession` hook manages save payment Card Fields sessions for vaulting cards without a purchase. ```tsx import { PayPalProvider, PayPalCardFieldsProvider, PayPalCardNumberField, PayPalCardExpiryField, PayPalCardCvvField, usePayPalCardFieldsSavePaymentSession, } from "@paypal/react-paypal-js/sdk-v6"; import { useEffect } from "react"; function SaveCardForm() { const { submit, submitResponse, error } = usePayPalCardFieldsSavePaymentSession(); useEffect(() => { if (!submitResponse) return; const { vaultSetupToken, message } = submitResponse.data; switch (submitResponse.state) { case "succeeded": console.log(`Card saved: ${vaultSetupToken}`); break; case "failed": console.error(`Save failed: ${message}`); break; } }, [submitResponse]); const handleSaveCard = async () => { const response = await fetch("/api/create-vault-token", { method: "POST" }); const { vaultSetupToken } = await response.json(); await submit(vaultSetupToken); }; return (
); } function SaveCardPage() { return ( ); } ``` ## Server-Side Rendering with useFetchEligibleMethods The `useFetchEligibleMethods` function enables server-side pre-fetching of eligibility data. Pass the response to `PayPalProvider` via the `eligibleMethodsResponse` prop to avoid client-side eligibility fetches. ```tsx // app/checkout/page.tsx (Next.js server component) import { useFetchEligibleMethods } from "@paypal/react-paypal-js/sdk-v6/server"; import { PayPalProvider } from "@paypal/react-paypal-js/sdk-v6"; export default async function CheckoutPage() { const clientToken = await getClientToken(); // Your server function const eligibleMethodsResponse = await useFetchEligibleMethods({ environment: "sandbox", headers: { "Content-Type": "application/json", Authorization: `Bearer ${clientToken}`, }, payload: { purchase_units: [ { amount: { currency_code: "USD", value: "100.00" } }, ], preferences: { include_account_details: true, payment_flow: "ONE_TIME_PAYMENT", }, }, }); return ( ); } // Client component (CheckoutForm.tsx) "use client"; import { PayPalOneTimePaymentButton, VenmoOneTimePaymentButton, useEligibleMethods, } from "@paypal/react-paypal-js/sdk-v6"; function CheckoutForm() { // Eligibility data is pre-hydrated, no client-side fetch needed const { eligiblePaymentMethods, isLoading } = useEligibleMethods(); if (isLoading) return
Loading...
; const createOrder = async () => { const response = await fetch("/api/create-order", { method: "POST" }); const { orderId } = await response.json(); return { orderId }; }; return (
{eligiblePaymentMethods?.isEligible("paypal") && ( console.log("Approved:", orderId)} /> )} {eligiblePaymentMethods?.isEligible("venmo") && ( console.log("Venmo approved:", orderId)} /> )}
); } ``` ## loadCoreSdkScript Function (Vanilla JS) The `loadCoreSdkScript` function from `@paypal/paypal-js/sdk-v6` loads the PayPal V6 SDK asynchronously for non-React applications. ```typescript import { loadCoreSdkScript } from "@paypal/paypal-js/sdk-v6"; import type { OnApproveDataOneTimePayments } from "@paypal/paypal-js/sdk-v6"; async function initPayPal() { // Load the V6 Core SDK script const paypal = await loadCoreSdkScript({ environment: "sandbox", // "sandbox" | "production" debug: true, // Enable debug mode (optional) }); if (!paypal) { console.error("Failed to load PayPal SDK"); return; } // Create SDK instance with credentials const sdkInstance = await paypal.createInstance({ clientId: "YOUR_CLIENT_ID", components: ["paypal-payments", "venmo-payments"], locale: "en-US", pageType: "checkout", }); // Check eligibility before rendering const eligibility = await sdkInstance.findEligibleMethods({ purchase_units: [{ amount: { currency_code: "USD", value: "99.99" } }], }); if (eligibility.isEligible("paypal")) { const session = sdkInstance.createPayPalOneTimePaymentSession({ onApprove: async (data: OnApproveDataOneTimePayments) => { await fetch("/api/capture", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ orderId: data.orderId }), }); console.log("Payment captured!"); }, onCancel: () => console.log("Payment cancelled"), onError: (error) => console.error("Payment error:", error), }); // Start payment when user clicks button document.getElementById("paypal-btn")?.addEventListener("click", async () => { const orderResponse = await fetch("/api/create-order", { method: "POST" }); const { orderId } = await orderResponse.json(); await session.start({ presentationMode: "popup" }, { orderId }); }); } } initPayPal(); ``` ## loadScript Function (Legacy V5 SDK) The `loadScript` function from `@paypal/paypal-js` provides async script loading for the legacy PayPal JS SDK (V5). ```typescript import { loadScript } from "@paypal/paypal-js"; async function initLegacyPayPal() { try { const paypal = await loadScript({ clientId: "YOUR_CLIENT_ID", currency: "USD", components: ["buttons", "marks", "messages"], }); if (!paypal || !paypal.Buttons) { throw new Error("PayPal SDK not available"); } await paypal.Buttons({ createOrder: (data, actions) => { return actions.order.create({ purchase_units: [{ amount: { value: "99.99" }, }], }); }, onApprove: async (data, actions) => { const order = await actions.order?.capture(); console.log("Order captured:", order); }, onCancel: () => console.log("Payment cancelled"), onError: (err) => console.error("Error:", err), }).render("#paypal-button-container"); } catch (error) { console.error("Failed to load PayPal SDK:", error); } } initLegacyPayPal(); ``` ## Summary PayPal JS provides a comprehensive suite of tools for integrating PayPal payments across different JavaScript environments. For React applications, the `@paypal/react-paypal-js/sdk-v6` package offers a declarative, component-based approach with the `PayPalProvider` as the central entry point, pre-built button components for common payment flows (PayPal, Venmo, Pay Later, subscriptions, vaulting), and hooks for advanced customization. The Card Fields components enable PCI-compliant card data collection directly on merchant pages. Server-side rendering is supported through the `useFetchEligibleMethods` function for pre-fetching eligibility data. For non-React applications, the `@paypal/paypal-js` package provides the `loadCoreSdkScript` function for V6 SDK integration and `loadScript` for legacy V5 SDK support. Both packages include full TypeScript definitions and support all modern browsers. The typical integration pattern involves: (1) wrapping the application with `PayPalProvider` using client credentials, (2) checking payment eligibility with `useEligibleMethods`, (3) rendering appropriate button components or using session hooks for custom implementations, and (4) handling callbacks for order creation, approval, cancellation, and errors. Backend API endpoints are required for creating orders, capturing payments, and managing vault tokens.