# Integrate Card Fields (/v5/expanded/integrate)

Set up PayPal Card Fields with the JavaScript SDK v5 to accept debit and credit card payments with your site's branding.



> **Note:** This page contains content that varies depending on which option is selected: HTML, React. Sections marked with a specific option name apply only when that option is chosen.

> **Warning:** **Important:** New integrations should use the [JavaScript SDK v6](/checkout/integrate).
>
> This integration uses the JavaScript SDK v5. Use it only to troubleshoot existing integrations.
>
> These are the integration instructions for the JavaScript SDK component CardFields. If your integration uses the HostedFields component, see [Integrate PayPal buttons and Hosted Fields](https://developer.paypal.com/docs/checkout/advanced/sdk/v1/) instead.

Integrate Card Fields with the JavaScript SDK v5 to accept credit and debit card payments directly on your site, using your own branding.

## Prerequisites [#prerequisites]

* [Get your client ID and secret](/api/rest#1-get-client-id-and-client-secret).
* Optional: Get sample code from the [GitHub Codespace](https://github.com/codespaces/new/paypal-examples/docs-examples?devcontainer_path=.devcontainer%2Fstandard-integration%2Fdevcontainer.json) or the [Postman collection](https://www.postman.com/paypal/paypal-public-api-workspace/flow/649ca4a7795511003133311e).

> **HTML:** ## 1. Integrate front end [#1-integrate-front-end]
>
> Set up your front end to integrate expanded checkout payments.
>
> Your app shows the PayPal card fields and payment buttons. When a customer selects a button, your app calls server endpoints to create the order and capture the payment.
>
> The `/src/index.html` and `/src/app.js` files handle the client-side logic and define how the PayPal front-end components connect with the back end. Use these files to set up the PayPal checkout using the JavaScript SDK and handle the payer's interactions with the PayPal checkout button.
>
> You'll need to save the `index.html` and `app.js` files in a folder named `/src`.
>
> ### Add the script tag and configure parameters [#add-the-script-tag-and-configure-parameters]
>
> Include the `<script>` tag on any page that shows the PayPal buttons. This script fetches all the necessary JavaScript to access the buttons on the window object.
>
> Configure your script parameters:
>
> * Pass a `client-id` and specify which components you want to use. The SDK offers buttons, marks, card fields, and other components. This sample uses the `buttons` and `card-fields` components.
> * Pass the `currency` you want to use for pricing. This example uses USD. `buyer-country` and `currency` are only for use in sandbox testing. Do not use these in production.
>
> > **Note:** Pass values in [`disable-funding`](/sdk/js/v5/configuration#disable-funding) and [`enable-funding`](/sdk/js/v5/configuration#enable-funding) to control which funding sources to offer or exclude. For example, if you want to offer Venmo as a payment method, add `enable-funding=venmo` to your script tag.
>
> Add the containers for the PayPal buttons, the card fields hosted by PayPal, and a submit button to your `index.html`:
>
> ```html lineNumbers
> <!DOCTYPE html>
> <html lang="en">
>     <head>
>         <meta charset="UTF-8" />
>         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
>         <link
>             rel="stylesheet"
>             type="text/css"
>             href="https://www.paypalobjects.com/webstatic/en_US/developer/docs/css/cardfields.css"
>         />
>         <title>PayPal JS SDK Advanced Integration - Checkout Flow</title>
>         <script
>             src="https://www.paypal.com/sdk/js?client-id=test&buyer-country=US&currency=USD&components=buttons,card-fields&enable-funding=venmo"
>             data-sdk-integration-source="developer-studio"
>         ></script>
>     </head>
>     <body>
>         <div id="paypal-button-container" class="paypal-button-container"></div>
>         <!-- Containers for Card Fields hosted by PayPal -->
>         <div id="card-form" class="card_container">
>             <div id="card-name-field-container"></div>
>             <div id="card-number-field-container"></div>
>             <div id="card-expiry-field-container"></div>
>             <div id="card-cvv-field-container"></div>
>
>             <br /><br />
>             <button id="card-field-submit-button" type="button">
>                 Pay now with Card
>             </button>
>         </div>
>         <p id="result-message"></p>
>         <script src="app.js"></script>
>     </body>
> </html>
> ```
>
> ### Render card fields and PayPal buttons [#render-card-fields-and-paypal-buttons]
>
> After setting up the SDK for your website, you need to render the PayPal buttons and the card fields components.
>
> The `paypal` namespace has a `CardFields` component to accept and save cards without handling card information. PayPal handles all security and compliance issues associated with processing cards. The `CardFields` function checks to see if a payment is eligible for card fields. If not, the card fields don't appear during the payment flow.
>
> The `paypal` namespace also has a `Buttons` function that initiates the callbacks needed to set up a payment.
>
> Both components use a shared `createOrderCallback` and `onApproveCallback`. The `createOrder` callback launches when the customer selects the payment button, starts the order, and returns an order ID. Completing the payment launches the `onApprove` callback. Use this callback to update business logic, show a confirmation page, or handle error responses.
>
> To override the default style settings for the buttons, use a `style` object inside the `Buttons` component. You can lay out the buttons in a horizontal or vertical stack and customize them with different colors and shapes. Read more about how to customize your payment buttons in the [style section of the JavaScript SDK v5 reference](/sdk/js/v5/reference#style).
>
> For the card fields, define styles for each field using the `style` object. You can also define the `selector` and `placeholder` values for the input fields. For more information about optional configurations, see [Card fields in the JavaScript SDK v5 reference](/sdk/js/v5/reference#card-fields).
>
> Pass the card field values, such as the cardholder's name and address, to the POST call through the `cardField.submit()` function. Anything you pass into the submit function is sent to the iframe that communicates with the Orders API. See the [Orders v2 API](/api/orders/v2) for details about billing address fields and other parameters.
>
> Set up your app to handle the payment capture response for three cases: a recoverable `INSTRUMENT_DECLINED` error, other non-recoverable errors, and a successful transaction.
>
> ```javascript lineNumbers
> // Render the button component
> paypal
>     .Buttons({
>         // Sets up the transaction when a payment button is clicked
>         createOrder: createOrderCallback,
>         onApprove: onApproveCallback,
>         onError: function (error) {
>             // Do something with the error from the SDK
>         },
>
>         style: {
>             shape: "rect",
>             layout: "vertical",
>             color: "gold",
>             label: "paypal",
>         },
>         message: {
>             amount: 100,
>         },
>     })
>     .render("#paypal-button-container");
>
> // Render each field after checking for eligibility
> const cardField = window.paypal.CardFields({
>     createOrder: createOrderCallback,
>     onApprove: onApproveCallback,
>     style: {
>         input: {
>             "font-size": "16px",
>             "font-family": "courier, monospace",
>             "font-weight": "lighter",
>             color: "#ccc",
>         },
>         ".invalid": { color: "purple" },
>     },
> });
>
> if (cardField.isEligible()) {
>     const nameField = cardField.NameField({
>         style: { input: { color: "blue" }, ".invalid": { color: "purple" } },
>     });
>     nameField.render("#card-name-field-container");
>
>     const numberField = cardField.NumberField({
>         style: { input: { color: "blue" } },
>     });
>     numberField.render("#card-number-field-container");
>
>     const cvvField = cardField.CVVField({
>         style: { input: { color: "blue" } },
>     });
>     cvvField.render("#card-cvv-field-container");
>
>     const expiryField = cardField.ExpiryField({
>         style: { input: { color: "blue" } },
>     });
>     expiryField.render("#card-expiry-field-container");
>
>     // Add click listener to submit button and call the submit function on the CardField component
>     document
>         .getElementById("card-field-submit-button")
>         .addEventListener("click", () => {
>             cardField.submit({}).then(() => {
>                 // submit successful
>             });
>         });
> }
>
>
> async function createOrderCallback() {
>     resultMessage("");
>     try {
>         const response = await fetch("/api/orders", {
>             method: "POST",
>             headers: {
>                 "Content-Type": "application/json",
>             },
>             // use the "body" param to optionally pass additional order information
>             // like product ids and quantities
>             body: JSON.stringify({
>                 cart: [
>                     {
>                         id: "YOUR_PRODUCT_ID",
>                         quantity: "YOUR_PRODUCT_QUANTITY",
>                     },
>                 ],
>             }),
>         });
>
>         const orderData = await response.json();
>
>         if (orderData.id) {
>             return orderData.id;
>         } else {
>             const errorDetail = orderData?.details?.[0];
>             const errorMessage = errorDetail
>                 ? `${errorDetail.issue} ${errorDetail.description} (${orderData.debug_id})`
>                 : JSON.stringify(orderData);
>
>             throw new Error(errorMessage);
>         }
>     } catch (error) {
>         console.error(error);
>         resultMessage(`Could not initiate PayPal Checkout...<br><br>${error}`);
>         throw error;
>     }
> }
>
> async function onApproveCallback(data, actions) {
>     try {
>         const response = await fetch(`/api/orders/${data.orderID}/capture`, {
>             method: "POST",
>             headers: {
>                 "Content-Type": "application/json",
>             },
>         });
>
>         const orderData = await response.json();
>         // Three cases to handle:
>         //   (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
>         //   (2) Other non-recoverable errors -> Show a failure message
>         //   (3) Successful transaction -> Show confirmation or thank you message
>
>         const transaction =
>             orderData?.purchase_units?.[0]?.payments?.captures?.[0] ||
>             orderData?.purchase_units?.[0]?.payments?.authorizations?.[0];
>         const errorDetail = orderData?.details?.[0];
>
>         if (errorDetail || !transaction || transaction.status === "DECLINED") {
>             // (2) Other non-recoverable errors -> Show a failure message
>             let errorMessage;
>             if (transaction) {
>                 errorMessage = `Transaction ${transaction.status}: ${transaction.id}`;
>             } else if (errorDetail) {
>                 errorMessage = `${errorDetail.description} (${orderData.debug_id})`;
>             } else {
>                 errorMessage = JSON.stringify(orderData);
>             }
>
>             throw new Error(errorMessage);
>         } else {
>             // (3) Successful transaction -> Show confirmation or thank you message
>             // Or go to another URL:  actions.redirect('thank_you.html');
>             resultMessage(
>                 `Transaction ${transaction.status}: ${transaction.id}<br><br>See console for all available details`
>             );
>             console.log(
>                 "Capture result",
>                 orderData,
>                 JSON.stringify(orderData, null, 2)
>             );
>         }
>     } catch (error) {
>         console.error(error);
>         resultMessage(
>             `Sorry, your transaction could not be processed...<br><br>${error}`
>         );
>     }
> }
>
> // Example function to show a result to the user. Your site's UI library can be used instead.
> function resultMessage(message) {
>     const container = document.querySelector("#result-message");
>     container.innerHTML = message;
> }
> ```
>
> ### Integrate 3D Secure using JavaScript SDK [#integrate-3d-secure-using-javascript-sdk]
>
> To trigger 3D Secure authentication, pass the verification method in the Create order payload. The verification method can be a `contingencies` parameter with `SCA_ALWAYS` or `SCA_WHEN_REQUIRED`:
>
> * Pass `SCA_ALWAYS` to trigger an authentication for every transaction.
> * Pass `SCA_WHEN_REQUIRED` to trigger an authentication only when required by a regional compliance mandate such as PSD2. 3D Secure is supported only in countries with a PSD2 compliance mandate.
>
> > **Warning:** These are the 3D Secure instructions for the JavaScript SDK component CardFields. If your integration uses the HostedFields component, see [Integrate 3D Secure using Hosted Fields](https://developer.paypal.com/docs/checkout/advanced/sdk/v1/) instead.

> **React:** ## 1. Integrate front end [#1-integrate-front-end-1]
>
> Set up your front end to integrate expanded checkout payments.
>
> Your app shows the PayPal card fields and payment buttons. When a customer selects a button, your app calls server endpoints to create the order and capture the payment.
>
> The `App.jsx` and `main.jsx` files handle the client-side logic and define how the PayPal front-end components connect with the back end. Use these files to set up payments using the JavaScript SDK and handle the payer's interactions with the PayPal Card Fields component.
>
> You'll need to save the `App.jsx` and `main.jsx` files in a folder named `/client/react/src`.
>
> ### Add the Script Provider and configure parameters [#add-the-script-provider-and-configure-parameters]
>
> Import `PayPalScriptProvider` from the `@paypal/react-paypal-js` library. This provider fetches the SDK so you can access the other child components.
>
> Configure the SDK by passing the `client-id` and `components` parameters to `PayPalScriptProvider` as the `options` prop.
>
> Pass values in [`disable-funding`](/sdk/js/v5/configuration#disable-funding) and [`enable-funding`](/sdk/js/v5/configuration#enable-funding) to control which funding sources to offer or exclude. For example, if you want to offer Venmo as a payment method, add `enable-funding=venmo`.
>
> `buyer-country` and `currency` are only for use in sandbox testing. Do not use these in production.
>
> ### Render card fields and PayPal buttons [#render-card-fields-and-paypal-buttons-1]
>
> After setting up the SDK for your website, you need to render the PayPal buttons and the card fields components.
>
> **Integrate card fields**
>
> Import `PayPalCardFieldsProvider` from `@paypal/react-paypal-js`. This is the parent component. It manages the state related to loading the JS SDK script and validates the request before rendering the card fields.
>
> There are two ways to render the card fields:
>
> * Use the `PayPalCardFieldsForm` React component, imported from `@paypal/react-paypal-js`. This renders the four card fields by default: Name, Number, Expiry, and CVV.
> * Use the individual card field components for more granular control over each field and more layout flexibility:
>
> | Component               | Field                                 | Required |
> | ----------------------- | ------------------------------------- | -------- |
> | `<PayPalNameField />`   | Name for the card                     | No       |
> | `<PayPalNumberField />` | Card number                           | Yes      |
> | `<PayPalExpiryField />` | Card expiration date                  | Yes      |
> | `<PayPalCVVField />`    | Card CVV or CID, a 3- or 4-digit code | Yes      |
>
> **Integrate PayPal buttons**
>
> Import the `PayPalButtons` component from the PayPal React library. Place the `PayPalButtons` component inside the `PayPalScriptProvider`, or the system returns an error.
>
> The `createOrder` callback launches when the buyer selects a payment button. The callback starts the order and returns an order ID. Completing the payment launches an `onApprove` callback. Use the `onApprove` response to update business logic, show a confirmation page, or handle error responses.
>
> Set up your app to handle the payment capture response for three cases: a recoverable `INSTRUMENT_DECLINED` error, other non-recoverable errors, and a successful transaction.
>
> App.jsx
>
> ```jsx lineNumbers 
> import React, { useState } from "react";
>
> import {
>     PayPalScriptProvider,
>     usePayPalCardFields,
>     PayPalCardFieldsProvider,
>     PayPalButtons,
>     PayPalNameField,
>     PayPalNumberField,
>     PayPalExpiryField,
>     PayPalCVVField,
> } from "@paypal/react-paypal-js";
>
> export default function App() {
>     const [isPaying, setIsPaying] = useState(false);
>     const initialOptions = {
>         "client-id": "YOUR_CLIENT_ID",
>         "enable-funding": "venmo",
>         "disable-funding": "",
>         "buyer-country": "US",
>         currency: "USD",
>         "data-page-type": "product-details",
>         components: "buttons,card-fields",
>         "data-sdk-integration-source": "developer-studio",
>     };
>
>    const [billingAddress, setBillingAddress] =
>         useState({
>             addressLine1: "",
>             addressLine2: "",
>             adminArea1: "",
>             adminArea2: "",
>             countryCode: "",
>             postalCode: "",
>         });
>
>     function handleBillingAddressChange(field, value) {
>         setBillingAddress((prev) => ({
>             ...prev,
>             [field]: value,
>         }));
>     }
>     async function createOrder() {
>         try {
>             const response = await fetch("/api/orders", {
>                 method: "POST",
>                 headers: {
>                     "Content-Type": "application/json",
>                 },
>                 // use the "body" param to optionally pass additional order information
>                 // like product ids and quantities
>                 body: JSON.stringify({
>                     cart: [
>                         {
>                             sku: "1blwyeo8",
>                             quantity: 2,
>                         },
>                     ],
>                 }),
>             });
>
>             const orderData = await response.json();
>
>             if (orderData.id) {
>                 return orderData.id;
>             } else {
>                 const errorDetail = orderData?.details?.[0];
>                 const errorMessage = errorDetail
>                     ? `${errorDetail.issue} ${errorDetail.description} (${orderData.debug_id})`
>                     : JSON.stringify(orderData);
>
>                 throw new Error(errorMessage);
>             }
>         } catch (error) {
>             console.error(error);
>             throw error;
>         }
>     }
>
>     async function onApprove(data, actions) {
>         try {
>             const response = await fetch(
>                 `/api/orders/${data.orderID}/capture`,
>                 {
>                     method: "POST",
>                     headers: {
>                         "Content-Type": "application/json",
>                     },
>                 }
>             );
>
>             const orderData = await response.json();
>             // Three cases to handle:
>             //   (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart()
>             //   (2) Other non-recoverable errors -> Show a failure message
>             //   (3) Successful transaction -> Show confirmation or thank you message
>
>             const transaction =
>                 orderData?.purchase_units?.[0]?.payments?.captures?.[0] ||
>                 orderData?.purchase_units?.[0]?.payments?.authorizations?.[0];
>             const errorDetail = orderData?.details?.[0];
>
>             if (
>                 errorDetail ||
>                 !transaction ||
>                 transaction.status === "DECLINED"
>             ) {
>                 // (2) Other non-recoverable errors -> Show a failure message
>                 let errorMessage;
>                 if (transaction) {
>                     errorMessage = `Transaction ${transaction.status}: ${transaction.id}`;
>                 } else if (errorDetail) {
>                     errorMessage = `${errorDetail.description} (${orderData.debug_id})`;
>                 } else {
>                     errorMessage = JSON.stringify(orderData);
>                 }
>
>                 throw new Error(errorMessage);
>             } else {
>                 // (3) Successful transaction -> Show confirmation or thank you message
>                 // Or go to another URL:  actions.redirect('thank_you.html');
>                 console.log(
>                     "Capture result",
>                     orderData,
>                     JSON.stringify(orderData, null, 2)
>                 );
>                 return `Transaction ${transaction.status}: ${transaction.id}. See console for all available details`;
>             }
>         } catch (error) {
>             return `Sorry, your transaction could not be processed...${error}`;
>         }
>     }
>
>     function onError(error) {
>         // Do something with the error from the SDK
>     }
>
>     return (
>         <PayPalScriptProvider options={initialOptions}>
>            
>             <PayPalButtons
>                 createOrder={createOrder}
>                 onApprove={onApprove}
>                 onError={onError}
>                style={{
>                     shape: "rect",
>                     layout: "vertical",
>                     color: "gold",
>                     label: "paypal",
>                 }}
>             />
>            
>             <PayPalCardFieldsProvider
>                 createOrder={createOrder}
>                 onApprove={onApprove}
>                 style={{
>                     input: {
>                         "font-size": "16px",
>                         "font-family": "courier, monospace",
>                         "font-weight": "lighter",
>                         color: "#ccc",
>                     },
>                     ".invalid": { color: "purple" },
>                 }}
>             >
>                 <PayPalNameField
>                     style={{
>                         input: { color: "blue" },
>                         ".invalid": { color: "purple" },
>                     }}
>                 />
>                 <PayPalNumberField />
>                 <PayPalExpiryField />
>                 <PayPalCVVField />
>                
>                 <input
>                     type="text"
>                     id="card-billing-address-line-1"
>                     name="card-billing-address-line-1"
>                     placeholder="Address line 1"
>                     onChange={(e) =>
>                         handleBillingAddressChange(
>                             "addressLine1",
>                             e.target.value
>                         )
>                     }
>                 />
>                 <input
>                     type="text"
>                     id="card-billing-address-line-2"
>                     name="card-billing-address-line-2"
>                     placeholder="Address line 2"
>                     onChange={(e) =>
>                         handleBillingAddressChange(
>                             "addressLine2",
>                             e.target.value
>                         )
>                     }
>                 />
>                 <input
>                     type="text"
>                     id="card-billing-address-admin-area-line-1"
>                     name="card-billing-address-admin-area-line-1"
>                     placeholder="Admin area line 1"
>                     onChange={(e) =>
>                         handleBillingAddressChange("adminArea1", e.target.value)
>                     }
>                 />
>                 <input
>                     type="text"
>                     id="card-billing-address-admin-area-line-2"
>                     name="card-billing-address-admin-area-line-2"
>                     placeholder="Admin area line 2"
>                     onChange={(e) =>
>                         handleBillingAddressChange("adminArea2", e.target.value)
>                     }
>                 />
>                 <input
>                     type="text"
>                     id="card-billing-address-country-code"
>                     name="card-billing-address-country-code"
>                     placeholder="Country code"
>                     onChange={(e) =>
>                         handleBillingAddressChange(
>                             "countryCode",
>                             e.target.value
>                         )
>                     }
>                 />
>                 <input
>                     type="text"
>                     id="card-billing-address-postal-code"
>                     name="card-billing-address-postal-code"
>                     placeholder="Postal/zip code"
>                     onChange={(e) =>
>                         handleBillingAddressChange("postalCode", e.target.value)
>                     }
>                 />
>                
>                 {/* Custom client component to handle card fields submission */}
>                 <SubmitPayment
>                     isPaying={isPaying}
>                     setIsPaying={setIsPaying}
>                    billingAddress={
>                         billingAddress
>                     }
>                 />
>             </PayPalCardFieldsProvider>
>            
>         </PayPalScriptProvider>
>     );
> }
>
> const SubmitPayment = ({ isPaying, setIsPaying, billingAddress }) => {
>     const { cardFieldsForm, fields } = usePayPalCardFields();
>
>     const handleClick = async () => {
>         if (!cardFieldsForm) {
>             const childErrorMessage =
>                 "Unable to find any child components in the <PayPalCardFieldsProvider />";
>
>             throw new Error(childErrorMessage);
>         }
>         const formState = await cardFieldsForm.getState();
>
>         if (!formState.isFormValid) {
>             return alert("The payment form is invalid");
>         }
>         setIsPaying(true);
>
>         cardFieldsForm.submit({ billingAddress }).catch((err) => {
>             setIsPaying(false);
>         });
>     };
>
>     return (
>         <button
>             className={isPaying ? "btn" : "btn btn-primary"}
>             style={{ float: "right" }}
>             onClick={handleClick}
>         >
>             {isPaying ? <div className="spinner tiny" /> : "Pay"}
>         </button>
>     );
> };
> ```
>
> ### Customize card fields [#customize-card-fields]
>
> Optional: To customize the card fields, define styles for each field using the `style` object inside `PayPalCardFieldsProvider`. You can also style individual fields by passing a `style` prop directly to each field component.
>
> Pass card field values, such as the cardholder's name and billing address, through the `cardFieldsForm.submit()` function. Anything passed into the submit function is sent to the iframe that communicates with the Orders API. See the [Orders v2 API](/api/orders/v2) for details about billing address fields and other parameters.
>
> ### Configure the layout of the Buttons component [#configure-the-layout-of-the-buttons-component]
>
> Optional: To override the default style settings for your page, use a `style` object inside the `Buttons` component. You can lay out the buttons in a horizontal or vertical stack and customize them with different colors and shapes. Read more about how to customize your payment buttons in the [style section of the JavaScript SDK v5 reference](/sdk/js/v5/reference#style).
>
> ### Integrate 3D Secure using JavaScript SDK [#integrate-3d-secure-using-javascript-sdk-1]
>
> To trigger 3D Secure authentication, pass the verification method in the Create order payload. The verification method can be a `contingencies` parameter with `SCA_ALWAYS` or `SCA_WHEN_REQUIRED`:
>
> * Pass `SCA_ALWAYS` to trigger an authentication for every transaction.
> * Pass `SCA_WHEN_REQUIRED` to trigger an authentication only when required by a regional compliance mandate such as PSD2. 3D Secure is supported only in countries with a PSD2 compliance mandate.
>
> > **Warning:** These are the 3D Secure instructions for the JavaScript SDK component CardFields. If your integration uses the HostedFields component, see [Integrate 3D Secure using Hosted Fields](https://developer.paypal.com/docs/checkout/advanced/sdk/v1/) instead.

## 2. Integrate back end [#2-integrate-back-end]

The PayPal Server SDK provides integration access to the PayPal REST APIs. The API endpoints are divided into distinct controllers:

* Orders Controller: [Orders API v2](/api/orders/v2)
* Payments Controller: [Payments API v2](/api/payments/v2)

Your app creates an order on the backend by calling the [`ordersCreate`](/api/orders/v2/orders-create) method in the Orders Controller.

When the payer confirms the order, your app calls the [`ordersCapture`](/api/orders/v2/orders-capture) method in the Orders Controller on the backend to move the money.

### Set up your server [#set-up-your-server]

The sample integration uses the PayPal Server SDK to connect to the PayPal REST APIs. Use the `server` folder to set up the backend to integrate with the payments flow.

* The server side code runs on port `8080`
* Declare the `PAYPAL_CLIENT_ID` and `PAYPAL_CLIENT_SECRET` as environment variables. The server side code is configured to fetch these values from the environment to authorize the calls to the PayPal REST APIs.
* By default, the server SDK clients are configured to connect to PayPal's sandbox API.

#### 1. Generate access token [#1-generate-access-token]

Initialize the Server SDK client using OAuth 2.0 Client Credentials (`PAYPAL_CLIENT_ID` and `PAYPAL_CLIENT_SECRET`). The SDK automatically retrieves the OAuth token when you call an endpoint that requires OAuth 2.0 Client Credentials.

#### 2. Create an order [#2-create-an-order]

Endpoint: `POST` [`/v2/checkout/orders`](/api/orders/v2/orders-create)

You need a `createOrder` function to start a payment between a payer and a merchant.

Set up the `createOrder` function to make a request to the `ordersCreate` method in the Orders Controller and pass data from the cart object to calculate the purchase units for the order.

> **Note:** If you process payments that require [Strong Customer Authentication](/reference/guidelines/psd2-compliance/sca), you need to provide additional context with payment indicators.

#### 3. Capture payment [#3-capture-payment]

Endpoint: `POST` [`/v2/checkout/orders/{orderID}/capture`](/api/orders/v2/orders-capture)

You need a `captureOrder` function to move money from the payer to the merchant.

Set up the `captureOrder` function to make a request to the `ordersCapture` method in the Orders Controller and pass the `orderID` generated from the Create Order step.

### Review the full server code sample [#review-the-full-server-code-sample]

This server-side code sample shows how to set up your backend to generate an access token, create an order, and capture a payment using the [PayPal Server SDK](/serversdk/java/getting-started/how-to-get-started).

node.js

Java

PHP

.NET

Ruby

Python

```javascript lineNumbers 
import express from "express";
import "dotenv/config";
import {
    ApiError,
    Client,
    Environment,
    LogLevel,
    OrdersController,
    PaymentsController,
} from "@paypal/paypal-server-sdk";
import bodyParser from "body-parser";

const app = express();
app.use(bodyParser.json());

const {
    PAYPAL_CLIENT_ID,
    PAYPAL_CLIENT_SECRET,
    PORT = 8080,
} = process.env;

const client = new Client({
    clientCredentialsAuthCredentials: {
        oAuthClientId: PAYPAL_CLIENT_ID,
        oAuthClientSecret: PAYPAL_CLIENT_SECRET,
    },
    timeout: 0,
    environment: Environment.Sandbox,
    logging: {
        logLevel: LogLevel.Info,
        logRequest: { logBody: true },
        logResponse: { logHeaders: true },
    },
});

const ordersController = new OrdersController(client);
const paymentsController = new PaymentsController(client);

/**
 * Create an order to start the transaction.
 * @see /api/orders/v2/orders-create
 */
const createOrder = async (cart) => {
   const payload = {
        body: {
            intent: "CAPTURE",
            purchaseUnits: [
                {
                    amount: {
                        currencyCode: "USD",
                        value: "100",
                    },
                },
            ],
        },
        prefer: "return=minimal",
    };

    try {
        const { body, ...httpResponse } = await ordersController.createOrder(
            payload
        );
        // Get more response info...
        // const { statusCode, headers } = httpResponse;
        return {
            jsonResponse: JSON.parse(body),
            httpStatusCode: httpResponse.statusCode,
        };
    } catch (error) {
        if (error instanceof ApiError) {
            // const { statusCode, headers } = error;
            throw new Error(error.message);
        }
        throw error;
    }
};

app.post("/api/orders", async (req, res) => {
    try {
        // use the cart information passed from the front-end to calculate the order amount details
        const { cart } = req.body;
        const { jsonResponse, httpStatusCode } = await createOrder(cart);
        res.status(httpStatusCode).json(jsonResponse);
    } catch (error) {
        console.error("Failed to create order:", error);
        res.status(500).json({ error: "Failed to create order." });
    }
});



/**
 * Capture payment for the created order to complete the transaction.
 * @see /api/orders/v2/orders-capture
 */
const captureOrder = async (orderID) => {
    const collect = {
        id: orderID,
        prefer: "return=minimal",
    };

    try {
        const { body, ...httpResponse } = await ordersController.captureOrder(
            collect
        );
        // Get more response info...
        // const { statusCode, headers } = httpResponse;
        return {
            jsonResponse: JSON.parse(body),
            httpStatusCode: httpResponse.statusCode,
        };
    } catch (error) {
        if (error instanceof ApiError) {
            // const { statusCode, headers } = error;
            throw new Error(error.message);
        }
        throw error;
    }
};

app.post("/api/orders/:orderID/capture", async (req, res) => {
    try {
        const { orderID } = req.params;
        const { jsonResponse, httpStatusCode } = await captureOrder(orderID);
        res.status(httpStatusCode).json(jsonResponse);
    } catch (error) {
        console.error("Failed to capture order:", error);
        res.status(500).json({ error: "Failed to capture order." });
    }
});


/**
 * Authorize payment for the created order to complete the transaction.
 * @see /api/orders/v2/orders-authorize
 */
const authorizeOrder = async (orderID) => {
    const collect = {
        id: orderID,
        prefer: "return=minimal",
    };

    try {
        const { body, ...httpResponse } = await ordersController.authorizeOrder(
            collect
        );
        // Get more response info...
        // const { statusCode, headers } = httpResponse;
        return {
            jsonResponse: JSON.parse(body),
            httpStatusCode: httpResponse.statusCode,
        };
    } catch (error) {
        if (error instanceof ApiError) {
            // const { statusCode, headers } = error;
            throw new Error(error.message);
        }
        throw error;
    }
};

// authorizeOrder route
app.post("/api/orders/:orderID/authorize", async (req, res) => {
    try {
        const { orderID } = req.params;
        const { jsonResponse, httpStatusCode } = await authorizeOrder(orderID);
        res.status(httpStatusCode).json(jsonResponse);
    } catch (error) {
        console.error("Failed to authorize order:", error);
        res.status(500).json({ error: "Failed to authorize order." });
    }
});

/**
 * Captures an authorized payment, by ID.
 * @see /api/payments/v2/authorizations-capture
 */
const captureAuthorize = async (authorizationId) => {
    const collect = {
        authorizationId: authorizationId,
        prefer: "return=minimal",
        body: {
            finalCapture: false,
        },
    };
    try {
        const { body, ...httpResponse } =
            await paymentsController.captureAuthorize(collect);
        // Get more response info...
        // const { statusCode, headers } = httpResponse.statusCode,
        return {
            jsonResponse: JSON.parse(body),
            httpStatusCode: httpResponse.statusCode,
        };
    } catch (error) {
        if (error instanceof ApiError) {
            // const { statusCode, headers } = error;
            throw new Error(error.message);
        }
        throw error;
    }
};

// captureAuthorize route
app.post("/orders/:authorizationId/captureAuthorize", async (req, res) => {
    try {
        const { authorizationId } = req.params;
        const { jsonResponse, httpStatusCode } = await captureAuthorize(
            authorizationId
        );
        res.status(httpStatusCode).json(jsonResponse);
    } catch (error) {
        console.error("Failed to capture authorize:", error);
        res.status(500).json({ error: "Failed to capture authorize." });
    }
});

app.listen(PORT, () => {
    console.log(`Node server listening at http://localhost:${PORT}/`);
});
```

```java lineNumbers 
package com.paypal.sample;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.paypal.sdk.Environment;
import com.paypal.sdk.PaypalServerSdkClient;
import com.paypal.sdk.authentication.ClientCredentialsAuthModel;
import com.paypal.sdk.controllers.OrdersController;
import com.paypal.sdk.controllers.PaymentsController;
import com.paypal.sdk.exceptions.ApiException;
import com.paypal.sdk.http.response.ApiResponse;
import com.paypal.sdk.logging.configuration.ApiLoggingConfiguration;
import com.paypal.sdk.logging.configuration.ApiRequestLoggingConfiguration;
import com.paypal.sdk.logging.configuration.ApiResponseLoggingConfiguration;
import com.paypal.sdk.models.*;
import org.slf4j.event.Level;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

@SpringBootApplication
public class SampleAppApplication {

  @Value("${PAYPAL_CLIENT_ID}")
  private String PAYPAL_CLIENT_ID;

  @Value("${PAYPAL_CLIENT_SECRET}")
  private String PAYPAL_CLIENT_SECRET;

  public static void main(String[] args) {
    SpringApplication.run(SampleAppApplication.class, args);
  }

  @Bean
  public PaypalServerSdkClient paypalClient() {
    return new PaypalServerSdkClient.Builder()
      .loggingConfig(builder -> builder
        .level(Level.DEBUG)
        .requestConfig(logConfigBuilder -> logConfigBuilder.body(true))
        .responseConfig(logConfigBuilder -> logConfigBuilder.headers(true)))
      .httpClientConfig(configBuilder -> configBuilder.timeout(0))
      .environment(Environment.SANDBOX)
      .clientCredentialsAuth(new ClientCredentialsAuthModel.Builder(
        PAYPAL_CLIENT_ID,
        PAYPAL_CLIENT_SECRET
      ).build())
      .build();
  }

  @Controller
  @RequestMapping("/")
  public class CheckoutController {

    private final ObjectMapper objectMapper;
    private final PaypalServerSdkClient client;

    public CheckoutController(ObjectMapper objectMapper, PaypalServerSdkClient client) {
      this.objectMapper = objectMapper;
      this.client = client;
    }

    /**
     * Create an order to start the transaction.
     * @see /api/orders/v2/orders-create
     */
    @PostMapping("/api/orders")
    public ResponseEntity<Order> createOrder(@RequestBody Map<String, Object> request) {
      try {
        String cart = objectMapper.writeValueAsString(request.get("cart"));
        Order response = createOrder(cart);
        return new ResponseEntity<>(response, HttpStatus.OK);
      } catch (Exception e) {
        e.printStackTrace();
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
      }
    }

    private Order createOrder(String cart) throws IOException, ApiException {
      CreateOrderInput createOrderInput = new CreateOrderInput.Builder(
        null,
        new OrderRequest.Builder(
          CheckoutPaymentIntent.fromString("CAPTURE"),
          Arrays.asList(
            new PurchaseUnitRequest.Builder(
              new AmountWithBreakdown.Builder("USD", "100").build()
            ).build()
          )
        ).build()
      ).build();
      OrdersController ordersController = client.getOrdersController();
      ApiResponse<Order> apiResponse = ordersController.createOrder(createOrderInput);
      return apiResponse.getResult();
    }

    /**
     * Capture payment for the created order to complete the transaction.
     * @see /api/orders/v2/orders-capture
     */
    @PostMapping("/api/orders/{orderID}/capture")
    public ResponseEntity<Order> captureOrder(@PathVariable String orderID) {
      try {
        Order response = captureOrder(orderID);
        return new ResponseEntity<>(response, HttpStatus.OK);
      } catch (Exception e) {
        e.printStackTrace();
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
      }
    }

    private Order captureOrder(String orderID) throws IOException, ApiException {
      CaptureOrderInput captureOrderInput = new CaptureOrderInput.Builder(orderID, null).build();
      OrdersController ordersController = client.getOrdersController();
      ApiResponse<Order> apiResponse = ordersController.captureOrder(captureOrderInput);
      return apiResponse.getResult();
    }

    /**
     * Authorize payment for the created order to complete the transaction.
     * @see /api/orders/v2/orders-authorize
     */
    @PostMapping("/api/orders/{orderID}/authorize")
    public ResponseEntity<Order> authorizeOrder(@PathVariable String orderID) {
      try {
        Order response = authorizeOrder(orderID);
        return new ResponseEntity<>(response, HttpStatus.OK);
      } catch (Exception e) {
        e.printStackTrace();
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
      }
    }

    private Order authorizeOrder(String orderID) throws IOException, ApiException {
      AuthorizeOrderInput authorizeOrderInput = new AuthorizeOrderInput.Builder(orderID, null).build();
      OrdersController ordersController = client.getOrdersController();
      ApiResponse<Order> apiResponse = ordersController.authorizeOrder(authorizeOrderInput);
      return apiResponse.getResult();
    }

    /**
     * Captures an authorized payment, by ID.
     * @see /api/payments/v2/authorizations-capture
     */
    @PostMapping("/orders/{authorizationId}/captureAuthorize")
    public ResponseEntity<Payment> captureAuthorize(@PathVariable String authorizationId) {
      try {
        Payment response = captureAuthorize(authorizationId);
        return new ResponseEntity<>(response, HttpStatus.OK);
      } catch (Exception e) {
        e.printStackTrace();
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
      }
    }

    private Payment captureAuthorize(String authorizationId) throws IOException, ApiException {
      CaptureAuthorizeInput captureAuthorizeInput = new CaptureAuthorizeInput.Builder(
        authorizationId,
        null,
        new AuthorizationsCaptureInput.Builder().finalCapture(false).build()
      ).build();
      PaymentsController paymentsController = client.getPaymentsController();
      ApiResponse<Payment> apiResponse = paymentsController.captureAuthorize(captureAuthorizeInput);
      return apiResponse.getResult();
    }

  }
}
```

```php lineNumbers 
<?php
require __DIR__ . "/../vendor/autoload.php";

use PaypalServerSdkLib\PaypalServerSdkClientBuilder;
use PaypalServerSdkLib\Authentication\ClientCredentialsAuthCredentialsBuilder;
use PaypalServerSdkLib\Logging\LoggingConfigurationBuilder;
use PaypalServerSdkLib\Logging\RequestLoggingConfigurationBuilder;
use PaypalServerSdkLib\Logging\ResponseLoggingConfigurationBuilder;
use Psr\Log\LogLevel;
use PaypalServerSdkLib\Models\Builders\OrderRequestBuilder;
use PaypalServerSdkLib\Models\CheckoutPaymentIntent;
use PaypalServerSdkLib\Models\Builders\PurchaseUnitRequestBuilder;
use PaypalServerSdkLib\Models\Builders\AmountWithBreakdownBuilder;
use PaypalServerSdkLib\Models\Builders\AuthorizationsCaptureInputBuilder;
use PaypalServerSdkLib\Environment;

$PAYPAL_CLIENT_ID = getenv("PAYPAL_CLIENT_ID");
$PAYPAL_CLIENT_SECRET = getenv("PAYPAL_CLIENT_SECRET");

$client = PaypalServerSdkClientBuilder::init()
    ->clientCredentialsAuthCredentials(
        ClientCredentialsAuthCredentialsBuilder::init(
            $PAYPAL_CLIENT_ID,
            $PAYPAL_CLIENT_SECRET
        )
    )
    ->environment(Environment::SANDBOX)
    ->loggingConfiguration(
        LoggingConfigurationBuilder::init()
            ->level(LogLevel::INFO)
            ->requestConfiguration(
                RequestLoggingConfigurationBuilder::init()->body(true)->build()
            )
            ->responseConfiguration(
                ResponseLoggingConfigurationBuilder::init()->headers(true)->build()
            )
            ->build()
    )
    ->build();

function handleResponse($response)
{
    $jsonResponse = json_decode($response->getBody(), true);
    return [
        "jsonResponse" => $jsonResponse,
        "httpStatusCode" => $response->getStatusCode(),
    ];
}

$endpoint = $_SERVER["REQUEST_URI"];

if ($endpoint === "/") {
    try {
        header("Content-Type: application/json");
        echo json_encode(["message" => "Server is running"]);
    } catch (Exception $e) {
        echo json_encode(["error" => $e->getMessage()]);
        http_response_code(500);
    }
}

/**
 * Create an order to start the transaction.
 * @see /api/orders/v2/orders-create
 */
function createOrder($cart)
{
    global $client;

    $orderBody = [
        "body" => OrderRequestBuilder::init("CAPTURE", [
            PurchaseUnitRequestBuilder::init(
                AmountWithBreakdownBuilder::init("USD", "100")->build()
            )->build(),
        ])->build(),
    ];

    $apiResponse = $client->getOrdersController()->createOrder($orderBody);
    return handleResponse($apiResponse);
}

if ($endpoint === "/api/orders") {
    $data = json_decode(file_get_contents("php://input"), true);
    $cart = $data["cart"];
    header("Content-Type: application/json");
    try {
        $orderResponse = createOrder($cart);
        echo json_encode($orderResponse["jsonResponse"]);
    } catch (Exception $e) {
        echo json_encode(["error" => $e->getMessage()]);
        http_response_code(500);
    }
}

/**
 * Capture payment for the created order to complete the transaction.
 * @see /api/orders/v2/orders-capture
 */
function captureOrder($orderID)
{
    global $client;

    $captureBody = ["id" => $orderID];
    $apiResponse = $client->getOrdersController()->captureOrder($captureBody);
    return handleResponse($apiResponse);
}

if (preg_match('/\/api\/orders\/(.+)\/capture$/', $endpoint, $matches)) {
    $orderID = $matches[1];
    header("Content-Type: application/json");
    try {
        $captureResponse = captureOrder($orderID);
        echo json_encode($captureResponse["jsonResponse"]);
    } catch (Exception $e) {
        echo json_encode(["error" => $e->getMessage()]);
        http_response_code(500);
    }
}

/**
 * Authorize payment for the created order to complete the transaction.
 * @see /api/orders/v2/orders-authorize
 */
function authorizeOrder($orderID)
{
    global $client;

    $authorizeBody = ["id" => $orderID];
    $apiResponse = $client->getOrdersController()->authorizeOrder($authorizeBody);
    return handleResponse($apiResponse);
}

if (preg_match('/\/api\/orders\/(.+)\/authorize$/', $endpoint, $matches)) {
    $orderID = $matches[1];
    header("Content-Type: application/json");
    try {
        $authorizeResponse = authorizeOrder($orderID);
        echo json_encode($authorizeResponse["jsonResponse"]);
    } catch (Exception $e) {
        echo json_encode(["error" => $e->getMessage()]);
        http_response_code(500);
    }
}

/**
 * Captures an authorized payment, by ID.
 * @see /api/payments/v2/authorizations-capture
 */
function captureAuthorize($authorizationId)
{
    global $client;

    $captureBody = [
        "authorizationId" => $authorizationId,
        "body" => ["finalCapture" => false],
    ];
    $apiResponse = $client->getPaymentsController()->captureAuthorize($captureBody);
    return handleResponse($apiResponse);
}

if (preg_match('/\/orders\/(.+)\/captureAuthorize$/', $endpoint, $matches)) {
    $authorizationId = $matches[1];
    header("Content-Type: application/json");
    try {
        $captureAuthorizeResponse = captureAuthorize($authorizationId);
        echo json_encode($captureAuthorizeResponse["jsonResponse"]);
    } catch (Exception $e) {
        echo json_encode(["error" => $e->getMessage()]);
        http_response_code(500);
    }
}
```

```csharp lineNumbers 
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using PaypalServerSdk.Standard;
using PaypalServerSdk.Standard.Authentication;
using PaypalServerSdk.Standard.Controllers;
using PaypalServerSdk.Standard.Http.Response;
using PaypalServerSdk.Standard.Models;
using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration;

namespace PayPalAdvancedIntegration;

public class Program {
  public static void Main(string[] args) {
    CreateHostBuilder(args).Build().Run();
  }

  public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder => {
      webBuilder.UseUrls("http://localhost:8080");
      webBuilder.UseStartup<Startup>();
    });
}

public class Startup {
  public void ConfigureServices(IServiceCollection services) {
    services.AddMvc().AddNewtonsoftJson();
    services.AddHttpClient();
  }

  public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    if (env.IsDevelopment()) {
      app.UseDeveloperExceptionPage();
    }
    app.UseRouting();
    app.UseStaticFiles();
    app.UseEndpoints(endpoints => {
      endpoints.MapControllers();
    });
  }
}

[ApiController]
public class CheckoutController : Controller {
  private readonly OrdersController _ordersController;
  private readonly PaymentsController _paymentsController;
  private IConfiguration _configuration { get; }

  private string _paypalClientId {
    get { return System.Environment.GetEnvironmentVariable("PAYPAL_CLIENT_ID"); }
  }
  private string _paypalClientSecret {
    get { return System.Environment.GetEnvironmentVariable("PAYPAL_CLIENT_SECRET"); }
  }

  private readonly ILogger<CheckoutController> _logger;

  public CheckoutController(IConfiguration configuration, ILogger<CheckoutController> logger) {
    _configuration = configuration;
    _logger = logger;

    // Initialize the PayPal SDK client
    PaypalServerSdkClient client = new PaypalServerSdkClient.Builder()
      .Environment(PaypalServerSdk.Standard.Environment.Sandbox)
      .ClientCredentialsAuth(
        new ClientCredentialsAuthModel.Builder(_paypalClientId, _paypalClientSecret).Build()
      )
      .LoggingConfig(config =>
        config
        .LogLevel(LogLevel.Information)
        .RequestConfig(reqConfig => reqConfig.Body(true))
        .ResponseConfig(respConfig => respConfig.Headers(true))
      )
      .Build();

    _ordersController = client.OrdersController;
    _paymentsController = client.PaymentsController;
  }

  /// <summary>
  /// Create an order to start the transaction.
  /// /api/orders/v2/orders-create
  /// </summary>
  [HttpPost("api/orders")]
  public async Task<IActionResult> CreateOrder([FromBody] dynamic cart) {
    try {
      var result = await _CreateOrder(cart);
      return StatusCode((int) result.StatusCode, result.Data);
    } catch (Exception ex) {
      Console.Error.WriteLine("Failed to create order:", ex);
      return StatusCode(500, new { error = "Failed to create order." });
    }
  }

  private async Task<dynamic> _CreateOrder(dynamic cart) {
    CreateOrderInput createOrderInput = new CreateOrderInput {
      Body = new OrderRequest {
        Intent = CheckoutPaymentIntent.Capture,
        PurchaseUnits = new List<PurchaseUnitRequest> {
          new PurchaseUnitRequest {
            Amount = new AmountWithBreakdown {
              CurrencyCode = "USD",
              MValue = "100",
            },
          },
        },
      },
    };

    ApiResponse<Order> result = await _ordersController.CreateOrderAsync(createOrderInput);
    return result;
  }

  /// <summary>
  /// Capture payment for the created order to complete the transaction.
  /// /api/orders/v2/orders-capture
  /// </summary>
  [HttpPost("api/orders/{orderID}/capture")]
  public async Task<IActionResult> CaptureOrder(string orderID) {
    try {
      var result = await _CaptureOrder(orderID);
      return StatusCode((int) result.StatusCode, result.Data);
    } catch (Exception ex) {
      Console.Error.WriteLine("Failed to capture order:", ex);
      return StatusCode(500, new { error = "Failed to capture order." });
    }
  }

  private async Task<dynamic> _CaptureOrder(string orderID) {
    CaptureOrderInput captureOrderInput = new CaptureOrderInput { Id = orderID };
    ApiResponse<Order> result = await _ordersController.CaptureOrderAsync(captureOrderInput);
    return result;
  }

  /// <summary>
  /// Authorize payment for the created order to complete the transaction.
  /// /api/orders/v2/orders-authorize
  /// </summary>
  [HttpPost("api/orders/{orderID}/authorize")]
  public async Task<IActionResult> AuthorizeOrder(string orderID) {
    try {
      var result = await _AuthorizeOrder(orderID);
      return StatusCode((int) result.StatusCode, result.Data);
    } catch (Exception ex) {
      Console.Error.WriteLine("Failed to authorize order:", ex);
      return StatusCode(500, new { error = "Failed to authorize order." });
    }
  }

  private async Task<dynamic> _AuthorizeOrder(string orderID) {
    AuthorizeOrderInput authorizeOrderInput = new AuthorizeOrderInput { Id = orderID };
    ApiResponse<Order> result = await _ordersController.AuthorizeOrderAsync(authorizeOrderInput);
    return result;
  }

  /// <summary>
  /// Captures an authorized payment, by ID.
  /// /api/payments/v2/authorizations-capture
  /// </summary>
  [HttpPost("orders/{authorizationId}/captureAuthorize")]
  public async Task<IActionResult> CaptureAuthorize(string authorizationId) {
    try {
      var result = await _CaptureAuthorize(authorizationId);
      return StatusCode((int) result.StatusCode, result.Data);
    } catch (Exception ex) {
      Console.Error.WriteLine("Failed to capture authorize:", ex);
      return StatusCode(500, new { error = "Failed to capture authorize." });
    }
  }

  private async Task<dynamic> _CaptureAuthorize(string authorizationId) {
    CaptureAuthorizeInput captureAuthorizeInput = new CaptureAuthorizeInput {
      AuthorizationId = authorizationId,
      Body = new AuthorizationsCapture { FinalCapture = false },
    };
    ApiResponse<CapturedPayment> result = await _paymentsController.CaptureAuthorizeAsync(captureAuthorizeInput);
    return result;
  }
}
```

```ruby lineNumbers 
require 'paypal_server_sdk'
require 'sinatra'
require 'sinatra/json'

include PaypalServerSdk

set :port, 8080


paypal_client = PaypalServerSdk::Client.new(
  client_credentials_auth_credentials: ClientCredentialsAuthCredentials.new(
    o_auth_client_id: ENV['PAYPAL_CLIENT_ID'],
    o_auth_client_secret: ENV['PAYPAL_CLIENT_SECRET']
  ),
  environment: Environment::SANDBOX,
  logging_configuration: LoggingConfiguration.new(
    mask_sensitive_headers: false,
    log_level: Logger::INFO,
    request_logging_config: RequestLoggingConfiguration.new(
      log_headers: true,
      log_body: true,
    ),
    response_logging_config: ResponseLoggingConfiguration.new(
      log_headers: true,
      log_body: true
    )
  )
)
# Health Check
get '/' do
  json :message => "Server is running"
end


# Create an order to start the transaction.
#
# @see /api/orders/v2/orders-create
post "/api/orders" do
  # use the cart information passed from the front-end to calculate the order amount details
  cart = JSON.parse request.body.read
  order_response = paypal_client.orders.create_order({

  'body' => OrderRequest.new(
    intent: CheckoutPaymentIntent::CAPTURE,
    purchase_units: [
      PurchaseUnitRequest.new(
        amount: AmountWithBreakdown.new(
          currency_code: 'CAD',
          value: '100',
          breakdown: AmountBreakdown.new(
            item_total: Money.new(
              currency_code: 'CAD',
              value: '100'
            )
          )
        ),
        # lookup item details in `cart` from database
        items: [
          Item.new(
            name: 'T-Shirt',
            unit_amount: Money.new(
              currency_code: 'CAD',
              value: '100'
            ),
            quantity: '1',
            sku: 'sku01',
            category: ItemCategory::PHYSICAL_GOODS
          )
        ],

      )
    ],


  )
  })
  json order_response.data
end

# Capture payment for the created order to complete the transaction.
#
# @see /api/orders/v2/orders-capture
post '/api/orders/:order_id/capture' do | order_id |
  capture_response = paypal_client.orders.capture_order({
    'id' => order_id,
    'prefer' => 'return=representation'
  })

  json capture_response.data
  rescue ErrorException => e
end
```

```python lineNumbers 
import logging
import os

from flask import Flask, request, Response
from paypalserversdk.http.auth.o_auth_2 import ClientCredentialsAuthCredentials
from paypalserversdk.logging.configuration.api_logging_configuration import (
    LoggingConfiguration,
    RequestLoggingConfiguration,
    ResponseLoggingConfiguration,
)
from paypalserversdk.paypal_serversdk_client import PaypalServersdkClient
from paypalserversdk.controllers.orders_controller import OrdersController
from paypalserversdk.controllers.payments_controller import PaymentsController
from paypalserversdk.models.amount_with_breakdown import AmountWithBreakdown
from paypalserversdk.models.checkout_payment_intent import CheckoutPaymentIntent
from paypalserversdk.models.order_request import OrderRequest
from paypalserversdk.models.purchase_unit_request import PurchaseUnitRequest
from paypalserversdk.models.authorizations_capture import AuthorizationsCapture
from paypalserversdk.exceptions.error_exception import ErrorException
from paypalserversdk.api_helper import ApiHelper

app = Flask(__name__)

paypal_client: PaypalServersdkClient = PaypalServersdkClient(
    client_credentials_auth_credentials=ClientCredentialsAuthCredentials(
        o_auth_client_id=os.getenv("PAYPAL_CLIENT_ID"),
        o_auth_client_secret=os.getenv("PAYPAL_CLIENT_SECRET"),
    ),
    logging_configuration=LoggingConfiguration(
        log_level=logging.INFO,
        # Disable masking of sensitive headers for Sandbox testing.
        # This should be set to True (the default if unset) in production.
        mask_sensitive_headers=False,
        request_logging_config=RequestLoggingConfiguration(
            log_headers=True, log_body=True
        ),
        response_logging_config=ResponseLoggingConfiguration(
            log_headers=True, log_body=True
        ),
    ),
)

"""
Health check
"""
@app.route("/", methods=["GET"])
def index():
    return {"message": "Server is running"}

orders_controller: OrdersController = paypal_client.orders
payments_controller: PaymentsController = paypal_client.payments


"""
Create an order to start the transaction.

@see /api/orders/v2/orders-create
"""
@app.route("/api/orders", methods=["POST"])
def create_order():
    request_body = request.get_json()
    # use the cart information passed from the front-end to calculate the order amount details
    cart = request_body["cart"]
    order = orders_controller.create_order(
        {
            "body": OrderRequest(
                intent=CheckoutPaymentIntent.CAPTURE,
                purchase_units=[
                    PurchaseUnitRequest(
                        amount=AmountWithBreakdown(
                            currency_code="USD",
                            value="100",
                        ),
                    )
                ],
            )
        }
    )
    return Response(
        ApiHelper.json_serialize(order.body), status=200, mimetype="application/json"
    )


"""
Capture payment for the created order to complete the transaction.

@see /api/orders/v2/orders-capture
"""
@app.route("/api/orders/<order_id>/capture", methods=["POST"])
def capture_order(order_id):
    order = orders_controller.capture_order(
        {"id": order_id, "prefer": "return=minimal"}
    )
    return Response(
        ApiHelper.json_serialize(order.body), status=200, mimetype="application/json"
    )


"""
Authorize payment for the created order to complete the transaction.

@see /api/orders/v2/orders-authorize
"""
@app.route("/api/orders/<order_id>/authorize", methods=["POST"])
def authorize_order(order_id):
    order = orders_controller.authorize_order(
        {"id": order_id, "prefer": "return=minimal"}
    )
    return Response(
        ApiHelper.json_serialize(order.body), status=200, mimetype="application/json"
    )


"""
Captures an authorized payment, by ID.

@see /api/payments/v2/authorizations-capture
"""
@app.route("/orders/<authorization_id>/captureAuthorize", methods=["POST"])
def capture_authorize(authorization_id):
    payment = payments_controller.capture_authorize(
        {
            "authorizationId": authorization_id,
            "prefer": "return=minimal",
            "body": AuthorizationsCapture(final_capture=False),
        }
    )
    return Response(
        ApiHelper.json_serialize(payment.body), status=200, mimetype="application/json"
    )
```

## 3. Test the integration [#3-test-the-integration]

Before going live, test your integration in the [sandbox environment](/sandbox-testing/overview/). Learn more about [card testing](/sandbox-testing/card-testing/).

> **Note:** [Use the credit card generator to generate test credit card numbers for sandbox testing](/sandbox-testing/card-testing/#credit-card-generator).

Test the following use cases before going live:

### Test a PayPal payment [#test-a-paypal-payment]

Test a purchase as a payer:

1. Select the PayPal button on your checkout page.
2. Log in using a personal sandbox account. This ensures payments go to the correct account. Make sure that you use the sandbox business account that corresponds to the REST app you are using.
3. Note the purchase amount in the PayPal checkout window and approve the purchase with the Pay Now button. The PayPal window closes and redirects you to your page, indicating that the transaction was completed.

Confirm the money reached the business account:

1. Log into the PayPal sandbox using the sandbox business account that received the payment. Remember that the SDK source now uses a sandbox client ID from one of your REST apps, and not the default test ID.
2. In **Recent Activity**, confirm that the sandbox business account received the money, subtracting any fees.
3. Log out of the account.

### Test a card payment [#test-a-card-payment]

1. Go to the checkout page for your integration.
2. Generate a test card using the credit card generator.
3. Enter the card details in the card fields, including the name on the card, billing address, and 2-character country code. Then, submit the order.
4. Confirm that the order was processed.
5. Log into your merchant sandbox account and navigate to the activity page to ensure the payment amount shows up in the account.

## Next steps [#next-steps]

* [PayPal Expanded Checkout best practices](/expanded/best-practices)
* [Go live](/api/rest/production)
