Understand the server.js code
sample.
Declare imports
This section of code sets up the port to run your server and starts the express Node.js web application framework. It also retreives variables and sets the base sandbox URL.
1const { PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PORT = 8888 } = process.env;2const base = "https://api-m.sandbox.paypal.com";3const app = express();
- Line 1 uses the DotEnv library to import and declare the client ID and client secret from your .env file.
- Line 2 declares the base URL for PayPal's sandbox API.
Additional functions
This section of the code defines a file directory for static files and calls the express.json function to parse JSON response bodies.
1// host static files2app.use(express.static("client"));3// parse post params sent in body in json format4app.use(express.json());
Generate access token
You need an access token to authenticate all REST API requests. The following code sample makes a POST call to the /v1/oauth2/
token endpoint to create an access token.
1/**2* Generate an OAuth 2.0 access token for authenticating with PayPal REST APIs.3* @see https://developer.paypal.com/api/rest/authentication/4*/5const generateAccessToken = async () => {6 try {7 if (!PAYPAL_CLIENT_ID || !PAYPAL_CLIENT_SECRET) {8 throw new Error("MISSING_API_CREDENTIALS");9 }10 const auth = Buffer.from(11 PAYPAL_CLIENT_ID + ":" + PAYPAL_CLIENT_SECRET,12 ).toString("base64");13 const response = await fetch(`${base}/v1/oauth2/token`, {14 method: "POST",15 body: "grant_type=client_credentials",16 headers: {17 Authorization: `Basic ${auth}`,18 },19 });20 const data = await response.json();21 return data.access_token;22 } catch (error) {23 console.error("Failed to generate Access Token:", error);24 }25};
- Line 10 combines the
PAYPAL_CLIENT_ID
andPAYPAL_CLIENT_SECRET
as a key-value pair. - Lines 13-18 define a response that makes a
POST
call to the/v1/oauth2/token
API endpoint to generate an access token. - Lines 20-21 establish a listener to capture the response data from the request and return the
access_token
.
Create order
Create an order to start a payment between a payer and a seller by making a POST
request to /v2/checkout/orders
.
Important:If you process payments that require Strong Customer Authentication, you need to provide additional context with payment indicators
1/**2* Create an order to start the transaction.3* @see https://developer.paypal.com/docs/api/orders/v2/#orders_create4*/5const createOrder = async (cart) => {6 // use the cart information passed from the front-end to calculate the purchase unit details7 console.log(8 "shopping cart information passed from the frontend createOrder() callback:",9 cart,10 );11 const accessToken = await generateAccessToken();12 const url = `${base}/v2/checkout/orders`;13 const payload = {14 intent: "CAPTURE",15 purchase_units: [16 {17 amount: {18 currency_code: "USD",19 value: "100.00",20 },21 },22 ],23 };24 const response = await fetch(url, {25 headers: {26 "Content-Type": "application/json",27 Authorization: `Bearer ${accessToken}`,28 // Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:29 // https://developer.paypal.com/tools/sandbox/negative-testing/request-headers/30 // "PayPal-Mock-Response": '{"mock_application_codes": "MISSING_REQUIRED_PARAMETER"}'31 // "PayPal-Mock-Response": '{"mock_application_codes": "PERMISSION_DENIED"}'32 // "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'33 },34 method: "POST",35 body: JSON.stringify(payload),36 });37 return handleResponse(response);38};
- Line 5 calls the
createOrder
function and uses the cart information from the front-end to calculate the purchase units for the order. - Line 12 establishes a listener to capture the
accessToken
from thegenerateAccessToken()
function later in the API call. - Lines 13-38 create an order by sending a
POST
request to the Orders v2 API, using theaccessToken
.
Note:See the Create Order endpoint of the PayPal Orders v2 API for sample responses and other details.
Payment processors return processor response codes when they receive a transaction request. For advanced card payments, the code displays in the authorization object under the response_code
field.
1"processor_response": {2 "avs_code": "Y",3 "cvv_code": "S",4 "payment_advice_code": "",5 "response_code": "0000"6}
If an external payment processor declines a transaction, PayPal returns a HTTP 201 CREATED
status code and a status of DECLINED
in the capture status.
See the Orders API response_code
object to get the processor response code for the non-PayPal payment processor errors.
Capture payment
Capture an order to move money from the payer to the merchant by making a POST
call to the /v2/checkout/orders/ORDER-ID/capture
endpoint.
1/**2 * Capture payment for the created order to complete the transaction.3 * @see https://developer.paypal.com/docs/api/orders/v2/#orders_capture4 */5const captureOrder = async (orderID) => {6 const accessToken = await generateAccessToken();7 const url = `${base}/v2/checkout/orders/${orderID}/capture`;89 const response = await fetch(url, {10 method: "POST",11 headers: {12 "Content-Type": "application/json",13 Authorization: `Bearer ${accessToken}`,14 // Uncomment one of these to force an error for negative testing (in sandbox mode only). Documentation:15 // https://developer.paypal.com/tools/sandbox/negative-testing/request-headers/16 // "PayPal-Mock-Response": '{"mock_application_codes": "INSTRUMENT_DECLINED"}'17 // "PayPal-Mock-Response": '{"mock_application_codes": "TRANSACTION_REFUSED"}'18 // "PayPal-Mock-Response": '{"mock_application_codes": "INTERNAL_SERVER_ERROR"}'19 },20 });
- Line 5 establishes a listener to capture the
accessToken
from thegenerateAccessToken()
function later in the API call. - Line 7 declares the URL of the Capture API endpoint using the Order ID generated from the Create Order endpoint.
- Lines 9-13 define a response that makes a
POST
call to the/v2/checkout/orders/ORDER-ID/capture
endpoint to capture the order, using theaccessToken
. - Lines 16-18 include mock responses for negative testing in the sandbox.
Sample capture response
This code sample shows a response to a POST
call to /v2/checkout/orders/ORDER-ID/capture
. The response is the orderData
retrieved in the create order section.
1{2 "id": "5O190127TN364715T",3 "status": "COMPLETED",4 "payment_source": {5 "paypal": {6 "name": {7 "given_name": "Firstname",8 "surname": "Lastname"9 },10 "email_address": "payer@example.com",11 "account_id": "QYR5Z8XDVJNXQ",12 },13 },14 "purchase_units": [15 {16 "reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",17 "shipping": {18 "address": {19 "address_line_1": "123 Main St.",20 "admin_area_2": "Anytown",21 "admin_area_1": "CA",22 "postal_code": "12345",23 "country_code": "US",24 },25 },26 "payments": {27 "captures": [28 {29 "id": "3C679366HH908993F",30 "status": "COMPLETED",31 "amount": {32 "currency_code": "USD",33 "value": "100.00",34 },35 "seller_protection": {36 "status": "ELIGIBLE",37 "dispute_categories": [38 "ITEM_NOT_RECEIVED",39 "UNAUTHORIZED_TRANSACTION",40 ],41 },42 "final_capture": true,43 "disbursement_mode": "INSTANT",44 "seller_receivable_breakdown": {45 "gross_amount": {46 "currency_code": "USD",47 "value": "100.00",48 },49 "paypal_fee": {50 "currency_code": "USD",51 "value": "3.00",52 },53 "net_amount": {54 "currency_code": "USD",55 "value": "97.00",56 },57 },58 "create_time": "2018-04-01T21:20:49Z",59 "update_time": "2018-04-01T21:20:49Z",60 "links": [61 {62 "href": "https://api-m.paypal.com/v2/payments/captures/3C679366HH908993F",63 "rel": "self",64 "method": "GET",65 },66 {67 "href": "https://api-m.paypal.com/v2/payments/captures/3C679366HH908993F/refund",68 "rel": "refund",69 "method": "POST",70 },71 ],72 },73 ],74 },75 },76 ],77 "payer": {78 "name": {79 "given_name": "Firstname",80 "surname": "Lastname",81 },82 "email_address": "payer@example.com",83 "payer_id": "QYR5Z8XDVJNXQ",84 },85 "links": [86 {87 "href": "https://api-m.paypal.com/v2/checkout/orders/5O190127TN364715T",88 "rel": "self",89 "method": "GET",90 },91 ],92}
- Line 2 shows the ID for this
orderData
object. - Lines 4-13 pass details about the payment source.
- Lines 14-76 pass the
purchase_units
in this transaction. Each purchase unit represents either a full or partial order and establishes a contract between a payer and a merchant. - Line 16 passes the
reference_id
that identifies the purchase unit in this payment response. - Lines 17-24 pass details about the shipping address.
- Line 26 declares the payments object that passes the payment details for this capture request.
- Line 27 declares the captures object that passes details about the captured payments for this request.
- Lines 28-57 pass the payment capture details, such as the capture identifier
id
,amount
,disbursement_mode
, andnet_amount
. - Lines 60-71 pass the HATEOAS details of the capture response. See the REST API Response Reference for more details about HATEOAS.
- Lines 77-84 pass details about the payer.
- Lines 85-91 pass the HATEOAS details for the orders response.
Note:Visit the Capture Payment endpoint of the PayPal Orders v2 API to see sample responses and other details.
Handle responses
The handleResponse
function sets up a listener for API responses.
1async function handleResponse(response) {2 try {3 const jsonResponse = await response.json();4 return {5 jsonResponse,6 httpStatusCode: response.status,7 };8 } catch (err) {9 const errorMessage = await response.text();10 throw new Error(errorMessage);11 }12 }1314 app.post("/api/orders", async (req, res) => {15 try {16 // use the cart information passed from the front-end to calculate the order amount detals17 const { cart } = req.body;18 const { jsonResponse, httpStatusCode } = await createOrder(cart);19 res.status(httpStatusCode).json(jsonResponse);20 } catch (error) {21 console.error("Failed to create order:", error);22 res.status(500).json({ error: "Failed to create order." });23 }24 });2526 app.post("/api/orders/:orderID/capture", async (req, res) => {27 try {28 const { orderID } = req.params;29 const { jsonResponse, httpStatusCode } = await captureOrder(orderID);30 res.status(httpStatusCode).json(jsonResponse);31 } catch (error) {32 console.error("Failed to create order:", error);33 res.status(500).json({ error: "Failed to capture order." });34 }35 });
- Line 1 creates a function which returns a HTTP status code response. Error status codes send an error message.
- Line 14 makes a
POST
call to theapi/orders/
endpoint and returns an HTTP status code response. Errors status codes send an error message. - Line 26 makes a
POST
call to theapi/orders/:orderID/capture
endpoint and returns an HTTP status code response for the particular order. Error status codes send an error message.