Skip to content

Events

Implement callbacks to customise your PayPal payment flow for iOS.

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 3D Secure and risk screening.
  • Manage different funding sources (PayPal, Pay Later).
  • Customise the checkout experience based on customer preferences.

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

Supported events

The following table lists all events supported by the different PayPal components.

EventPayPal button componentConsent component
onApprove
onError
onCancel
onClick
onOrderCreated
onSubmitError
onPreAuthorisation
onGetConsent

Callbacks

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:

  • Process the approved payment and complete the transaction.
  • Update the inventory and order status in your system.
  • Send order confirmation emails or push notifications to customers.
  • Navigate to a success screen in your app.

Event data

Event dataDescription
approvalData
PayPalApprovalData
The payment approval data object from PayPal.
approvalData.orderID
String
The unique PayPal order identifier for this transaction.
approvalData.payerID
String
The PayPal payer identifier for the customer who approved the payment.

Example implementation

let config = PayPalButtonComponentConfig()
config.onApprove = { approvalData in
    print("Payment approved: Order ID \(approvalData.orderID)")
    
    // Update your system with the successful payment
    updateOrderStatus(
        orderId: approvalData.orderID,
        status: "completed",
        paymentMethod: "paypal"
    )
    
    // Update inventory
    updateInventory(approvalData.orderID)
    
    // Send confirmation notification
    sendPushNotification(
        title: "Payment Successful",
        body: "Your order \(approvalData.orderID) has been confirmed"
    )
    
    // Track successful payment
    Analytics.track("paypal_payment_completed", properties: [
        "orderId": approvalData.orderID,
        "payerID": approvalData.payerID,
        "timestamp": Date().timeIntervalSince1970
    ])
    
    // Navigate to success screen
    navigateToSuccessScreen(approvalData.orderID)
}

onCancel

This callback is triggered when the buyer cancels the payment flow in the PayPal popup or navigates 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

Event dataDescription
error
BaseSdkException
The error object containing cancellation details.

Example implementation

let config = PayPalButtonComponentConfig()
config.onCancel = { error in
    print("Payment cancelled by user: \(error.errorMessage)")
    
    // Track cancellation for analytics
    Analytics.track("paypal_payment_cancelled", properties: [
        "timestamp": Date().timeIntervalSince1970,
        "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.",
        duration: 5.0
    )
    
    // Offer alternatives after a short delay
    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
        showAlternativePaymentOptions()
    }
    
    // Optional: Schedule abandoned cart notification
    scheduleAbandonedCartNotification(delayMinutes: 30)
}

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

Event dataDescription
error
BaseSdkException
The error object containing details about what went wrong.

Example implementation

let config = PayPalButtonComponentConfig()
config.onError = { error in
    print("Payment error: \(error.errorMessage)")
    
    // Log error for debugging
    Crashlytics.crashlytics().log("PayPal payment error: \(error.errorMessage)")
    Crashlytics.crashlytics().record(error: error)
    
    // Handle different error types
    let errorMessage: String
    let errorCode = error.errorCode
    
    switch errorCode {
    case "INSTRUMENT_DECLINED":
        errorMessage = "Your PayPal payment was declined. Please try a different payment method or contact PayPal support."
    case "PAYER_ACTION_REQUIRED":
        errorMessage = "Additional action required in PayPal. Please try again or complete any pending actions in your PayPal account."
    case "UNPROCESSABLE_ENTITY":
        errorMessage = "There was an issue processing your payment. Please check your payment details and try again."
    case let code where code.contains("Network") || code.contains("timeout"):
        errorMessage = "Network error. Please check your connection and try again."
    default:
        errorMessage = "Payment failed. Please try again or use a different payment method."
    }
    
    showErrorDialog(
        title: "Payment Error",
        message: errorMessage
    )
    
    // Show alternative payment methods
    showAlternativePaymentMethods()
    
    // Track error metrics
    Analytics.track("paypal_error", properties: [
        "errorCode": errorCode,
        "errorMessage": error.errorMessage,
        "timestamp": Date().timeIntervalSince1970
    ])
}

onClick

This callback is triggered when the PayPal button is clicked, before the payment flow starts.

You can use it to:

  • Trigger analytics tracking.
  • Perform pre-payment validation.
  • Update UI state to show loading indicators.
  • Log user interactions.

Event data

This callback receives no parameters.

Example implementation

let config = PayPalButtonComponentConfig()
config.onClick = {
    print("PayPal button clicked")
    
    // Track button click
    Analytics.track("paypal_button_clicked", properties: [
        "timestamp": Date().timeIntervalSince1970,
        "cartValue": getCurrentCartValue()
    ])
    
    // Show loading indicator
    showLoadingIndicator()
    
    // Perform pre-payment validation
    validateCartBeforePayment()
}

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

Event dataDescription
submitResult
BaseSubmitResult
The order creation response object containing transaction details.
submitResult.merchantTransactionId
String
Your unique transaction identifier (available after casting to MerchantSubmitResult).
submitResult.systemTransactionId
String
The PXP transaction ID for tracking within the PXP system (available after casting to MerchantSubmitResult).

Example implementation

let config = PayPalButtonComponentConfig()
config.onOrderCreated = { submitResult in
    print("Order created successfully")
    
    // Cast to MerchantSubmitResult to access transaction IDs
    if let merchantResult = submitResult as? MerchantSubmitResult {
        print("- Merchant TX ID: \(merchantResult.merchantTransactionId)")
        print("- System TX ID: \(merchantResult.systemTransactionId)")
        
        // Store order information
        storeOrderInformation(
            pxpTransactionId: merchantResult.systemTransactionId,
            merchantTransactionId: merchantResult.merchantTransactionId,
            status: "created"
        )
        
        // Track order creation
        Analytics.track("paypal_order_created", properties: [
            "pxpTransactionId": merchantResult.systemTransactionId,
            "merchantTransactionId": merchantResult.merchantTransactionId,
            "timestamp": Date().timeIntervalSince1970
        ])
    }
    
    // Update UI to show order created state
    updateOrderStatus("Order created, awaiting payment approval...")
}

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

Event dataDescription
error
BaseSdkException
The error object describing the submission failure.

Example implementation

let config = PayPalButtonComponentConfig()
config.onSubmitError = { error in
    print("Submission error: \(error.errorMessage)")
    
    // Log error for debugging
    Crashlytics.crashlytics().log("PayPal submission error: \(error.errorMessage)")
    
    // Handle specific error types
    let errorMessage: String
    let errorCode = error.errorCode
    
    switch errorCode {
    case "VALIDATION_ERROR":
        errorMessage = "Please check your payment details and try again."
    case "INSUFFICIENT_FUNDS":
        errorMessage = "Insufficient funds in your PayPal account. Please add funds or use a different payment method."
    case "MERCHANT_NOT_ELIGIBLE":
        errorMessage = "PayPal payments are temporarily unavailable. Please try a different payment method."
    default:
        errorMessage = "Payment processing failed. Please try again or contact support."
    }
    
    showErrorDialog(
        title: "Submission Error",
        message: errorMessage
    )
    
    // Offer alternative payment methods
    showAlternativePaymentMethods()
    
    // Track submission errors
    Analytics.track("paypal_submission_error", properties: [
        "errorCode": errorCode,
        "error": error.errorMessage,
        "timestamp": Date().timeIntervalSince1970
    ])
}

onPreAuthorisation

This callback is triggered before payment authorisation to provide additional transaction data for PayPal payments.

You can use it to:

  • Include address verification details.
  • Supply risk screening data with transaction items.
  • Provide device session information for fraud prevention.

Event data

This callback receives no parameters but returns optional transaction data.

Example implementation

let config = PayPalButtonComponentConfig()
config.onPreAuthorisation = { 
    print("Pre-authorisation callback triggered")
    
    // Return additional transaction data
    return PayPalTransactionInitData(
        addressVerification: AddressVerification(
            countryCode: "US",
            houseNumberOrName: "123",
            postalCode: "12345"
        ),
        riskScreeningData: RiskScreeningData(
            performRiskScreening: true,
            excludeDeviceData: false,
            deviceSessionId: "device-session-\(UUID().uuidString)",
            items: [
                RiskScreeningItem(
                    price: 25.00,
                    quantity: 1,
                    category: "electronics",
                    sku: "PROD-001"
                )
            ]
        )
    )
}

onGetConsent

This callback is triggered to get user consent for vaulting (saving) their PayPal account for one-click payments.

You can use it to:

  • Check if user agreed to save their PayPal account.
  • Integrate with consent components.
  • Track consent preferences.
  • Comply with payment method storage regulations.

Event data

This callback receives no parameters and returns a Boolean indicating consent status.

Example implementation

// Option 1: Using consent component
let consentComponent = try pxpCheckout.create(
    .paypalConsent,
    componentConfig: PayPalConsentComponentConfig(
        label: "Save my PayPal account"
    )
)

let config = PayPalButtonComponentConfig()
config.paypalConsentComponent = consentComponent as? PayPalConsentComponent

// Option 2: Using custom consent logic
config.onGetConsent = {
    // Get consent from your custom mechanism
    let isConsentGranted = UserDefaults.standard.bool(forKey: "paypal_consent")
    
    // Log consent status
    print("Consent status: \(isConsentGranted)")
    
    // Track consent
    Analytics.track("paypal_consent_status", properties: [
        "granted": isConsentGranted,
        "timestamp": Date().timeIntervalSince1970
    ])
    
    return isConsentGranted
}

Complete example

Here's a complete example with all event callbacks:

let config = PayPalButtonComponentConfig()
config.fundingSource = .paypal

// Click handler
config.onClick = {
    print("PayPal button clicked")
    showLoadingIndicator()
}

// Pre-authorisation
config.onPreAuthorisation = {
    return PayPalTransactionInitData(
        riskScreeningData: RiskScreeningData(
            performRiskScreening: true,
            excludeDeviceData: false,
            deviceSessionId: "device-\(UUID().uuidString)",
            items: []
        )
    )
}

// Order created
config.onOrderCreated = { submitResult in
    if let merchantResult = submitResult as? MerchantSubmitResult {
        print("Order created: \(merchantResult.systemTransactionId)")
        trackOrderCreation(merchantResult.systemTransactionId)
    }
}

// Payment approved
config.onApprove = { approvalData in
    print("Payment approved: \(approvalData.orderID)")
    processPaymentSuccess(approvalData.orderID)
    navigateToSuccessScreen(approvalData.orderID)
}

// Payment cancelled
config.onCancel = { error in
    print("Payment cancelled")
    hideLoadingIndicator()
    showCancelMessage()
}

// Payment error
config.onError = { error in
    print("Payment error: \(error.errorMessage)")
    hideLoadingIndicator()
    showErrorDialog(error.errorMessage)
}

// Submission error
config.onSubmitError = { error in
    print("Submit error: \(error.errorMessage)")
    hideLoadingIndicator()
    handleSubmitError(error)
}

// Get consent
config.onGetConsent = {
    return UserDefaults.standard.bool(forKey: "paypal_consent")
}

// Create component
let paypalComponent = try pxpCheckout.create(
    .paypalButton,
    componentConfig: config
)