Skip to content

Events

Implement callbacks to customise your PayPal payment flow for Web.

Overview

Components emit events based on user interaction or validation. You can use these to implement callback functions, which allow you to inject your own business logic and user experience customisations into the payment flow at critical moments. They ensure that while the SDK handles the complex technical aspects of payment processing, you retain full control over the customer experience and can seamlessly integrate payments into your broader business workflows and systems.

Callbacks enable you to:

  • Validate business rules before payments proceed.
  • Display custom error, failure, or success messages.
  • Tailor user interfaces to match your brand's look and feel.
  • Integrate with your own systems for fraud detection or customer management.
  • Control exactly how your customers experience both successful and failed transactions.
  • Handle PayPal-specific requirements like shipping calculations and address validation.
  • Manage different funding sources (PayPal, Pay Later, Venmo).
  • Customise the checkout experience based on customer preferences.

SDK data callbacks

The PayPal component requires SDK initialisation callbacks to gather shopper and shipping information during transaction processing:

  • onGetShopper: Required - Called to retrieve current shopper data (ID, email, name, etc.) for transaction submission to the backend.
  • onGetShippingAddress: Required when shipping is needed - Called to get shipping address information for the transaction.

These callbacks ensure the PayPal component always uses the latest customer data from your application state, forms, or APIs when processing payments.

PayPal-specific events

All PayPal-specific events are optional and can be mixed and matched based on your business needs.

The SDK initialisation callbacks (onGetShopper, onGetShippingAddress) work alongside PayPal-specific callbacks to provide a complete payment experience. SDK callbacks handle backend transaction data, while PayPal callbacks handle the PayPal checkout flow interactions.

Supported events

onApprove

This callback is triggered when the buyer approves the payment in the PayPal checkout flow. This is where you capture the funds and complete the transaction.

You can use it to:

  • Capture the approved payment and complete the transaction.
  • Update inventory and order status in your system.
  • Send order confirmation emails to customers.
  • Record successful transactions for business intelligence.

Event data

Event dataDescription
data
object
The payment approval data from PayPal.
data.orderID
string
The PayPal order ID for the approved payment.
data.paymentID
string
The PayPal payment ID (legacy v1/v2 API).
data.paymentSource
string
The funding source used (paypal, paylater, venmo).
data.facilityId
string
The merchant facility ID.
actions
object
PayPal actions object containing helper methods.
actions.capture
function
Method to capture the payment.
actions.get
function
Method to get order details.

Example implementation

const paypalComponent = sdk.create('paypal-button', {
  onApprove: async (data, actions) => {
    console.log('PayPal payment approved:', data);
    
    try {
      // Capture the payment using PayPal actions
      const captureResult = await actions.capture();
      console.log('Payment captured successfully:', captureResult);
      
      // Extract important transaction details
      const transactionId = captureResult.id;
      const payerEmail = captureResult.payer.email_address;
      const amount = captureResult.purchase_units[0].amount.value;
      const currency = captureResult.purchase_units[0].amount.currency_code;
      
      // Update your system with the successful payment
      await updateOrderStatus({
        orderId: data.orderID,
        transactionId: transactionId,
        status: 'completed',
        paymentMethod: 'paypal',
        fundingSource: data.paymentSource,
        amount: amount,
        currency: currency
      });
      
      // Update inventory
      await updateInventory(data.orderID);
      
      // Send confirmation email
      await sendConfirmationEmail(payerEmail, {
        orderId: data.orderID,
        transactionId: transactionId,
        amount: amount,
        currency: currency
      });
      
      // Track successful payment
      trackEvent('paypal-payment-completed', {
        orderId: data.orderID,
        transactionId: transactionId,
        fundingSource: data.paymentSource,
        amount: amount,
        currency: currency,
        timestamp: new Date().toISOString()
      });
      
      // Redirect to success page
      window.location.href = `/payment-success?order=${data.orderID}&txn=${transactionId}`;
      
    } catch (error) {
      console.error('Payment capture failed:', error);
      
      // Handle capture failure
      showErrorMessage('Payment processing failed. Please try again or contact support.');
      
      // Log the error for debugging
      logError('paypal-capture-failed', {
        orderId: data.orderID,
        error: error.message,
        timestamp: new Date().toISOString()
      });
    }
  }
});

onCancel

This callback is triggered when the buyer cancels the payment flow in the PayPal popup or redirects back without completing the payment.

You can use it to:

  • Track cancellation rates for conversion optimisation.
  • Show helpful messages or alternative payment options.
  • Save the customer's cart for later completion.
  • Trigger email campaigns for abandoned checkouts.

Event data

ParameterDescription
data
object
The cancellation data from PayPal.
data.orderID
string
The PayPal order ID that was cancelled.
data.paymentID
string
The PayPal payment ID (if available).

Example implementation

const paypalComponent = sdk.create('paypal-button', {
onCancel: (data) => {
    console.log('PayPal payment cancelled:', data);
    
    // Track cancellation for analytics
    trackEvent('paypal-payment-cancelled', {
      orderId: data.orderID,
      timestamp: new Date().toISOString(),
      cartValue: getCurrentCartValue(),
      stage: 'paypal_checkout'
    });
    
    // Preserve cart for later
    saveCartForLater();
    
    // Show helpful message
    showMessage('No worries! Your items are saved. You can complete your purchase anytime.', 'info');
    
    // Offer alternatives after a short delay
    setTimeout(() => {
      showAlternativePaymentOptions();
    }, 2000);
    
    // Optional: Schedule abandoned cart email
    scheduleAbandonedCartEmail(customerEmail, 30); // 30 minutes delay
  }
});

onError

This callback is triggered when an error occurs during the PayPal payment process, such as network issues, invalid configuration, or payment processing failures.

You can use it to:

  • Log errors for debugging and monitoring.
  • Display user-friendly error messages.
  • Offer alternative payment methods.
  • Implement automatic retry for transient errors.

Event data

ParameterDescription
error
Error
The error object containing details about what went wrong.
error.message
string
A human-readable error description.
error.name
string
The error type or name.
error.stack
string
The stack trace for debugging.

Example implementation

const paypalComponent = sdk.create('paypal-button', {
onError: (error) => {
    console.error('PayPal payment error:', error);
    
    // Log error for debugging
    logError('paypal-payment-error', {
      message: error.message,
      name: error.name,
      stack: error.stack,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent
    });
    
    // Handle different error types
    if (error.message.includes('INSTRUMENT_DECLINED')) {
      showUserMessage('Your PayPal payment was declined. Please try a different payment method or contact PayPal support.');
    } else if (error.message.includes('PAYER_ACTION_REQUIRED')) {
      showUserMessage('Additional action required in PayPal. Please try again or complete any pending actions in your PayPal account.');
    } else if (error.message.includes('UNPROCESSABLE_ENTITY')) {
      showUserMessage('There was an issue processing your payment. Please check your payment details and try again.');
    } else if (error.message.includes('Network')) {
      showUserMessage('Network error. Please check your connection and try again.');
      // Offer retry option for network errors
      showRetryButton();
    } else {
      showUserMessage('Payment failed. Please try again or use a different payment method.');
    }
    
    // Show alternative payment methods
    showAlternativePaymentMethods();
    
    // Notify support team for critical errors
    if (error.message.includes('INTERNAL_ERROR')) {
      notifySupport('PayPal internal error occurred', error);
    }
  }
});

onOrderCreated

This callback is triggered when the PayPal order is successfully created but before the customer approves the payment.

You can use it to:

  • Store the order ID for tracking purposes.
  • Update order status in your system.
  • Log order creation events for analytics.
  • Validate order creation success.

Event data

ParameterDescription
data
object
The order creation response from PXP's backend.
data.providerTransactionId
string
The PayPal order ID that was created.
data.transactionId
string
The PXP transaction ID.
data.correlationId
string
The correlation ID for tracking.

Example implementation

const paypalComponent = sdk.create('paypal-button', {
onOrderCreated: (data) => {
    console.log('PayPal order created successfully:', data);
    
    // Store order information
    storeOrderInformation({
      paypalOrderId: data.providerTransactionId,
      pxpTransactionId: data.transactionId,
      correlationId: data.correlationId,
      status: 'created',
      createdAt: new Date().toISOString()
    });
    
    // Update UI to show order created state
    updateOrderStatus('Order created, awaiting payment approval...');
    
    // Track order creation
    trackEvent('paypal-order-created', {
      paypalOrderId: data.providerTransactionId,
      pxpTransactionId: data.transactionId,
      correlationId: data.correlationId,
      timestamp: new Date().toISOString()
    });
    
    // Optional: Start order timeout timer
    startOrderTimeoutTimer(data.providerTransactionId, 15); // 15 minute timeout
  }
});

onShippingAddressChange

This callback is triggered when the customer changes their shipping address in the PayPal checkout flow. Use this to calculate shipping costs and validate delivery availability.

You can use it to:

  • Calculate shipping costs based on the new address.
  • Validate if delivery is available to the location.
  • Update tax rates based on the shipping destination.
  • Check shipping restrictions for specific products.

Event data

ParameterDescription
data
object
The shipping address change data.
data.shipping_address
object
The new shipping address information.
data.shipping_address.country_code
string
The country code of the shipping address.
data.shipping_address.state
string
The state or province.
data.shipping_address.city
string
The city name.
data.shipping_address.postal_code
string
The postal or ZIP code.
actions
object
PayPal actions object for updating the order.
actions.resolve
function
Method to approve the address change with updated order details.
actions.reject
function
Method to reject the address change.

Example implementation

const paypalComponent = sdk.create('paypal-button', {
  onShippingAddressChange: async (data, actions) => {
    console.log('PayPal shipping address changed:', data.shipping_address);
    
    try {
      const shippingAddress = data.shipping_address;
      
      // Validate if we can ship to this location
      const canShip = await validateShippingAddress(shippingAddress);
      if (!canShip) {
        return actions.reject();
      }
      
      // Calculate new shipping cost and tax
      const shippingCost = await calculateShipping(shippingAddress);
      const taxAmount = await calculateTax(shippingAddress, baseAmount);
      const newTotal = baseAmount + taxAmount + shippingCost;
      
      // Get available shipping methods for this address
      const shippingOptions = await getShippingOptions(shippingAddress);
      
      // Update the order with new totals and shipping options
      return actions.resolve({
        purchase_units: [{
          amount: {
            currency_code: 'USD',
            value: newTotal.toFixed(2),
            breakdown: {
              item_total: {
                currency_code: 'USD',
                value: baseAmount.toFixed(2)
              },
              shipping: {
                currency_code: 'USD',
                value: shippingCost.toFixed(2)
              },
              tax_total: {
                currency_code: 'USD',
                value: taxAmount.toFixed(2)
              }
            }
          },
          shipping: {
            options: shippingOptions.map(option => ({
              id: option.id,
              label: option.label,
              type: option.type,
              amount: {
                currency_code: 'USD',
                value: option.cost.toFixed(2)
              },
              selected: option.selected
            }))
          }
        }]
      });
      
    } catch (error) {
      console.error('Shipping calculation failed:', error);
    return actions.reject();
  }
}
});

onShippingOptionsChange

This callback is triggered when the customer selects a different shipping option in the PayPal checkout flow.

You can use it to:

  • Update the total cost based on the shipping method selection.
  • Show updated delivery dates for the selected method.
  • Apply shipping-specific business rules or discounts.
  • Track popular shipping method preferences.

Event data

ParameterDescription
data
object
The shipping option change data.
data.selected_shipping_option
object
The selected shipping option.
data.selected_shipping_option.id
string
The shipping option identifier.
data.selected_shipping_option.label
string
The shipping option display name.
data.selected_shipping_option.type
string
The shipping option type.
data.selected_shipping_option.amount.value
string
The shipping cost.
actions
object
PayPal actions object for updating the order.

Example implementation

const paypalComponent = sdk.create('paypal-button', {
  onShippingOptionsChange: async (data, actions) => {
    console.log('PayPal shipping option changed:', data.selected_shipping_option);
    
    const selectedOption = data.selected_shipping_option;
    const baseAmount = 100.00;
    const taxAmount = 10.00;
    const shippingCost = parseFloat(selectedOption.amount.value);
    
    // Add insurance for expedited shipping
    let insurance = 0;
    if (selectedOption.id === 'expedited' && baseAmount > 50) {
      insurance = 2.99;
    }
    
    // Calculate carbon offset for eco-friendly shipping
    const carbonOffset = selectedOption.id === 'standard' ? 0.50 : 0;
    
    const newTotal = baseAmount + taxAmount + shippingCost + insurance + carbonOffset;
    
    // Track shipping method selection
    trackEvent('paypal-shipping-method-selected', {
      shippingMethod: selectedOption.id,
      shippingCost: shippingCost,
      orderValue: baseAmount,
      timestamp: new Date().toISOString()
    });
    
    // Update order totals
    return actions.resolve({
      purchase_units: [{
        amount: {
          currency_code: 'USD',
          value: newTotal.toFixed(2),
          breakdown: {
            item_total: {
              currency_code: 'USD',
              value: baseAmount.toFixed(2)
            },
            shipping: {
              currency_code: 'USD',
              value: shippingCost.toFixed(2)
            },
            tax_total: {
              currency_code: 'USD',
              value: taxAmount.toFixed(2)
            },
            ...(insurance > 0 && {
              insurance: {
                currency_code: 'USD',
                value: insurance.toFixed(2)
              }
            })
          }
        }
      }]
    });
  }
});

onInit

This callback is triggered when the PayPal button is initialised and ready for interaction.

You can use it to:

  • Control when the PayPal button becomes available for clicks.
  • Implement custom validation before allowing payment.
  • Show loading states or preparation messages.
  • Enable or disable the button based on form validation.

Event data

ParameterDescription
data
object
The initialisation data from PayPal.
actions
object
PayPal actions object for controlling button state.
actions.enable
function
Method to enable the PayPal button.
actions.disable
function
Method to disable the PayPal button.

Example implementation

const paypalComponent = sdk.create('paypal-button', {
  onInit: (data, actions) => {
    console.log('PayPal button initialised:', data);
    
    // Initially disable the button
    actions.disable();
    
    // Enable button when form is valid
    function validateForm() {
      const email = document.getElementById('email')?.value;
      const terms = document.getElementById('terms')?.checked;
      
      if (email && terms && email.includes('@')) {
        actions.enable();
      } else {
        actions.disable();
      }
    }
    
    // Set up form validation listeners
    document.getElementById('email')?.addEventListener('input', validateForm);
    document.getElementById('terms')?.addEventListener('change', validateForm);
    
    // Initial validation
    validateForm();
  }
});

onClick

This callback is triggered when the PayPal button is clicked, before the PayPal checkout flow begins.

You can use it to:

  • Validate collected shipping address and shopper details before proceeding to PayPal.
  • Perform final validation before checkout.
  • Track button click events for analytics.
  • Show loading states or checkout preparation messages.
  • Prevent checkout if conditions are not met.

The SDK will automatically collect shopper and shipping address data via the onGetShopper and onGetShippingAddress callbacks when creating the PayPal order. Use the onClick callback to validate this data before the user is redirected to PayPal.

Event data

ParameterDescription
data
object
The click event data from PayPal.
actions
object
PayPal actions object for controlling the flow.
actions.resolve
function
Method to continue with the checkout flow.
actions.reject
function
Method to prevent the checkout flow.

Example implementation

const paypalComponent = sdk.create('paypal-button', {
  onClick: async (data, actions) => {
    console.log('PayPal button clicked:', data);
    
    // Track button click
    trackEvent('paypal-button-clicked', {
      timestamp: new Date().toISOString(),
      cartValue: getCurrentCartValue()
    });
    
    // Validate collected shipping address and shopper details before proceeding
    try {
      // Collect current shopper data from your form/state
      const shopperData = {
        id: getCurrentShopperId(),
        email: getCustomerEmail(),
        firstName: getCustomerFirstName(),
        lastName: getCustomerLastName(),
        phoneNumber: getCustomerPhone()
      };
      
      // Collect current shipping address from your form/state
      const shippingAddress = {
        address: getShippingAddress(),
        city: getShippingCity(),
        state: getShippingState(),
        postalCode: getShippingPostalCode(),
        countryCode: getShippingCountryCode()
      };
      
      // Validate shopper data completeness
      if (!shopperData.id || !shopperData.email) {
        showError('Please complete customer information before proceeding.');
        return actions.reject();
      }
      
      // Validate shipping address when required
      if (requiresShipping() && (!shippingAddress.address || !shippingAddress.city || !shippingAddress.postalCode)) {
        showError('Please complete shipping address before proceeding.');
        return actions.reject();
      }
      
      // Additional business validations
      const isFormValid = validateCheckoutForm();
      const hasInventory = await checkInventoryAvailability();
      const isCustomerEligible = await checkCustomerEligibility();
      
      if (!isFormValid) {
        showError('Please complete all required fields before proceeding.');
        return actions.reject();
      }
      
      if (!hasInventory) {
        showError('Some items in your cart are no longer available.');
        return actions.reject();
      }
      
      if (!isCustomerEligible) {
        showError('PayPal is not available for your location.');
        return actions.reject();
      }
      
      // All validations passed - show loading state and proceed
      showCheckoutLoader();
      return actions.resolve();
      
    } catch (error) {
      console.error('Validation failed:', error);
      showError('Please verify your information and try again.');
      return actions.reject();
    }
  }
});

onSubmitError

This callback is triggered when there's an error during the order creation or submission process on the PXP side.

You can use it to:

  • Handle PXP-specific errors and validation failures.
  • Display detailed error messages to users.
  • Log submission errors for debugging.
  • Implement retry logic for failed submissions.

Event data

ParameterDescription
error
BaseSdkException
The SDK exception containing error details.
error.message
string
The error message.
error.errorCode
string
The specific error code.

Example implementation

const paypalComponent = sdk.create('paypal-button', {
  onSubmitError: (error) => {
    console.error('PayPal submission error:', error);
    
    // Log error for debugging
    logError('paypal-submission-error', {
      message: error.message,
      errorCode: error.errorCode,
      timestamp: new Date().toISOString()
    });
    
    // Handle specific error types
    switch (error.errorCode) {
      case 'VALIDATION_ERROR':
        showError('Please check your payment details and try again.');
        break;
      case 'INSUFFICIENT_FUNDS':
        showError('Insufficient funds in your PayPal account. Please add funds or use a different payment method.');
        break;
      case 'MERCHANT_NOT_ELIGIBLE':
        showError('PayPal payments are temporarily unavailable. Please try a different payment method.');
        break;
      default:
        showError('Payment processing failed. Please try again or contact support.');
    }
    
    // Offer alternative payment methods
    showAlternativePaymentMethods();
  }
});

onScriptLoaded

This callback is triggered when the PayPal SDK script has been successfully loaded and is ready to use.

You can use it to:

  • Initialise PayPal-related features that depend on the SDK.
  • Show PayPal buttons that were hidden during loading.
  • Track script loading performance.
  • Set up PayPal-specific configurations.

Example implementation

const paypalComponent = sdk.create('paypal-button', {
  onScriptLoaded: () => {
    console.log('PayPal SDK script loaded successfully');
    
    // Track script loading
    trackEvent('paypal-script-loaded', {
      timestamp: new Date().toISOString(),
      loadTime: performance.now()
    });
    
    // Show PayPal buttons that were hidden during loading
    document.querySelectorAll('.paypal-button-container').forEach(container => {
      container.style.display = 'block';
    });
    
    // Hide loading spinner
    hidePayPalLoadingSpinner();
    
    // Initialise PayPal messaging
    if (window.paypal && window.paypal.Messages) {
      window.paypal.Messages({
        amount: getCurrentCartValue(),
        placement: 'cart',
        style: {
          layout: 'text',
          logo: {
            type: 'primary'
          }
        }
      }).render('#paypal-messages');
    }
  }
});

Advanced PayPal event usage

Multiple funding sources

When using the setOfButtons render type, you can handle different funding sources:

const paypalComponent = sdk.create('paypal-button', {
  renderType: 'setOfButtons',
  fundingSources: ['paypal', 'paylater', 'venmo'],
  
  onClick: (data, actions) => {
    // Track which funding source was clicked
    trackEvent('paypal-funding-source-clicked', {
      fundingSource: data.fundingSource,
      timestamp: new Date().toISOString()
    });
    
    // Apply different logic based on funding source
    if (data.fundingSource === 'paylater') {
      // Special handling for Pay Later
      const isEligibleForPayLater = checkPayLaterEligibility();
      if (!isEligibleForPayLater) {
        showError('Pay Later is not available for this purchase.');
    return actions.reject();
      }
    }
    
    return actions.resolve();
  },
  
  onApprove: async (data, actions) => {
    // Handle approval differently based on funding source
    const fundingSource = data.paymentSource || 'paypal';
    
    trackEvent('paypal-payment-approved', {
      fundingSource: fundingSource,
      orderId: data.orderID,
      timestamp: new Date().toISOString()
    });
    
    // Proceed with capture
    return await actions.capture();
  }
});

Error handling and retry logic

Comprehensive error handling for PayPal payments:

const paypalComponent = sdk.create('paypal-button', {
  onError: (error) => {
    const errorInfo = {
      message: error.message,
      timestamp: new Date().toISOString(),
      userAgent: navigator.userAgent,
      url: window.location.href
    };
    
    // Log to monitoring service
    logError('paypal-error', errorInfo);
    
    // Determine if error is retryable
    const retryableErrors = [
      'NETWORK_ERROR',
      'TIMEOUT',
      'TEMPORARY_FAILURE'
    ];
    
    const isRetryable = retryableErrors.some(retryableError => 
      error.message.includes(retryableError)
    );
    
    if (isRetryable) {
      showRetryButton(() => {
        // Retry the PayPal initialisation
        location.reload();
      });
    } else {
      // Show alternative payment methods
      showAlternativePaymentMethods();
    }
    
    // Send error metrics to analytics
    trackEvent('paypal-error', {
      errorType: error.name,
      errorMessage: error.message,
      isRetryable: isRetryable
    });
  }
});