These packages cover different layers of payment integration, ranging from server-side transaction validation to client-side UI components. @paypal/checkout-server-sdk handles backend logic for PayPal, while @paypal/react-paypal-js provides React components for PayPal buttons. @stripe/stripe-js is the core library for integrating Stripe payments in the browser, and react-payment-inputs offers generic UI hooks for building custom credit card forms. Understanding where each tool fits ensures secure and compliant payment architectures.
Integrating payments requires mixing server-side security with client-side interactivity. The packages listed here serve different roles — some run on your backend to protect secrets, while others run in the browser to handle user input. Let's compare how they tackle common integration tasks.
@paypal/checkout-server-sdk runs on your Node.js server.
// server-sdk: Node.js backend controller
const client = new paypal.core.PayPalEnvironment(clientId, clientSecret);
const request = new paypal.orders.OrdersCreateRequest();
request.prefer("return=representation");
request.requestBody({ intent: "CAPTURE", purchase_units: [{ amount: { currency_code: "USD", value: "100.00" } }] });
const response = await client.execute(request);
@paypal/react-paypal-js runs in the browser within React.
// react-paypal-js: React component
import { PayPalScriptProvider, PayPalButtons } from "@paypal/react-paypal-js";
<PayPalScriptProvider options={{ clientId: "..." }}>
<PayPalButtons createOrder={(data, actions) => {...}} />
</PayPalScriptProvider>
@stripe/stripe-js runs in the browser.
// stripe-js: Client-side initialization
import { loadStripe } from "@stripe/stripe-js";
const stripe = await loadStripe("pk_test_...");
const result = await stripe.confirmCardPayment(clientSecret);
react-payment-inputs runs in the browser.
// react-payment-inputs: Custom form hook
import { usePaymentInputs } from "react-payment-inputs";
const { getCardNumberProps } = usePaymentInputs();
<input {...getCardNumberProps()} />
@paypal/checkout-server-sdk requires credentials in code.
// server-sdk: Secure server setup
const client = new paypal.core.PayPalEnvironment(clientId, clientSecret);
@paypal/react-paypal-js uses a Context Provider.
// react-paypal-js: Provider wrapper
<PayPalScriptProvider options={{ clientId: "..." }}>
<App />
</PayPalScriptProvider>
@stripe/stripe-js uses a standalone loader function.
react-stripe-js).// stripe-js: Async loader
const stripe = await loadStripe("pk_test_...");
react-payment-inputs uses a React hook.
// react-payment-inputs: Hook usage
const { getCardNumberProps, getExpiryDateProps } = usePaymentInputs();
@paypal/checkout-server-sdk is fully secure by design.
// server-sdk: Server-side validation
const response = await client.execute(request);
// Validate response.status === "COMPLETED"
@paypal/react-paypal-js offloads security to PayPal.
// react-paypal-js: OnApprove callback
onApprove={(data, actions) => {
return actions.order.capture().then((details) => {
// Send details to your server for verification
});
}}
@stripe/stripe-js reduces PCI scope significantly.
// stripe-js: Confirming payment
const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
payment_method: { card: elements.getElement(CardElement) }
});
react-payment-inputs increases PCI scope.
// react-payment-inputs: Handling raw data
const { meta } = usePaymentInputs();
// meta.number contains the raw card number - handle with care
| Package | Environment | Primary Role | PCI Scope |
|---|---|---|---|
@paypal/checkout-server-sdk | Node.js Server | Transaction Validation | Low (Server-side) |
@paypal/react-paypal-js | React Browser | PayPal Buttons | None (Hosted Fields) |
@stripe/stripe-js | Browser | Stripe Logic | Low (Elements) |
react-payment-inputs | Browser | UI Input Masking | High (Raw Data) |
@paypal/checkout-server-sdk is mandatory for any PayPal integration that validates payments on your backend. Pair it with @paypal/react-paypal-js for the frontend buttons.
@stripe/stripe-js is the standard for Stripe integrations. Use it with Stripe Elements to keep PCI compliance simple.
react-payment-inputs should only be used if you need a fully custom card form design and cannot use Stripe Elements. Be aware of the security burden.
Overall: Prefer provider-specific SDKs (react-paypal-js, stripe-js) over generic UI libs (react-payment-inputs) to reduce security risk and maintenance work.
Choose this package when building React frontends that require PayPal checkout buttons or smart payment fields. It manages script loading states and provides ready-made components that reduce boilerplate code. It is the official way to integrate PayPal UIs into a React application.
Choose this package for Node.js backend services that need to create, validate, or capture PayPal orders securely. It keeps your client secrets off the client side and is essential for verifying transaction status before fulfilling orders. Do not use this in the browser as it exposes sensitive credentials.
Choose this package for integrating Stripe payments into any JavaScript application, especially when paired with React. It loads the secure Stripe.js library and enables tokenization of card data without handling raw numbers. It is the foundation for using Stripe Elements or Payment Intents.
Choose this package only if you need a fully custom credit card form design and cannot use provider-specific components like Stripe Elements. Be aware that using this increases your PCI compliance burden because you handle raw card data. It is best suited for projects where design control outweighs security convenience.
React components for the PayPal JS SDK V6
Using react-paypal-js version 8.x or earlier?
This documentation covers the V6 SDK integration introduced in v9.0.0. For the legacy integration using
PayPalScriptProvider,PayPalButtons,PayPalHostedFields, andBraintreePayPalButtons, see README-v8.md.
Integrating PayPal into React applications requires careful handling of SDK script loading, payment session management, and UI rendering. Building a robust integration from scratch can lead to issues with timing, state management, and buyer experience.
react-paypal-js provides a modern, hooks-based solution that abstracts away the complexities of the PayPal V6 SDK. It enforces best practices by default to ensure buyers get the best possible user experience.
Features
usePayPalOneTimePaymentSession, useVenmoOneTimePaymentSession, and moreuseEligibleMethods()<paypal-button>, <venmo-button>, and <paypal-pay-later-button> web componentsnpm install @paypal/react-paypal-js
import {
PayPalProvider,
PayPalOneTimePaymentButton,
} from "@paypal/react-paypal-js/sdk-v6";
function App() {
return (
<PayPalProvider
clientId="your-client-id"
components={["paypal-payments"]}
pageType="checkout"
>
<CheckoutPage />
</PayPalProvider>
);
}
function CheckoutPage() {
return (
<PayPalOneTimePaymentButton
createOrder={async () => {
const response = await fetch("/api/create-order", {
method: "POST",
});
const { orderId } = await response.json();
return { orderId };
}}
onApprove={async ({ orderId }: OnApproveDataOneTimePayments) => {
await fetch(`/api/capture-order/${orderId}`, {
method: "POST",
});
console.log("Payment captured!");
}}
/>
);
}
The PayPalProvider component is the entry point for the V6 SDK. It handles loading the PayPal SDK, creating an instance, and running eligibility checks.
| Prop | Type | Required | Description |
|---|---|---|---|
clientToken | string | Promise<string> | * | Client token from your server. Mutually exclusive with clientId. |
clientId | string | Promise<string> | * | Client ID from your PayPal app. Mutually exclusive with clientToken. |
components | Components[] | No | SDK components to load. Defaults to ["paypal-payments"]. |
pageType | string | No | Type of page: "checkout", "product-details", "cart", "product-listing", etc. |
locale | string | No | Locale for the SDK (e.g., "en_US"). |
environment | "sandbox" | "production" | No | SDK environment. |
merchantId | string | string[] | No | PayPal merchant ID(s). |
clientMetadataId | string | No | Client metadata ID for tracking. |
partnerAttributionId | string | No | Partner attribution ID (BN code). |
shopperSessionId | string | No | Shopper session ID for tracking. |
testBuyerCountry | string | No | Test buyer country code (sandbox only). |
debug | boolean | No | Enable debug mode. |
dataNamespace | string | No | Custom namespace for the SDK script data attribute. |
eligibleMethodsResponse | FindEligiblePaymentMethodsResponse | No | Server-fetched eligibility response for SDK hydration (see Server-Side Rendering). |
* Either
clientTokenorclientIdis required, but not both. They are mutually exclusive.
The components prop accepts an array of the following values:
"paypal-payments" - PayPal and Pay Later buttons"venmo-payments" - Venmo button"paypal-guest-payments" - Guest checkout (card payments)"paypal-subscriptions" - Subscription paymentsfunction App() {
// Memoize to prevent re-fetching on each render
const clientIdPromise = useMemo(() => fetchClientId(), []);
return (
<PayPalProvider
clientId={clientIdPromise}
components={["paypal-payments"]}
pageType="checkout"
>
<CheckoutPage />
</PayPalProvider>
);
}
Alternative: With Promise-based Token
function App() {
// Memoize to prevent re-fetching on each render
const tokenPromise = useMemo(() => fetchClientToken(), []);
return (
<PayPalProvider
clientToken={tokenPromise}
components={["paypal-payments"]}
pageType="checkout"
>
<CheckoutPage />
</PayPalProvider>
);
}
function App() {
const [clientId, setClientId] = useState<string>();
useEffect(() => {
fetchClientId().then(setClientId);
}, []);
return (
<PayPalProvider
clientId={clientId}
components={["paypal-payments"]}
pageType="checkout"
>
<CheckoutPage />
</PayPalProvider>
);
}
Use the usePayPal hook to access the SDK loading status:
import {
usePayPal,
INSTANCE_LOADING_STATE,
} from "@paypal/react-paypal-js/sdk-v6";
function CheckoutPage() {
const { loadingStatus, error } = usePayPal();
if (loadingStatus === INSTANCE_LOADING_STATE.PENDING) {
return <div className="spinner">Loading PayPal...</div>;
}
if (loadingStatus === INSTANCE_LOADING_STATE.REJECTED) {
return (
<div className="error">
Failed to load PayPal SDK: {error?.message}
</div>
);
}
return <PayPalOneTimePaymentButton orderId="ORDER-123" />;
}
Renders a PayPal button for one-time payments.
import { PayPalOneTimePaymentButton } from "@paypal/react-paypal-js/sdk-v6";
<PayPalOneTimePaymentButton
createOrder={async () => {
const response = await fetch("/api/create-order", { method: "POST" });
const { orderId } = await response.json();
return { orderId };
}}
onApprove={async ({ orderId }: OnApproveDataOneTimePayments) => {
await fetch(`/api/capture/${orderId}`, { method: "POST" });
console.log("Payment approved!");
}}
onCancel={(data: OnCancelDataOneTimePayments) =>
console.log("Payment cancelled")
}
onError={(data: OnErrorData) => console.error("Payment error:", data)}
onComplete={(data: OnCompleteData) => console.log("Payment Flow Completed")}
/>;
Props:
| Prop | Type | Description |
|---|---|---|
orderId | string | Static order ID (alternative to createOrder) |
createOrder | () => Promise<{ orderId: string }> | Async function to create an order |
presentationMode | "auto" | "popup" | "modal" | "redirect" | How to present the payment session (default: "auto") |
onApprove | (data) => void | Called when payment is approved |
onCancel | () => void | Called when buyer cancels |
onError | (error) => void | Called on error |
onComplete | (data) => void | Called when payment session completes |
type | "pay" | "checkout" | "buynow" | "donate" | "subscribe" | Button label type |
disabled | boolean | Disable the button |
Renders a Venmo button for one-time payments. Requires "venmo-payments" in the provider's components array.
import { VenmoOneTimePaymentButton } from "@paypal/react-paypal-js/sdk-v6";
<PayPalProvider
clientId={clientId}
components={["paypal-payments", "venmo-payments"]}
pageType="checkout"
>
<VenmoOneTimePaymentButton
createOrder={async () => {
const { orderId } = await createOrder();
return { orderId };
}}
onApprove={(data: OnApproveDataOneTimePayments) =>
console.log("Venmo payment approved!", data)
}
onCancel={(data: OnCancelDataOneTimePayments) =>
console.log("Venmo payment cancelled", data)
}
onError={(data: OnErrorData) =>
console.error("Venmo payment error:", data)
}
onComplete={(data: OnCompleteData) =>
console.log("Venmo payment flow completed", data)
}
/>
</PayPalProvider>;
Renders a Pay Later button for financing options. Country code and product code are automatically populated from eligibility data.
import { PayLaterOneTimePaymentButton } from "@paypal/react-paypal-js/sdk-v6";
<PayLaterOneTimePaymentButton
createOrder={async () => {
const { orderId } = await createOrder();
return { orderId };
}}
onApprove={(data: OnApproveDataOneTimePayments) =>
console.log("Pay Later approved!", data)
}
onCancel={(data: OnCancelDataOneTimePayments) =>
console.log("Pay Later cancelled", data)
}
onError={(data: OnErrorData) => console.error("Pay Later error:", data)}
onComplete={(data: OnCompleteData) =>
console.log("Pay Later flow completed", data)
}
/>;
Renders a guest checkout button for card payments without a PayPal account (Branded Card/Debit Card checkout). Requires "paypal-guest-payments" in the provider's components array.
import { PayPalGuestPaymentButton } from "@paypal/react-paypal-js/sdk-v6";
<PayPalProvider
clientId={clientId}
components={["paypal-payments", "paypal-guest-payments"]}
pageType="checkout"
>
<PayPalGuestPaymentButton
createOrder={async () => {
const { orderId } = await createOrder();
return { orderId };
}}
onApprove={(data: OnApproveDataOneTimePayments) =>
console.log("Guest payment approved!", data)
}
onCancel={(data: OnCancelDataOneTimePayments) =>
console.log("Guest payment cancelled", data)
}
onError={(data: OnErrorData) =>
console.error("Guest payment error:", data)
}
onComplete={(data: OnCompleteData) =>
console.log("Guest payment flow completed", data)
}
/>
</PayPalProvider>;
Renders a button for vaulting a payment method without making a purchase.
import { PayPalSavePaymentButton } from "@paypal/react-paypal-js/sdk-v6";
<PayPalSavePaymentButton
createVaultToken={async () => {
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={(data: OnCancelDataSavePayments) =>
console.log("Save payment cancelled", data)
}
onError={(data: OnErrorData) => console.error("Save payment error:", data)}
onComplete={(data: OnCompleteData) =>
console.log("Save payment flow completed", data)
}
/>;
Renders a PayPal button for subscription payments. Requires "paypal-subscriptions" in the provider's components array.
import { PayPalSubscriptionButton } from "@paypal/react-paypal-js/sdk-v6";
<PayPalProvider
clientId={clientId}
components={["paypal-subscriptions"]}
pageType="checkout"
>
<PayPalSubscriptionButton
createSubscription={async () => {
const response = await fetch("/api/create-subscription", {
method: "POST",
});
const { subscriptionId } = await response.json();
return { subscriptionId };
}}
onApprove={(data: OnApproveDataOneTimePayments) =>
console.log("Subscription approved:", data)
}
onCancel={(data: OnCancelDataOneTimePayments) =>
console.log("Subscription cancelled", data)
}
onError={(data: OnErrorData) =>
console.error("Subscription error:", data)
}
onComplete={(data: OnCompleteData) =>
console.log("Subscription flow completed", data)
}
/>
</PayPalProvider>;
Renders a PayPal Credit button for one-time payments. The countryCode is automatically populated from eligibility data.
import { PayPalCreditOneTimePaymentButton } from "@paypal/react-paypal-js/sdk-v6";
<PayPalCreditOneTimePaymentButton
createOrder={async () => {
const response = await fetch("/api/create-order", { method: "POST" });
const { orderId } = await response.json();
return { orderId };
}}
onApprove={({ orderId }: OnApproveDataOneTimePayments) =>
console.log("Credit payment approved:", orderId)
}
onCancel={(data: OnCancelDataOneTimePayments) =>
console.log("Credit payment cancelled", data)
}
onError={(data: OnErrorData) =>
console.error("Credit payment error:", data)
}
onComplete={(data: OnCompleteData) =>
console.log("Credit payment flow completed", data)
}
/>;
Renders a PayPal Credit button for saving a credit payment method (vaulting).
import { PayPalCreditSavePaymentButton } from "@paypal/react-paypal-js/sdk-v6";
<PayPalCreditSavePaymentButton
createVaultToken={async () => {
const response = await fetch("/api/create-vault-token", {
method: "POST",
});
const { vaultSetupToken } = await response.json();
return { vaultSetupToken };
}}
onApprove={(data: OnApproveDataSavePayments) =>
console.log("Credit saved:", data)
}
onCancel={(data: OnCancelDataSavePayments) =>
console.log("Credit save cancelled", data)
}
onError={(data: OnErrorData) => console.error("Credit save error:", data)}
onComplete={(data: OnCompleteData) =>
console.log("Credit save flow completed", data)
}
/>;
handleClick() starts the payment sessioncreateOrder callback creates an order via your backend APIonApprove callback captures the order via the backendReturns the PayPal context including the SDK instance and loading status.
import {
usePayPal,
INSTANCE_LOADING_STATE,
} from "@paypal/react-paypal-js/sdk-v6";
function MyComponent() {
const {
sdkInstance, // The PayPal SDK instance
eligiblePaymentMethods, // Eligible payment methods
loadingStatus, // PENDING | RESOLVED | REJECTED
error, // Any initialization error
isHydrated, // SSR hydration status
} = usePayPal();
const isPending = loadingStatus === INSTANCE_LOADING_STATE.PENDING;
const isReady = loadingStatus === INSTANCE_LOADING_STATE.RESOLVED;
// ...
}
Returns eligible payment methods and loading state. Use this to conditionally render payment buttons based on eligibility. This hook also updates the PayPalProvider reducer with Eligibility Output from the SDK, enabling built-in eligibility features in the UI Button components.
import { useEligibleMethods } from "@paypal/react-paypal-js/sdk-v6";
function PaymentOptions() {
const { eligiblePaymentMethods, isLoading, error } = useEligibleMethods();
if (isLoading) return <div>Checking eligibility...</div>;
const isPayPalEligible = eligiblePaymentMethods?.isEligible("paypal");
const isVenmoEligible = eligiblePaymentMethods?.isEligible("venmo");
const isPayLaterEligible = eligiblePaymentMethods?.isEligible("paylater");
return (
<div>
{isPayPalEligible && <PayPalOneTimePaymentButton {...props} />}
{isVenmoEligible && <VenmoOneTimePaymentButton {...props} />}
{isPayLaterEligible && <PayLaterOneTimePaymentButton {...props} />}
</div>
);
}
Hook for integrating PayPal messaging (Pay Later promotions).
import { usePayPalMessages } from "@paypal/react-paypal-js/sdk-v6";
function PayLaterMessage() {
const { error, isReady, handleFetchContent, handleCreateLearnMore } =
usePayPalMessages({
buyerCountry: "US",
currencyCode: "USD",
});
// Use to display financing messages
}
For advanced use cases where you need full control over the payment flow, use the session hooks directly with web components.
Note: One-time payment session hooks (e.g.,
usePayPalOneTimePaymentSession) accept either a staticorderIdor acreateOrdercallback — they are mutually exclusive. UseorderIdwhen you've already created the order, orcreateOrderto defer order creation until the buyer clicks. The same pattern applies to save payment hooks withvaultSetupTokenvscreateVaultToken.
| Hook | Payment Type |
|---|---|
usePayPalOneTimePaymentSession | PayPal |
useVenmoOneTimePaymentSession | Venmo |
usePayLaterOneTimePaymentSession | Pay Later |
usePayPalGuestPaymentSession | Basic Card |
usePayPalSubscriptionPaymentSession | Subscriptions |
usePayPalSavePaymentSession | Save Payment Method |
usePayPalCreditOneTimePaymentSession | Credit (One-time) |
usePayPalCreditSavePaymentSession | Credit (Save) |
import { usePayPalOneTimePaymentSession } from "@paypal/react-paypal-js/sdk-v6";
function CustomPayPalButton() {
const { isPending, error, handleClick } = usePayPalOneTimePaymentSession({
createOrder: async () => {
const { orderId } = await createOrder();
return { orderId };
},
presentationMode: "auto",
onApprove: (data: OnApproveDataOneTimePayments) =>
console.log("Approved:", data),
onCancel: (data: OnCancelDataOneTimePayments) =>
console.log("Cancelled"),
onError: (data: OnErrorData) => console.error(data),
onComplete: (data: OnCompleteData) =>
console.log("Payment session complete", data),
});
return (
<paypal-button
onClick={() => handleClick()}
type="pay"
disabled={isPending || error !== null}
/>
);
}
import { useVenmoOneTimePaymentSession } from "@paypal/react-paypal-js/sdk-v6";
function CustomVenmoButton() {
const { handleClick } = useVenmoOneTimePaymentSession({
createOrder: async () => {
const { orderId } = await createOrder();
return { orderId };
},
onApprove: (data: OnApproveDataOneTimePayments) =>
console.log("Approved:", data),
onCancel: (data: OnCancelDataOneTimePayments) =>
console.log("Cancelled", data),
onError: (data: OnErrorData) => console.error("Error:", data),
onComplete: (data: OnCompleteData) =>
console.log("Payment session complete", data),
});
return <venmo-button onClick={() => handleClick()} />;
}
import { usePayLaterOneTimePaymentSession } from "@paypal/react-paypal-js/sdk-v6";
function CustomPayLaterButton() {
const { handleClick } = usePayLaterOneTimePaymentSession({
createOrder: async () => {
const { orderId } = await createOrder();
return { orderId };
},
onApprove: (data: OnApproveDataOneTimePayments) =>
console.log("Approved:", data),
onCancel: (data: OnCancelDataOneTimePayments) =>
console.log("Cancelled", data),
onError: (data: OnErrorData) => console.error("Error:", data),
onComplete: (data: OnCompleteData) =>
console.log("Payment session complete", data),
});
return <paypal-pay-later-button onClick={() => handleClick()} />;
}
import { usePayPalGuestPaymentSession } from "@paypal/react-paypal-js/sdk-v6";
function CustomPayPalGuestButton() {
const { handleClick, buttonRef } = usePayPalGuestPaymentSession({
createOrder: async () => {
const { orderId } = await createOrder();
return { orderId };
},
onApprove: (data: OnApproveDataOneTimePayments) =>
console.log("Approved:", data),
onCancel: (data: OnCancelDataOneTimePayments) =>
console.log("Cancelled", data),
onError: (data: OnErrorData) => console.error("Error:", data),
onComplete: (data: OnCompleteData) =>
console.log("Payment session complete", data),
});
return (
<paypal-basic-card-container>
<paypal-basic-card-button
ref={buttonRef}
onClick={() => handleClick()}
/>
</paypal-basic-card-container>
);
}
import { usePayPalSavePaymentSession } from "@paypal/react-paypal-js/sdk-v6";
function CustomPayPalSaveButton() {
const { handleClick } = usePayPalSavePaymentSession({
createVaultToken: async () => {
const { vaultSetupToken } = await createVaultToken();
return { vaultSetupToken };
},
presentationMode: "popup",
onApprove: (data: OnApproveDataSavePayments) =>
console.log("Saved:", data),
onCancel: (data: OnCancelDataSavePayments) =>
console.log("Cancelled", data),
onError: (data: OnErrorData) => console.error("Error:", data),
onComplete: (data: OnCompleteData) =>
console.log("Payment session complete", data),
});
return <paypal-button onClick={() => handleClick()} type="pay" />;
}
import { usePayPalSubscriptionPaymentSession } from "@paypal/react-paypal-js/sdk-v6";
function CustomPayPalSubscriptionButton() {
const { handleClick } = usePayPalSubscriptionPaymentSession({
createSubscription: async () => {
const response = await fetch("/api/create-subscription", {
method: "POST",
});
const { subscriptionId } = await response.json();
return { subscriptionId };
},
onApprove: (data: OnApproveDataOneTimePayments) =>
console.log("Subscription approved:", data),
onCancel: (data: OnCancelDataOneTimePayments) =>
console.log("Cancelled", data),
onError: (data: OnErrorData) => console.error("Error:", data),
onComplete: (data: OnCompleteData) =>
console.log("Payment session complete", data),
});
return <paypal-button onClick={() => handleClick()} type="subscribe" />;
}
For PayPal Credit one-time payments.
import { usePayPalCreditOneTimePaymentSession } from "@paypal/react-paypal-js/sdk-v6";
function CustomPayPalCreditButton() {
const { handleClick } = usePayPalCreditOneTimePaymentSession({
createOrder: async () => {
const { orderId } = await createOrder();
return { orderId };
},
onApprove: (data: OnApproveDataOneTimePayments) =>
console.log("Credit approved:", data),
onCancel: (data: OnCancelDataOneTimePayments) =>
console.log("Cancelled", data),
onError: (data: OnErrorData) => console.error("Error:", data),
onComplete: (data: OnCompleteData) =>
console.log("Payment session complete", data),
});
return <paypal-credit-button onClick={() => handleClick()} />;
}
For saving PayPal Credit as a payment method.
import { usePayPalCreditSavePaymentSession } from "@paypal/react-paypal-js/sdk-v6";
function CustomPayPalCreditSaveButton() {
const { handleClick } = usePayPalCreditSavePaymentSession({
createVaultToken: async () => {
const { vaultSetupToken } = await createVaultSetupToken();
return { vaultSetupToken };
},
onApprove: (data: OnApproveDataSavePayments) =>
console.log("Credit approved:", data),
onCancel: (data: OnCancelDataSavePayments) =>
console.log("Cancelled", data),
onError: (data: OnErrorData) => console.error("Error:", data),
onComplete: (data: OnCompleteData) =>
console.log("Payment session complete", data),
});
return <paypal-credit-button onClick={() => handleClick()} />;
}
The V6 SDK uses web components for rendering buttons. These are automatically typed when you import from @paypal/react-paypal-js/sdk-v6.
| Component | Description |
|---|---|
<paypal-button> | PayPal payment button |
<venmo-button> | Venmo payment button |
<paypal-pay-later-button> | Pay Later button |
<paypal-basic-card-container> | Guest checkout container |
<paypal-basic-card-button> | Guest checkout button |
<paypal-credit-button> | PayPal Credit button |
<paypal-message> | PayPal messaging component |
The type prop controls the button label:
"pay" - "Pay with PayPal" (default)"checkout" - "Checkout with PayPal""buynow" - "Buy Now""donate" - "Donate""subscribe" - "Subscribe"<paypal-button type="checkout" onClick={handleClick} />
The useFetchEligibleMethods function is available from the server export path for pre-fetching eligibility data on the server. Pass the response to PayPalProvider via the eligibleMethodsResponse prop to avoid a client-side eligibility fetch.
// 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 eligibleMethodsResponse = await useFetchEligibleMethods({
environment: "sandbox",
headers: {
Authorization: `Bearer ${clientToken}`,
"Content-Type": "application/json",
},
payload: {
purchase_units: [
{ amount: { currency_code: "USD", value: "100.00" } },
],
},
});
return (
<PayPalProvider
clientId={clientId}
pageType="checkout"
eligibleMethodsResponse={eligibleMethodsResponse}
>
<CheckoutForm />
</PayPalProvider>
);
}
The v9.0.0 release introduces the V6 SDK with a new API. Here are the key differences:
| v8.x (Legacy) | v9.0.0 (V6 SDK) |
|---|---|
PayPalScriptProvider | PayPalProvider |
PayPalButtons | PayPalOneTimePaymentButton or hooks |
options={{ clientId }} | clientId={clientId} or clientToken={clientToken} |
createOrder returns orderId | createOrder returns { orderId } |
@paypal/react-paypal-js | @paypal/react-paypal-js/sdk-v6 |
import { PayPalScriptProvider, PayPalButtons } from "@paypal/react-paypal-js";
<PayPalScriptProvider options={{ clientId: "test" }}>
<PayPalButtons
createOrder={() => {
return fetch("/api/orders", { method: "POST" })
.then((res) => res.json())
.then((order) => order.id);
}}
onApprove={(data) => {
return fetch(`/api/orders/${data.orderID}/capture`, {
method: "POST",
});
}}
/>
</PayPalScriptProvider>;
import {
PayPalProvider,
PayPalOneTimePaymentButton,
} from "@paypal/react-paypal-js/sdk-v6";
<PayPalProvider clientId={clientId} pageType="checkout">
<PayPalOneTimePaymentButton
createOrder={async () => {
const res = await fetch("/api/orders", { method: "POST" });
const order = await res.json();
return { orderId: order.id };
}}
onApprove={async ({ orderId }) => {
await fetch(`/api/orders/${orderId}/capture`, { method: "POST" });
}}
/>
</PayPalProvider>;
For the legacy API documentation, see README-v8.md.
This package includes full TypeScript definitions. Import types from the same path:
import type {
// Web component props
ButtonProps,
PayLaterButtonProps,
PayPalBasicCardButtonProps,
PayPalCreditButtonProps,
// Session hook props
UsePayPalOneTimePaymentSessionProps,
UseVenmoOneTimePaymentSessionProps,
UsePayLaterOneTimePaymentSessionProps,
UsePayPalGuestPaymentSessionProps,
UsePayPalSubscriptionPaymentSessionProps,
UsePayPalSavePaymentSessionProps,
UsePayPalCreditOneTimePaymentSessionProps,
UsePayPalCreditSavePaymentSessionProps,
// Button component props
PayPalSubscriptionButtonProps,
PayPalCreditOneTimePaymentButtonProps,
PayPalCreditSavePaymentButtonProps,
// Enums
INSTANCE_LOADING_STATE,
} from "@paypal/react-paypal-js/sdk-v6";
The package automatically extends JSX types to include PayPal web components. No additional configuration is needed for React 17, 18, or 19.
This library supports all modern browsers. See the PayPal browser support documentation for the full list.