This breakdown focuses on key lines from the /public/app.js
code sample.
PayPal Button JavaScript
This section calls the JavaScript SDK that defines the PayPal buttons
1paypal2 .Buttons({3 {...}4 })5 .render("#paypal-button-container");
Create order for PayPal Button
This code sample defines the createOrder()
function. Add this code to the PayPal button in /public/app.js
.
3// Sets up the transaction when a payment button is selected4createOrder: function () {5 return fetch("/api/orders", {6 method: "post",7 // use the "body" param to optionally pass additional order information8 // like product skus and quantities9 body: JSON.stringify({10 cart: [11 {12 sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",13 quantity: "YOUR_PRODUCT_QUANTITY",14 },15 ],16 }),17 })18 .then((response) => response.json())19 .then((order) => order.id);20},
- Line 5 creates the order by calling the
/api/orders
endpoint on your server. - Lines 10-15 pass the SKU and quantity for the product in the cart. For more information about creating orders, see the
createOrder
section of the JavaScript SDK reference guide.
Capture payment when approved
This code sample defines a POST
call to /api/orders/{orderID}/capture
. Add this code to the PayPal button in /public/app.js
.
22onApprove: function (data) {23 return fetch(`/api/orders/${data.orderID}/capture`, {24 method: "post",25 })26 .then((response) => response.json())27 .then((orderData) => {28 // Successful capture! For dev/demo purposes:29 console.log(30 "Capture result",31 orderData,32 JSON.stringify(orderData, null, 2)33 );34 const transaction = orderData.purchase_units[0].payments.captures[0];35 alert(`Transaction ${transaction.status}: ${transaction.id}3637 See console for all available details38 `);39 // When ready to go live, remove the alert and show a success message within this page. For example:40 // var element = document.getElementById('paypal-button-container');41 // element.innerHTML = '<h3>Thank you for your payment!</h3>';42 // Or go to another URL: actions.redirect('thank_you.html');43 });44},
- Line 23 calls your server's capture endpoint to capture the order.
- Lines 28-42 declare what happens when the payment is successfully captured. When you're ready to go live, you can remove the alert and either show a success message or redirect the payer to another URL.
Check for CardFields eligibility
Hosted card fields use the JavaScript SDK to help non-PCI-compliant sellers save a payer's card data and create a token for future payments. During an eligible payment, the hosted fields show up in the payment experience and prompt the payer for consent to save their card data. For more information about hosted fields, see Hosted fields.
This code sample checks to see if a payment is eligible for hosted card fields. If not, the payment experience won't show the hosted card fields. Add this section of code to the PayPal button in /public/app.js
.
48// If this returns false or the card fields aren't visible, see Step #1.49if (paypal.CardFields.isEligible()) {50 let orderId;51 {...}52} else {53 // Hides card fields if the merchant isn't eligible54 document.querySelector("#card-form").style = "display: none";55}
You can modify this code to show a custom message for eligible or ineligible payments.
Render hosted card fields and create order
This code sample renders the hosted card fields for an eligible payment. Add this section of code to the PayPal button in /public/app.js
.
52// Renders card fields53paypal.CardFields.render({54 // Call your server to set up the transaction55 createOrder: () => {56 return fetch("/api/orders", {57 method: "post",58 // use the "body" param to optionally pass additional order information59 // like product skus and quantities60 body: JSON.stringify({61 cart: [62 {63 sku: "YOUR_PRODUCT_STOCK_KEEPING_UNIT",64 quantity: "YOUR_PRODUCT_QUANTITY",65 },66 ],67 }),68 })69 .then((res) => res.json())70 .then((orderData) => {71 orderId = orderData.id;72 // needed later to complete capture73 return orderData.id;74 });75 },76 styles: {77 ".valid": {78 color: "green",79 },80 ".invalid": {81 color: "red",82 },83 },84 fields: {85 number: {86 selector: "#card-number",87 placeholder: "4111 1111 1111 1111",88 },89 cvv: {90 selector: "#cvv",91 placeholder: "123",92 },93 expirationDate: {94 selector: "#expiration-date",95 placeholder: "MM/YY",96 },97 },98})
- Line 56 creates the order by calling the
/api/orders
endpoint on your server. - Lines 63-64 pass the SKU and quantity for the product in the cart.
- Lines 71-72 get the order identifier
id
from theorderData
object returned. - Lines 75-82 define styles for the hosted card fields. You can change these styles as needed for your implementation.
- Lines 83-95 define the selector values and placeholders for the input fields. You can edit this section as needed for your implementation, such as adding more fields. For more information about optional configuration, see Options in the JavaScript SDK reference.
Capture eligible payment
This code sample captures the payment when it is eligible for hosted card fields. Add this code to the PayPal button in /public/app.js
, right after rendering the card fields.
97.then((cardFields) => {98 document.querySelector("#card-form").addEventListener("submit", (event) => {99 event.preventDefault();100 cardFields101 .submit({102 // Cardholder's first and last name103 cardholderName: document.getElementById("card-holder-name").value,104 // Billing Address105 billingAddress: {106 // Street address, line 1107 streetAddress: document.getElementById(108 "card-billing-address-street"109 ).value,110 // Street address, line 2 (Ex: Unit, Apartment, etc.)111 extendedAddress: document.getElementById(112 "card-billing-address-unit"113 ).value,114 // State115 region: document.getElementById("card-billing-address-state").value,116 // City117 locality: document.getElementById("card-billing-address-city")118 .value,119 // Postal Code120 postalCode: document.getElementById("card-billing-address-zip")121 .value,122 // Country Code123 countryCodeAlpha2: document.getElementById(124 "card-billing-address-country"125 ).value,126 },127 })128 .then(() => {129 fetch(`/api/orders/${orderId}/capture`, {130 method: "post",131 })132 .then((res) => res.json())133 .then((orderData) => {134 // Two cases to handle:135 // (1) Other non-recoverable errors -> Show a failure message136 // (2) Successful transaction -> Show confirmation or thank you137 // This example reads a v2/checkout/orders capture response, propagated from the server138 // You could use a different API or structure for your 'orderData'139 const errorDetail =140 Array.isArray(orderData.details) && orderData.details[0];141 if (errorDetail) {142 var msg = "Sorry, your transaction could not be processed.";143 if (errorDetail.description)144 msg += "\n\n" + errorDetail.description;145 if (orderData.debug_id) msg += " (" + orderData.debug_id + ")";146 return alert(msg); // Show a failure message147 }148 // Show a success message or redirect149 alert("Transaction completed!");150 });151 })152 .catch((err) => {153 alert("Payment could not be captured! " + JSON.stringify(err));154 });155 });156 });
- Line 98 sets up an event listener for when the payer submits an eligible card payment.
- Lines 100-127 pass the hosted card field values, such as the cardholder's name and address, to the
POST
call in lines 128-131. Anything you pass into thesubmit
is sent to the iframe that communicates with the Orders API. The iframe retrieves the relevant data and sends it along to thePOST
call. - Lines 128-131 define a
POST
call to/v2/checkout/orders/{id}/capture
that captures the order using theorderId
. - Lines 133-150 declare how to handle the payment capture response. When you're ready to go live, you can remove the alert and either show a success message or redirect the payer to another URL.
Sample capture response
This code sample shows a response to a POST
call to /v2/checkout/orders/{id}/capture
. This response is the orderData
that is grabbed by lines 71-72 in the Render hosted card fields and 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-77 pass the purchase_units in this transaction. Each purchase unit establishes a contract between a customer and merchant. Each purchase unit represents either a full or partial order that the customer intends to purchase from the merchant.
- Line 16 passes the reference_id that identifies the 1 purchase unit in this payment response.
- Lines 17-24 pass details about the shipping address.
- Line 27 declares the payments object that passes the payment details for this capture request.
- Line 28 declares the captures object that passes details about the captured payments for this request.
- Lines 29-57 pass the payment capture details, such as the capture identifier id, amount, disbursement_mode, and net_amount.
- Lines 59-73 pass the HATEOAS details of the capture response.
- Lines 78-85 pass details about the payer.
- Lines 86-92 pass the HATEOAS details for the orders response.
See the Capture payment for order API endpoint for more details about the capture response.