Learn about how to configure PayPal payout components for iOS.
The PayPal iOS SDK provides configurable components for integrating PayPal payouts into your iOS application. Each component is built with SwiftUI and offers comprehensive configuration options for styling, behaviour, and event handling.
All PayPal payout components follow a consistent configuration pattern:
// Payout submission component
let submissionConfig = PayoutSubmissionComponentConfig(
submitText: "Withdraw to PayPal",
submitAccessibilityLabel: "Withdraw funds to PayPal"
)
// Styling
submissionConfig.styles = ButtonStateStyles(
base: FieldStyle(
color: .white,
backgroundColor: Color(red: 0, green: 0.44, blue: 0.73)
)
)
// Event handlers
submissionConfig.onPostPayout = { result in
// Handle payout completion
}Environment and PayPal credentials are configured at SDK initialisation via CheckoutConfig and PayPalConfig. The payout execution mode (proceedPayoutWithSdk) is configured via PayPalConfig.
Each component can be customised with configuration properties:
// Payout submission component
let submissionConfig = PayoutSubmissionComponentConfig(
submitText: "Withdraw to PayPal",
submitAccessibilityLabel: "Withdraw funds to PayPal"
)
// Payout amount component (display only)
let amountConfig = PayoutAmountComponentConfig(
label: "Payout Amount",
accessibilityLabel: "Payout amount"
)
// PayPal payout receiver component (display only)
let receiverConfig = PayPalPayoutReceiverComponentConfig(
label: "PayPal Account",
accessibilityLabel: "PayPal account email"
)Each component supports the following configuration properties:
| Property | Description |
|---|---|
recipientTypePayoutRecipientType | The recipient type. Default: .email. |
walletPayPalPayoutWalletType | The wallet type. Default: .paypal. |
submitTextString? | Custom text for the submit button. Default: "Withdraw with PayPal". |
submitAccessibilityLabelString? | Accessibility label for the button. Default: "Withdraw with PayPal". |
stylesButtonStateStyles? | Custom button styles for different states. |
onClick(() -> Void)? | Called when the button is clicked (before validation). |
onPrePayoutSubmit(() async -> PrePayoutSubmitResult?)? | Called for merchant approval before payout (SDK-managed mode only). |
onPostPayout((MerchantSubmitResult) -> Void)? | Called after payout completes (SDK-managed mode only). |
onCancel(() -> Void)? | Called when the payout is cancelled. |
onError((BaseSdkException) -> Void)? | Called when an error occurs. Parameter: error - Exception with errorCode and errorMessage properties. |
| Property | Description |
|---|---|
labelString? | Label text displayed above the amount field. |
placeholderString? | Placeholder text shown when amount is empty. |
guideTextString? | Helper text displayed below the amount field. |
accessibilityLabelString? | Accessibility label for screen readers. |
stylesFieldStyle? | Custom styling for the container. |
inputStylesFieldInputStateStyles? | Custom styling for the amount text. |
labelStylesFieldLabelStateStyles? | Custom styling for the label text. |
| Property | Description |
|---|---|
labelString? | Label text displayed above the email field. |
placeholderString? | Placeholder text shown when field is empty. |
guideTextString? | Helper text displayed below the email field. |
accessibilityLabelString? | Accessibility label for screen readers. |
applyMaskBool | Whether to mask the email address. Default: true. |
showMaskToggleBool | Whether to show a toggle to reveal/hide email. Default: true. |
receiverTypePayoutRecipientType | The recipient type. Default: .email. |
stylesFieldStyle? | Custom styling for the container. |
inputStylesFieldInputStateStyles? | Custom styling for the email text. |
labelStylesFieldLabelStateStyles? | Custom styling for the label text. |
validationsPayPalPayoutReceiverValidationResource? | Custom validation error messages. |
Error callback type handling: While the onError callback signature accepts Error, the SDK always passes a BaseSdkException instance at runtime, allowing access to .errorCode and .errorMessage properties.
For more complex implementations, you can configure styling, callbacks, and additional features:
let submissionConfig = PayoutSubmissionComponentConfig(
submitText: "Withdraw to PayPal",
submitAccessibilityLabel: "Withdraw funds to PayPal",
// Custom button styling
styles: ButtonStateStyles(
base: FieldStyle(
color: .white,
fontSize: 16,
fontWeight: .semibold,
backgroundColor: Color(red: 0, green: 0.44, blue: 0.73),
cornerRadius: 8,
padding: EdgeInsets(top: 14, leading: 24, bottom: 14, trailing: 24)
),
disabled: FieldStyle(
backgroundColor: Color(.systemGray5),
opacity: 0.6
),
loading: FieldStyle(
backgroundColor: Color(red: 0, green: 0.37, blue: 0.65),
opacity: 0.9
)
),
// Event handlers
onClick: {
print("Submit button clicked")
},
onPrePayoutSubmit: {
return await showApprovalDialog()
},
onPostPayout: { result in
showSuccessMessage(result.merchantTransactionId)
},
onError: { error in
handlePayoutError(error)
}
)All components come with PayPal-branded default styling:
- Submission button: PayPal blue with collapsed size.
- Amount display: Read-only field with label.
- Receiver display: Read-only field with email masking.
Each component offers comprehensive styling options:
- Colours: Customise all colour properties including backgrounds, text, and states.
- Typography: Control font sizes, weights, and text styles.
- Layout: Configure spacing, padding, dimensions, and positioning.
- Shapes: Adjust corner radius and border styles.
// Custom button styling with states
let buttonStyles = ButtonStateStyles(
base: FieldStyle(
color: .white,
fontSize: 18,
fontWeight: .bold,
backgroundColor: Color(red: 0.2, green: 0.7, blue: 0.3),
borderColor: Color(red: 0.2, green: 0.7, blue: 0.3),
borderWidth: 0,
cornerRadius: 12,
padding: EdgeInsets(top: 16, leading: 32, bottom: 16, trailing: 32),
opacity: 1.0
),
disabled: FieldStyle(
color: Color(.systemGray2),
backgroundColor: Color(.systemGray5),
opacity: 0.5
),
loading: FieldStyle(
color: .white,
backgroundColor: Color(red: 0.15, green: 0.55, blue: 0.25),
opacity: 0.9
)
)
let submissionConfig = PayoutSubmissionComponentConfig(
submitText: "Withdraw to PayPal",
styles: buttonStyles
)The display components (PayoutAmountComponent, PayPalPayoutReceiverComponent) support field-specific styling:
let amountConfig = PayoutAmountComponentConfig(
label: "Payout Amount",
placeholder: "$0.00",
guideText: "Amount to be sent to your PayPal account",
accessibilityLabel: "Payout amount",
styles: FieldStyle(
backgroundColor: Color(.systemGray6),
cornerRadius: 12,
padding: EdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20)
),
inputStyles: FieldInputStateStyles(
base: FieldStyle(
color: Color(red: 0.2, green: 0.7, blue: 0.3),
fontSize: 32,
fontWeight: .bold,
textAlignment: .center
)
),
labelStyles: FieldLabelStateStyles(
base: FieldStyle(
color: .secondary,
fontSize: 14,
fontWeight: .medium
)
)
)All components provide event handlers for user interactions and lifecycle events.
Common event patterns:
- Click handlers: Called when the button is tapped.
- Success handlers: Called when operations complete successfully.
- Error handlers: Called when errors occur.
- Cancellation handlers: Called when the user cancels the flow.
- Approval handlers: Called for merchant approval before payout execution.
let submissionConfig = PayoutSubmissionComponentConfig(
submitText: "Withdraw to PayPal",
// Click handling
onClick: {
print("Button clicked")
showLoadingIndicator()
},
// Merchant approval (SDK-managed mode)
onPrePayoutSubmit: {
let approved = await showApprovalDialog(
amount: 100.00,
currency: "USD"
)
guard approved else { return nil }
return PrePayoutSubmitResult(isApproved: true)
},
// Success handling
onPostPayout: { result in
print("Payout completed: \(result.merchantTransactionId)")
navigateToSuccessScreen()
},
// Error handling
onError: { error in
print("Payout error: \(error.errorMessage)")
showErrorDialog(error.errorMessage)
},
// Cancellation handling
onCancel: {
print("User cancelled payout")
showCancelMessage()
}
)The payout SDK provides components for displaying payout information and executing transactions:
Use PayoutAmountComponent and PayPalPayoutReceiverComponent to show payout details to users:
// Create amount display
let amountConfig = PayoutAmountComponentConfig(
label: "Amount"
)
let amountComponent = try pxpCheckout.create(
.payoutAmount,
componentConfig: amountConfig
)
// Create receiver display
let receiverConfig = PayPalPayoutReceiverComponentConfig(
label: "PayPal Account",
applyMask: true,
showMaskToggle: true
)
let receiverComponent = try pxpCheckout.create(
.paypalPayoutReceiver,
componentConfig: receiverConfig
)Use PayoutSubmissionComponent to provide a button for users to execute the payout:
// Configure SDK with stored credentials
let paypalConfig = PayPalConfig(
payout: PayPalPayoutConfig(
paypalWallet: PayPalWallet(
email: "user@example.com",
payerId: "PAYERID123ABC",
proceedPayoutWithSdk: true
)
)
)
let submissionConfig = PayoutSubmissionComponentConfig(
submitText: "Withdraw to PayPal",
onPostPayout: { result in
print("Payout completed: \(result.merchantTransactionId)")
}
)
let submitComponent = try pxpCheckout.create(
.payoutSubmission,
componentConfig: submissionConfig
)Configure how payouts are executed via PayPalConfig. The proceedPayoutWithSdk parameter controls whether the SDK or your backend manages payout execution.
The SDK executes the payout after merchant approval:
let paypalConfig = PayPalConfig(
payout: PayPalPayoutConfig(
paypalWallet: PayPalWallet(
email: "user@example.com", // From your backend
payerId: "PAYERID123ABC", // From your backend
proceedPayoutWithSdk: true // SDK manages payout
)
)
)With SDK-managed payouts, implement the onPrePayoutSubmit and onPostPayout callbacks.
Your backend handles payout execution:
let paypalConfig = PayPalConfig(
payout: PayPalPayoutConfig(
paypalWallet: PayPalWallet(
email: "user@example.com", // From your backend
payerId: "PAYERID123ABC", // From your backend
proceedPayoutWithSdk: false // Backend manages payout (default)
)
)
)With backend-managed payouts, your iOS app notifies the backend when the user requests a payout, then your backend executes the payout via the Unity API.
Default behaviour: proceedPayoutWithSdk defaults to false. When false:
- SDK doessn't execute the payout
onPrePayoutSubmitandonPostPayoutcallbacks aren't triggered.- Your backend must handle payout execution.
- The iOS app notifies the backend when the user initiates a payout.
Customise the styling to match your app's design:
// Define brand colours
struct BrandColors {
static let primary = Color(red: 0, green: 0.44, blue: 0.73)
static let success = Color(red: 0.2, green: 0.7, blue: 0.3)
static let surface = Color(red: 0.97, green: 0.98, blue: 0.98)
static let onSurface = Color(red: 0.13, green: 0.15, blue: 0.16)
}
// Apply to components
let submissionConfig = PayoutSubmissionComponentConfig(
submitText: "Withdraw to PayPal",
styles: ButtonStateStyles(
base: FieldStyle(
color: .white,
backgroundColor: BrandColors.primary,
cornerRadius: 8
)
)
)Implement comprehensive event handling:
let submissionConfig = PayoutSubmissionComponentConfig(
submitText: "Withdraw to PayPal",
onClick: { handleClick() },
onPrePayoutSubmit: { await handleApproval() },
onPostPayout: { handleSuccess($0) },
onCancel: { handleCancel() },
onError: { handleError($0) }
)For more information about events, see Events.
Test your components in various scenarios:
- Light and dark mode.
- Different screen sizes (iPhone, iPad).
- Various payout amounts.
- Error conditions.
- Network failures.
Here's a comprehensive example showing all configuration options for a complete payout flow:
import SwiftUI
import PXPCheckoutSDK
// Initialise SDK
let sessionData = SessionData(
sessionId: "your-session-id",
hmacKey: "your-hmac-key",
encryptionKey: "your-encryption-key",
allowedFundingTypes: nil
)
let transactionData = TransactionData(
amount: Decimal(100.00),
currency: "USD",
entryType: .ecom,
intent: TransactionIntentData(card: nil, paypal: .payout),
merchantTransactionId: "payout-\(UUID().uuidString)",
merchantTransactionDate: { Date() }
)
let paypalConfig = PayPalConfig(
payout: PayPalPayoutConfig(
paypalWallet: PayPalWallet(
email: "user@example.com", // From your backend
payerId: "PAYERID123ABC", // From your backend
proceedPayoutWithSdk: true
)
)
)
let checkoutConfig = CheckoutConfig(
environment: .test,
session: sessionData,
transactionData: transactionData,
merchantShopperId: "user-123",
ownerId: "merchant-456",
paypalConfig: paypalConfig,
analyticsEvent: { event in
print("Analytics: \(event.eventName)")
}
)
let pxpCheckout = try PxpCheckout.initialize(config: checkoutConfig)
// Create amount display component
let amountConfig = PayoutAmountComponentConfig(
label: "Payout Amount"
)
let amountComponent = try pxpCheckout.create(.payoutAmount, componentConfig: amountConfig)
// Create receiver display component
let receiverConfig = PayPalPayoutReceiverComponentConfig(
label: "PayPal Account",
applyMask: true,
showMaskToggle: true
)
let receiverComponent = try pxpCheckout.create(.paypalPayoutReceiver, componentConfig: receiverConfig)
// Create submission component with full configuration
let submissionConfig = PayoutSubmissionComponentConfig(
recipientType: .email,
wallet: .paypal,
submitText: "Withdraw to PayPal",
submitAccessibilityLabel: "Withdraw funds to PayPal",
// Custom styling
styles: ButtonStateStyles(
base: FieldStyle(
color: .white,
fontSize: 16,
fontWeight: .semibold,
backgroundColor: Color(red: 0, green: 0.44, blue: 0.73),
borderColor: Color(red: 0, green: 0.44, blue: 0.73),
borderWidth: 0,
cornerRadius: 8,
padding: EdgeInsets(top: 14, leading: 24, bottom: 14, trailing: 24),
opacity: 1.0
),
disabled: FieldStyle(
color: Color(.systemGray),
backgroundColor: Color(.systemGray5),
borderColor: Color(.systemGray5),
opacity: 0.6
),
loading: FieldStyle(
color: .white,
backgroundColor: Color(red: 0, green: 0.37, blue: 0.65),
opacity: 0.9
)
),
// Event handlers
onClick: {
print("Withdraw button clicked")
Analytics.track("payout_initiated")
},
onPrePayoutSubmit: {
print("Approval requested")
// Show confirmation dialog
let approved = await showPayoutApprovalDialog(
amount: 100.00,
currency: "USD"
)
guard approved else {
Analytics.track("payout_approval_rejected")
return nil
}
Analytics.track("payout_approval_granted")
return PrePayoutSubmitResult(isApproved: true)
},
onPostPayout: { result in
print("Payout successful!")
print("Merchant TX ID: \(result.merchantTransactionId)")
print("System TX ID: \(result.systemTransactionId)")
Analytics.track("payout_completed", properties: [
"transactionId": result.merchantTransactionId
])
navigateToSuccessScreen(result.merchantTransactionId)
},
onCancel: {
print("User cancelled payout")
Analytics.track("payout_cancelled")
showCancelMessage()
},
onError: { error in
print("Payout error: \(error.errorCode) - \(error.errorMessage)")
Analytics.track("payout_error", properties: [
"errorCode": error.errorCode,
"errorMessage": error.errorMessage
])
showErrorDialog(error.errorMessage)
}
)
// Create submission component
let submitComponent = try pxpCheckout.create(
.payoutSubmission,
componentConfig: submissionConfig
)
// Render in SwiftUI
VStack(spacing: 16) {
amountComponent.buildContent()
receiverComponent.buildContent()
submitComponent.buildContent()
}Now that you understand component configuration fundamentals, dive into the detailed documentation for each component:
- Payout amount component: Amount display configuration.
- Payout receiver component: Receiver display configuration.
- Payout submission component: Submit button configuration.