Buyer experience


Implement any of these capabilities to provide a unique buyer experience.

Know before you code

1. Display other payment methods

If you present other funding sources alongside PayPal, you can create an uncluttered user interface using marks and radio buttons to dynamically display the funding source only when the user selects it.

To show or hide the PayPal buttons dynamically when radio buttons are clicked:

  1. Add the marks component when setting up the SDK, using components=buttons,marks.
  2. Create multiple radio buttons, one for PayPal, and one for each of the other funding sources you want to display.
  3. Render the PayPal marks next to the PayPal radio button, and other marks next to the other radio buttons.
  4. If the buyer selects the PayPal radio button, dynamically show the PayPal buttons and hide the other funding sources.
  5. If the buyer selects a different radio button, dynamically show that funding source.

Sample PayPal JavaScript SDK code

<!-- Add the PayPal JavaScript SDK with both buttons and marks components -->
<script src="https://www.paypal.com/sdk/js?client-id=test&components=buttons,marks"></script>

<!-- Render the radio buttons and marks -->
<label>
  <input type="radio" name="payment-option" value="paypal" checked>
  <img src="paypal-mark.jpg" alt="Pay with PayPal">
</label>

<label>
  <input type="radio" name="payment-option" value="alternate">
  <div id="paypal-marks-container"></div>
</label>

<div id="paypal-buttons-container"></div>
<div id="alternate-button-container">
  <button>Pay with a different method</button>
</div>

<script>
  // Render the PayPal marks
  paypal.Marks().render('#paypal-marks-container');

  // Render the PayPal buttons
  paypal.Buttons().render('#paypal-buttons-container');

  // Listen for changes to the radio buttons
  document.querySelectorAll('input[name=payment-option]')
    .forEach(function (el) {
      el.addEventListener('change', function (event) {

        // If PayPal is selected, show the PayPal button
        if (event.target.value === 'paypal') {
          document.body.querySelector('#alternate-button-container')
            .style.display = 'none';
          document.body.querySelector('#paypal-button-container')
            .style.display = 'block';
        }

        // If alternate funding is selected, show a different button
        if (event.target.value === 'alternate') {
          document.body.querySelector('#alternate-button-container')
            .style.display = 'block';
          document.body.querySelector('#paypal-button-container')
            .style.display = 'none';
        }
      });
    });

  // Hide non-PayPal button by default
  document.body.querySelector('#alternate-button-container')
    .style.display = 'none';
</script>

2. Display funding source used

In your Checkout experience, you might have a confirmation page or a notification to the user that they're paying with PayPal. You will want to ensure the funding source the user chose (for example, Venmo) is reflected or it might lead to confusion with your buyers and lead to poor Checkout conversion. You can use an onClick handler to get the funding source that was used and decide what to display within your Checkout experience.

let fundingSource;

paypal.Buttons({
    onClick: (data) => {
        // fundingSource = "venmo"
        fundingSource = data.fundingSource;

        // Use this value to determine what funding source was used to pay
        // Update your confirmation pages and notifications from "PayPal" to "Venmo"
    }
})

Here is a list of possible fundingSource values:

Funding source Description
paypal.FUNDING.PAYPAL PayPal
paypal.FUNDING.CARD Credit or debit cards
paypal.FUNDING.PAYLATER Pay later
paypal.FUNDING.CREDIT PayPal Credit
paypal.FUNDING.VENMO Venmo

3. Show cancellation page

To show a cancellation page instead of the parent page when a buyer cancels a payment, add the onCancel function to the JavaScript that renders the PayPal buttons.

paypal.Buttons({
  onCancel: function (data) {
    // Show a cancel page, or return to cart
  }
}).render('#paypal-button-container');

4. Validate user input

Sometimes it's necessary to run validation when the PayPal buttons are clicked. For example, your page might contain a web form that must be valid before the transaction initiates.

For an optimal buyer experience, PayPal recommends completing the majority of this validation prior to rendering the buttons. For validation that you cannot complete before rendering the buttons, run synchronous or asynchronous validation when clicking the PayPal buttons.

Synchronous validation

<p id="error" class="hidden">Please check the checkbox</p>
<label><input id="check" type="checkbox"> Check here to continue</label>

<script>
  paypal.Buttons({

    // onInit is called when the button first renders
    onInit: function(data, actions) {

      // Disable the buttons
      actions.disable();

      // Listen for changes to the checkbox
      document.querySelector('#check')
        .addEventListener('change', function(event) {

          // Enable or disable the button when it is checked or unchecked
          if (event.target.checked) {
            actions.enable();
          } else {
            actions.disable();
          }
        });
    },

    // onClick is called when the button is clicked
    onClick: function() {

      // Show a validation error if the checkbox is not checked
      if (!document.querySelector('#check').checked) {
        document.querySelector('#error').classList.remove('hidden');
      }
    }

  }).render('#paypal-button-container');
</script>

Whenever possible, PayPal recommends setting up synchronous validation for your page. This results in the best user experience because the buyer sees instant feedback and no popup window displays.

Asynchronous validation

<p id="error" class="hidden">Please check your information to continue</p>

<script>
  paypal.Buttons({

    // onClick is called when the button is clicked
    onClick: function(data, actions) {

      // You must return a promise from onClick to do async validation
      return fetch('/my-api/validate', {
        method: 'post',
        headers: {
          'content-type': 'application/json'
        }
      }).then(function(res) {
        return res.json();
      }).then(function(data) {

        // If there is a validation error, reject, otherwise resolve
        if (data.validationError) {
          document.querySelector('#error').classList.remove('hidden');
          return actions.reject();
        } else {
          return actions.resolve();
        }
      });
    }

  }).render('#paypal-button-container');
</script>

Some validation must be done on the server-side or using an asynchronous channel.

Do not use asynchronous validation for any validation that can be done synchronously as it degrades the buyer's experience.

5. Update order details

This procedure applies to standard and advanced integrations.

For increased flexibility when obtaining payments from buyers, you can update the transaction after it has been set up. This allows you to:

  • Determine additional amounts, including shipping and tax.
  • Capture a different total amount, without needing the buyer to re-approve the transaction.
  • Update fields, such as shipping address, after collecting them from the buyer.

To update order details, complete the following steps:

  1. If you are updating the final amount of the transaction after the buyer approves the payment, or if you are updating other amount fields, such as shipping or tax, add commit=false in the script tag as shown in the following example:

    <script src="https://www.paypal.com/sdk/js?client-id=CLIENT_ID&commit=false">
    </script>    
    

    Passing commit=false shows a Continue button during the buyer checkout experience rather than a Pay Now button, indicating to the buyer that the amount or other details might change before they complete the transaction.

    Tip: Using commit=false reduces the number of payment methods that are shown to your buyer, since not all funding sources can be used when modifying the transaction. When possible, determine the final amount before the buyer approves the transaction on PayPal, and avoid using PATCH.

  2. Before you capture the funds from the transaction, call the PayPal Orders API on your server with the order ID. You can pass in a different amount, invoice_id, and custom_id. See the following code sample for the list of fields that you can modify.

    curl -v -X PATCH https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer Access-Token" \
    -d '[
      {
    "op": "add",
    "path": "/purchase_units/@reference_id=='PUHF'/invoice_id",
    "value": {
      "integration_artifact": "INV-HighFashions"
    }
      }
    ]'
    

6. Handle errors

These procedures apply to standard and advanced integrations.

Buyer checkout error

If an error prevents buyer checkout, alert the user that an error has occurred with the buttons using the onError callback:

paypal.Buttons({
  onError: function (err) {
    // For example, redirect to a specific error page
    window.location.href = "/your-error-page-here";
  }
}).render('#paypal-button-container');

This error handler is a catch-all. Errors at this point are not expected to be handled beyond showing a generic error message or page.

Script not loading error

If null pointer errors prevent the script from loading, provide a different checkout experience:

if (window.paypal && window.paypal.Buttons) {
  // render the buttons
} else {
  // show a fallback experience
}