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=sb&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. 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');

3. 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.

4. 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"
    }
      }
    ]'
    

5. Handle errors

This procedure applies to standard and advanced integrations.

If an error prevents buyer checkout, show an error page using the onError callback:

paypal.Buttons({
  onError: function (err) {
    // Show an error page here, when an error occurs
  }
}).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.