On this page
No Headings
Last updated: June 3, 2026
Follow these steps to add payment buttons to your app or programmatically start the SDK
The PaymentButtonContainer is responsible for displaying the payment buttons. This container can display three different buttons: the PayPal button, the PayPal Credit button, and the Pay Later button. By default the PaymentButtonContainer displays the PayPal button.
Note: PaymentButtonContainer does not support PayPal Credit and Pay Later. Those buttons will return as not eligible. No matter how you configure the PaymentButtonContainer, you will only see the PayPal button.
The PaymentButtonContainer provides the callbacks needed to integrate with the native checkout experience: CreateOrder, OnApprove, OnError, OnCancel, and OnShippingChange.
Each button type has a corresponding configuration object. To customize the buttons' appearance, you can instantiate PayPalButtonUIConfiguration, PayPalCreditButtonUIConfiguration, and PayLaterButtonUIConfiguration objects with custom parameters. Finally, you instantiate the PaymentButtonContainer passing in these configuration instances. If you don't pass any configuration objects to the PaymentButtonContainer initializer, it will use default configuration values.
// Create custom configuration objects
let attributes = PaymentButtonAttributes(
edges: .softEdges,
size: .expanded,
isEnabled: true
)
let payPalUIConfig = PayPalButtonUIConfiguration(
color: .blue,
label: nil,
attributes: attributes
)
let payPalCreditUIConfig = PayPalCreditButtonUIConfiguration(
color: .white,
attributes: attributes
)
let payLaterUIConfig = PayLaterButtonUIConfiguration(
color: .black,
attributes: attributes
)
// Instantiate the container passing the configuration objects
let container = PaymentButtonContainer(
payPalButtonUIConfiguration: payPalUIConfig,
payPalCreditButtonUIConfiguration: payPalCreditUIConfig,
payLaterButtonUIConfiguration: payLaterUIConfig,
)
// Display the container
view.addSubview(container)
NSLayoutConstraint.activate(
[
container.centerYAnchor.constraint(equalTo: view.centerYAnchor),
container.centerXAnchor.constraint(equalTo: view.centerXAnchor)
]
)The PaymentButtonAttributes class contains the three properties that are common to all button types:
| Property | Description |
|---|---|
edges | Overrides the shape of the button. The default value is softEdges, but you can also set it to hardEdges or rounded. |
size | Adjusts the size of the button. The default value is collapsed, but you can also set it to: expanded, full, or mini. |
isEnabled | Determines whether or not to show this button. If this button type is ineligible, this flag will be ignored and the button won't be showed. |
You can customize the appearance of the PayPal button via the PayPalButtonUIConfiguration, which contains the following attributes:
| Property | Description |
|---|---|
color | Changes the background color of the button and adjusts the wordmark color to be visible. The default value is gold, but you can also set it to: white, black, silver, or blue. |
label | Adds a label next to the button logo. The default value is no label, but you can also set it to: checkout, buyNow, or payWith. |
attributes | The button attributes defined by PaymentButtonAttributes. |
You can customize the appearance of the PayPal Credit button via the PayPalCreditButtonUIConfiguration, which contains the following attributes:
| Property | Description |
|---|---|
color | Changes the background color of the button and adjusts the wordmark color to be visible. The default value is darkBlue, but you can also set it to: white or black. |
attributes | The button attributes defined by PaymentButtonAttributes. |
You can customize the appearance of the Pay Later button via the PayLaterButtonUIConfiguration, which contains the following attributes:
| Property | Description |
|---|---|
color | Changes the background color of the button and adjusts the wordmark color to be visible. The default value is gold, but you can also set it to: white, black, silver, or blue. |
attributes | The button attributes defined by PaymentButtonAttributes. |
The PaymentButtonContainer provides the callbacks needed to integrate with the native checkout experience: createOrder, onApproval, onError, onCancel, and onShippingChange.
let container = PaymentButtonContainer(delegate: self)
container.createOrder = { action in
print("Create order here from (action)")
}
container.onCancel = {
print("Cancelled")
}
container.onError = { error in
print("Failed with error: (error)")
}
container.onApproval = { approval in
print("Got approval: (approval)")
}
container.onShippingChange = { change, action in
print("Shipping changed: (change) (action)")
}You can also observe the view state of the PaymentButtonContainer using the PaymentButtonContainerDelegate's onLoading() and onFinish() callbacks.
onLoading and onFinish callbacks are invoked during the network call to check for buyer eligibility.onLoading callback is invoked at the start of the eligibility network call and the PaymentButtonContainer view begins loading.onFinish callback is invoked when the eligibility network call has completed.These callbacks can be implemented like so
// MARK: - PaymentButtonContainerDelegate
func onLoading() {
print("PaymentButtonContainer is loading...")
}
func onFinish(fundingEligibilityState: PaymentFundingEligibilityState?) {
print("PaymentButtonContainer finished loading")
}The fundingEligibilityState contains a map of each PaymentButtonFundingSourceavailable. This includes the default PayPal payment funding type as well as PayPal Credit and Pay Later. A PaymentButtonFundingSource may not be eligible for the buyer to use. The payment button for the funding type will not be visible if the funding type is not eligible. If there's a failure in the request, the error object within fundingEligibilityState will contain the error.
PaymentButtonContainer does not support PayPal Credit and Pay Later. Those buttons will return as not eligible.
Note: PayPalButton, PayPalCreditButton, and PayPalPayLaterButtonare deprecated in favor of PaymentButtonContainer. This section is provided as a reference to those using previous versions.
To migrate to PaymentButtonContainer, follow these steps:
PayPalButton, PayPalCreditButton, and PayPalPayLaterButtonreferences with PaymentButtonContainer.PaymentButtonContainer initializer passing your custom configuration objects.eligibilityStatus for a payment button with onEligibilityStatusChanged, use the onLoading() and onFinish() callbacks provided by the PaymentButtonContainerDelegate for the PaymentButtonContainer as detailed in the PaymentButtonContainerDelegatesection above.You can customize the appearance of the PayPalButton in the following ways:
| Property | Description |
|---|---|
color | Changes the background color of the button and adjusts the wordmark color to be visible. The default value is gold, but you can also set it to: black, blue, silver, or white. |
edges | Overrides the shape of the button. The default value is softEdges, but you can also set it to hardEdges or rounded. |
label | Adds a label next to the button logo. The default value is no label, but you can also set it to: checkout, buyNow, or payWith. |
size | Adjusts the size of the button. The default value is collapsed, but you can also set it to: expanded, full, or mini. |
You can customize the appearance of the PayPalPayLaterButton in the following ways:
| Property | Description |
|---|---|
color | Changes the background color of the button and adjusts the wordmark color to be visible. The default value is gold, but you can also set it to: black, blue, silver, or white. |
edges | Overrides the shape of the button. The default value is softEdges, but you can also set it to hardEdges or rounded. |
size | Adjusts the size of the button. The default value is collapsed, but you can also set it to: expanded, full, or mini. |
| Property | Description |
|---|---|
color | Changes the background color of the button and adjust the wordmark color to be visible. The default value is darkBlue, but you can also set it to black or white. |
edges | Overrides the shape of the button. The default value is softEdges, but you can also set it to hardEdges or rounded. |
size | Adjusts the size of the button. The default value is collapsed, but you can also set it to: expanded, full, or mini. |
To ensure buyers have an optimal experience, every PaymentButton adheres to strict eligibility requirements. Generally, the PayPalButton is always Eligible so it's safe to fallback for other buttons.
Eligibility updates are communicated through a delegate property of the PaymentButton. To receive these events, conform to PaymentButtonDelegate:
class CheckoutView: UIView, PaymentButtonDelegate {
var payPalButton = PayPalButton()
func configurePayPalButton() {
payPalButton.delegate = self
}
func onButtonStart(_ button: PaymentButton) {
// Invoked when the button is selected, before the checkout process begins.
}
func onButtonFinish(_ button: PaymentButton) {
// Invoked when the checkout process has finished
}
func button(_ button: PaymentButton, changedEligibilityStatus status: PaymentButtonEligibilityStatus) {
// Invoked when the eligibility status of the button has changed
}
}Both properties emit one value at a minimum, as the current value is emitted when either property is set. This is useful because the SDK retrieves funding eligibility requirements as soon as PayPalCheckout.setConfig is invoked. Usually, the funding eligibility retrieves by the time the PaymentButton renders. If funding eligibility isn't retrieved, the buttons are disabled and display a loading state.
You can also retrieve the current status through the eligibilityStatus property. The following are possible values:
| Status | Description |
|---|---|
eligible | The button is visible and enabled, so buyers can interact with it. |
error | The button is disabled and the visibility is hidden. This status only returns if there's an error retrieving the eligibility status. |
ineligible | The button is disabled and the visibility is hidden. |
loading | The button is disabled and the visibility is hidden. |
If you use a custom UI to launch the PayPal Checkout experience, you need to programmatically start the PayPal Mobile Checkout SDK.
Instead of invoking the payment sheet by adding a button with the event closures set on the Checkout type, provide the closures directly to the SDK through Checkout.start.
This sample code creates an order of a single item for $10.00 USD.
func triggerPayPalCheckout() {
Checkout.start(
createOrder: { createOrderAction in
let amount = PurchaseUnit.Amount(currencyCode: .usd, value: "10.00")
let purchaseUnit = PurchaseUnit(amount: amount)
let order = OrderRequest(intent: .capture, purchaseUnits: [purchaseUnit])
createOrderAction.create(order: order)
}, onApprove: { approval in
// Optionally use this closure to respond to the order approval
}, onCancel: {
// Optionally use this closure to respond to the user canceling the paysheet
}, onError: { error in
// Optionally use this closure to respond to the user experiencing an error in
// the payment experience
}
)
}The PaymentButtonContainer is responsible for displaying the payment buttons. This container can display three different buttons: the PayPal button, the PayPal Credit button, and the Pay Later button. By default the PaymentButtonContainer displays the PayPal button.
Note: PaymentButtonContainer does not support PayPal Credit and Pay Later. Those buttons will return as not eligible. No matter how you configure the PaymentButtonContainer, you will only see the PayPal button.
The PaymentButtonContainer provides the callbacks needed to integrate with the native checkout experience: CreateOrder, OnApprove, OnError, OnCancel, and OnShippingChange.
For most integrations, the default PaymentButtonContainer is sufficient. However, if you want greater control over your buttons customization, you can customize the buttons. You can change the payment button's style. This includes the button's label, color, shape, and size. You can also enable or disable the payment button. The payment button will only be shown if the button is enabled and that funding option is eligible for the buyer.
paymentButtonContainer.setPayPalButtonUi(
paypalButtonUi = PayPalButtonUi(
PayPalButtonColor.BLUE,
PayPalButtonLabel.CHECKOUT,
PaymentButtonAttributes(
PaymentButtonShape.ROUNDED,
PaymentButtonSize.LARGE,
isEnabled = true
)
)
)
paymentButtonContainer.setup(
createOrder = CreateOrder { createOrderActions ->
createOrderActions.create(OrderRequest())
},
onApprove = OnApprove { approval ->
Log.d(TAG, "orderId: ${approval.data.orderId}")
},
onError = OnError { errorInfo ->
Log.e(TAG, errorInfo.reason, errorInfo.error)
},
onCancel = OnCancel {
Log.i(TAG, "cancelled")
},
onShippingChange = OnShippingChange { shippingChangeData, shippingChangeActions ->
Log.i(TAG, "Shipping type change: ${shippingChangeData.shippingChangeType}")
}
)You can also observe the view state of the PaymentButtonContainer using the PaymentButtonContainerViewState's onLoading() and onFinish() callbacks.
onLoading and onFinish callbacks are invoked during the network call to check for buyer eligibility.onLoading callback is invoked at the start of the eligibility network call and the PaymentButtonContainer view begins loading.onFinish callback is invoked when the eligibility network call has completed.These callbacks can be implemented like so:
paymentButtonContainer.viewState = PaymentButtonContainerViewState.invoke(
onLoading = {
Log.d(TAG, "Loading")
},
onFinish = { fundingEligibilityState, exception ->
fundingEligibilityState?.let { state ->
state.paymentsButtonMap?.let { map ->
map.entries.forEach {
val paymentFundingType = it.key
val fundingEligibilityItem = it.value
val eligibility = fundingEligibilityItem.eligible
val reasons = fundingEligibilityItem.reasons
val eligibilityStatus = if (eligibility)
"is eligible" else "is not eligible"
Log.d(TAG,
"$paymentFundingType $eligibilityStatus. $reasons"
)
}
}
}
exception?.let { e ->
Log.e(TAG, "Error", e)
}
}
)If the call failed, then the Exception parameter will not be null. If the call succeeded, then the FundingEligibilityState parameter will not be null. The FundingEligibilityStatecontains a map of each PaymentFundingType available. This includes the default PayPal payment funding type as well as PayPal Credit and Pay Later. A PaymentFundingType may not be eligible for the buyer to use. The payment button for the funding type will not be visible if the funding type is not eligible.
PaymentButtonContainer does not support PayPal Credit and Pay Later. Those buttons will return as not eligible.
Note: PayPalButton, PayPalCreditButton, and PayPalPayLaterButtonare deprecated in favor of PaymentButtonContainer. This section is provided as a reference to those using previous versions.
PayPalButton, PayPalCreditButton, and PayLaterButton references with PaymentButtonContainer in your layout.<com.paypal.checkout.paymentbutton.PayPalButton
android:id="@+id/payPalButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_16x"
android:tag="PayPal" />
<com.paypal.checkout.paymentbutton.PayPalCreditButton
android:id="@+id/payPalCreditButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_16x"
android:tag="PayPal Credit" />
<com.paypal.checkout.paymentbutton.PayLaterButton
android:id="@+id/payLaterButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_16x"
android:tag="Pay Later" /><com.paypal.checkout.paymentbutton.PaymentButtonContainer
android:id="@+id/payment_button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:pay_later_button_color="silver"
app:pay_later_button_enabled="true"
app:pay_later_button_shape="pill"
app:pay_later_button_size="medium"
app:paypal_button_color="silver"
app:paypal_button_enabled="true"
app:paypal_button_label="pay"
app:paypal_button_shape="rectangle"
app:paypal_button_size="large"
app:paypal_credit_button_color="black"
app:paypal_credit_button_enabled="true"
app:paypal_credit_button_shape="rounded"
app:paypal_credit_button_size="small" />Instead of setting up each payment button individually, you now only need to call PaymentButtonContainer.setup().
payPalButton.setup()
payLaterButton.setup()
payPalCreditButton.setup()paymentButtonContainer.setup()Instead of observing the eligibilityStatus for a payment button with onEligibilityStatusChanged in Kotlin or paymentButtonEligibilityStatusChangedin Java, use the onLoading() and onFinish() callbacks provided by the PaymentButtonContainerViewState for the PaymentButtonContainer as detailed in the PaymentButtonContainerViewState section above.
The PaymentButton is the parent for all other buttons in the PayPal Mobile Checkout SDK for Android. The following attributes are available for use on every child button:
| Attribute | Property | Description |
|---|---|---|
payment_button_size | size | Adjusts the minimum height of the button. The default value is medium, but can also be set to: large or small. |
payment_button_shape | shape | Overrides the shape of the button. The default shape derives from the theme of your app, but you can set it to: pill, rectangle, or rounded. |
To ensure buyers have an optimal experience, every PaymentButton adheres to strict eligibility requirements. Generally, the PayPalButton is always Eligible so it's safe to fallback for other buttons. The button offers functional and non-functional properties to observe on the status. For example:
val paymentButton: PaymentButton //...
// Functional
paymentButton.onEligibilityStatusChanged = { buttonEligibilityStatus: PaymentButtonEligibilityStatus ->
Log.i("PaymentButtonEligibility", "eligibility: $buttonEligibilityStatus")
}
// Non-functional
paymentButton.paymentButtonEligibilityStatusChanged = object : PaymentButtonEligibilityStatusChanged {
override fun onPaymentButtonEligibilityStatusChanged(paymentButtonEligibilityStatus: PaymentButtonEligibilityStatus) {
Log.i("PaymentButtonEligibility", "$paymentButton : $paymentButtonEligibilityStatus")
}
}Both properties emit one value at a minimum, as the current value is emitted when either property is set. This is useful because the SDK retrieves funding eligibility requirements as soon as PayPalCheckout.setConfig is invoked. Usually, the funding eligibility retrieves by the time the PaymentButton renders. If funding eligibility isn't retrieved, the buttons are disabled and display a loading state.
You can also retrieve the current status through the eligibilityStatus property. The following are possible values:
| Status | Description |
|---|---|
Eligible | The button is visible and enabled, so buyers can interact with it. |
Error | The button is disabled and the visibility is set hidden. This status only returns if there's an error retrieving the eligibility status. |
Ineligible | The button is disabled and the visibility is set to hidden. |
Loading | The button is visible, but disabled and displaying a loading indicator. |
| Attribute | Property | Description |
|---|---|---|
paypal_color | color | Changes the background color of the button and adjusts the wordmark color to be visible. The default value is gold, but you can also set it to: black, blue, silver, or white. |
paypal_label | label | Adds a label on the button itself. The default value is no label, which is set with paypal, but you can also set it to: checkout, buy_now, or pay. |
The PayLaterButton overrides the PayPalButton and always displays the Pay Later branding on the button as a label. You can't modify the label, but you can change the colorto any color available for the PayPalButton.
| Attribute | Property | Description |
|---|---|---|
paypal_credit_color | color | Changes the background color of the button and adjusts the wordmark to be visible. You can set it to dark_blue or black. |
If you use a custom UI to launch the PayPal Checkout experience, you need to programmatically start the PayPal Mobile Checkout SDK.
Register all callbacks to the onCreate() method of your activity (or to the onAttach()method of your fragment). This enables you to listen to certain key events in the checkout process, such as order approvals, orders cancellations, or errors.
Note: If the activity is destroyed or re-created, you must register the callbacks again.
class YourActivity: Activity {
override fun onCreate(savedInstanceState: Bundle?) {
PayPalCheckout.registerCallbacks(
onApprove = OnApprove { approval ->
// Optional callback for when an order is approved
},
onCancel = OnCancel {
// Optional callback for when a buyer cancels the paysheet
},
onError = OnError { errorInfo ->
// Optional error callback
},
onShippingChange = OnShippingChange { shippingChangeData, shippingChangeActions ->
// Optional onShippingChange callback.
}
)
setupButtons()
}
}PayPalCheckout.startCheckout(createOrderActions -> {}); when users select your custom UI button.This sample code creates an order of a single item for $10.00 USD.
PayPalCheckout.startCheckout(
CreateOrder { createOrderActions ->
val order = OrderRequest(
intent = OrderIntent.CAPTURE,
appContext = AppContext(
userAction = UserAction.PAY_NOW
),
purchaseUnitList = listOf(
PurchaseUnit(
amount = Amount(
currencyCode = CurrencyCode.USD,
value = "10.00"
)
)
)
)
createOrderActions.create(order)
}
)Register all callbacks in the ViewModel constructor.
Note: Don't reference UI objects in your callbacks that might get destroyed by Android.
class MyViewModel: ViewModel() {
val _message = MutableLiveData<String>()
val message: LiveData<String>
get() =_message
init {
PayPalCheckout.registerCallbacks(
onApprove = OnApprove { approval ->
_message.value = "Order approved"
},
onCancel = OnCancel {
_message.value = "Order cancelled"
},
onError = OnError { errorInfo ->
_message.value = "Error creating order"
}
)
}
}Our example receives a list of items to add to the order.
class MyViewModel: ViewModel() {
...
fun startCheckout(items: List<PurchaseUnit>) {
PayPalCheckout.startCheckout(
CreateOrder { createOrderActions ->
val order = OrderRequest(
intent = OrderIntent.CAPTURE,
appContext = AppContext(
userAction = UserAction.PAY_NOW
),
purchaseUnitList = items
)
createOrderActions.create(order)
}
)
}
}