On this page
No Headings
Last updated: July 7, 2026
Accept PayPal, credit, and debit card payments in a web or native experience using the PayPal Mobile Android SDK. Use customizable PayPal buttons with your custom checkout UI to align with your business branding. For more implementation details, see the PayPal GitHub repository.
You need a developer account to get sandbox credentials:
Read Get started with PayPal APIs for more information.
You need a combination of PayPal and third-party tools:
Use Postman to explore and test PayPal APIs.
This integration requires a sandbox business account with the Advanced Credit and Debit Card Payments capability. Your account should automatically have this capability.
To confirm that Advanced Credit and Debit Card Payments are enabled for you, check your sandbox business account as follows:
Note: If you created a sandbox business account through sandbox.paypal.com, and the advanced credit and debit card payments status for the account is disabled, complete the sandbox onboarding steps.
Add 3D Secure to reduce the chance of fraud and improve the payment experience by authenticating a cardholder through their card issuer.
Visit our 3D Secure page to see if 3D Secure is required in your region and learn more about implementing 3DS in your app.
The PayPal Mobile SDK is available through Maven Central. Add the mavenCentral repository to the build.gradle file of your project root:
allprojects {
repositories {
mavenCentral()
}
}You can also use snapshot builds to test upcoming features before release. To include a snapshot build:
Add the snapshots repository to the build.gradle file of your project root.
allprojects {
repositories {
mavenCentral()
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots/'
}
}
}Then, add a snapshot build by adding -SNAPSHOT to the current dependency version. For example, if you want to add a snapshot build for CardPayments, add the following:
dependencies {
implementation 'com.paypal.android:card-payments:CURRENT-VERSION-SNAPSHOT'
}Integrate 3 different types of payments using the PayPal Mobile SDK:
Build and customize the card fields to align with your branding.
Add the card-payments package dependency in your app's build.gradle file:
dependencies {
implementation "com.paypal.android:card-payments:CURRENT-VERSION"
}A CardClient helps you attach a card to a payment.
In your Android app:
CLIENT_ID to construct a CoreConfig.CardClient using your CoreConfig object.val config = CoreConfig("CLIENT_ID", environment = Environment.SANDBOX)
val cardClient = CardClient(config)On your server:
Create an ORDER_ID by using the Orders v2 API.
Pass your ACCESS_TOKEN in the Authorization header. To get an ACCESS_TOKEN, use the Authentication API.
Note: This access token is only for the sandbox environment. When you're ready to go live, request a live access token by changing the request sandbox endpoint to https://api-m.paypal.com/v1/oauth2/token.
Pass the intent. You'll need to pass either AUTHORIZE or CAPTURE as the intent type. This type must match the /authorize or /capture endpoint you use to process your order.
curl --location --request POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw '{
"intent": "CAPTURE|AUTHORIZE",
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": "5.00"
}
}
]
}'When a buyer starts a payment, send the ORDER_ID from your server to your client app.
A CardRequest object:
ORDER_ID.Build a card object with the buyer's card details:
val card = Card(
number = "4005519200000004",
expirationMonth = "01",
expirationYear = "2025",
securityCode = "123",
billingAddress = Address(
streetAddress = "123 Main St.",
extendedAddress = "Apt. 1A",
locality = "Anytown",
region = "CA",
postalCode = "12345",
countryCode = "US"
)
)Collecting a billing address can reduce the number of authentication challenges to customers.
Build a CardRequest with the card object and your ORDER_ID:
val cardRequest = CardRequest(
orderID = "ORDER_ID",
card = card,
returnUrl = "myapp://return_url", // custom URL scheme needs to be configured in AndroidManifest.xml
sca = SCA.SCA_ALWAYS // default value is SCA.SCA_WHEN_REQUIRED
)3D Secure is supported for all card payments to comply with the Second Payment Services Directive (PSD2). PSD2 is a European Union regulation that introduces Strong Customer Authentication (SCA) and other security requirements.
Select your SCA launch option type using the sca parameter in the CardRequest initializer:
SCA.SCA_WHEN_REQUIRED launches an SCA challenge when applicable. This is enabled by default.SCA.SCA_ALWAYS requires an SCA challenge for all card transactions.The sca challenge launches in a browser within your application. Your app needs to handle the browser switch between the sca challenge and the checkout page. Set up a return URL that returns to your app from the browser.
Provide a returnUrl so the browser returns to your application after the sca challenge finishes.
The returnUrl should have the following format:
myapp://return_urlThe myapp:// portion of the returnUrl is a custom URL scheme that you need to register in your app's AndroidManifest.xml.
Update your app's AndroidManifest.xml with details about the card payment activity that will return the user to your app after completing the SCA check. Include the following elements:
launchMode to singleTop.android:scheme on the Activity that will be responsible for handling the deep link back into the app.intent-filter.myapp:// custom URL scheme in the intent-filter.Note: android:exported is required if your app compile SDK version is API 31 (Android 12) or later.
<activity
android:name=".MyCardPaymentActivity"
android:launchMode="singleTop"
android:exported="true">
<intent-filter&g;
&l;action android:name="android.intent.action.VIEW" />
<data android:scheme="myapp" />
<category android:name="android.intent.category.DEFAULT" />;
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>Add onNewIntent to your activity:
override fun onNewIntent(newIntent: Intent?) {
super.onNewIntent(intent)
intent = newIntent
}After your CardRequest has the card details, call cardClient.approveOrder() to process the payment.
class MyCardPaymentActivity: FragmentActivity {
fun cardCheckoutTapped(cardRequest: CardRequest) {
cardClient.approveOrder(this, cardRequest)
}
}Set up your ApproveOrderListener to handle successful payments, errors, cancellations, and 3D Secure transaction flows.
class MyCardPaymentActivity: FragmentActivity, ApproveOrderListener {
fun cardCheckoutTapped(cardRequest: CardRequest) {
val result = cardClient.approveOrder(this, cardRequest)
}
fun setupCardClient() {
cardClient.listener = this
}
fun onApproveOrderSuccess(result: CardResult) {
// order was approved and is ready to be captured/authorized (see step 6)
}
fun onApproveOrderFailure(error: PayPalSDKError) {
// inspect 'error' for more information
}
fun onApproveOrderCanceled() {
// 3D Secure flow was canceled
}
fun onApproveOrderThreeDSecureWillLaunch() {
// 3D Secure flow will launch
}
fun onApproveOrderThreeDSecureDidFinish() {
// 3D Secure auth did finish successfully
}
}Submit your ORDER_ID for authorization or capture when the PayPal Android SDK calls the onApproveOrderSuccess method.
Call the authorize endpoint of the Orders V2 API to place the money on hold:
curl --location --request POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/ORDER_ID/authorize' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw ''Call the capture endpoint of the Orders V2 API to capture the money immediately:
curl --location --request POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/ORDER_ID/capture' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw ''Before going live, test your integration in the sandbox environment.
Learn more about the following resources on the Card Testing page:
Note: Use the credit card generator to generate additional test credit cards for sandbox testing.
When prompted for required data for the sandbox business request, such as a phone number, enter any number that fits the required format. Because this is a sandbox request, the data doesn't have to be factual.
Before you go live, you'll need to complete live onboarding to be eligible to process cards with your live PayPal account.
Deprecation notice: The PayPalNativePayments module is deprecated since July 2024. Updates and bug fixes end in July 2025. We recommend to use the PayPalWebPayments module instead. To migrate from PayPalNativePayments to PayPalWebPayments, follow the steps in Web payments.
Use PayPalNativePayments to add a PayPal paysheet to your app. The paysheet is a checkout page that the client launches within your app, instead of showing up as a pop-up. The paysheet shows all the PayPal payment methods available and helps the payer complete their payment.
Follow these steps to add PayPalNativePayments:
Add the paypal-native-payments package dependency in your app's build.gradle file:
dependencies {
implementation "com.paypal.android:paypal-native-payments:CURRENT-VERSION"
}Add the following Cardinal SDK Maven repository to your project's top-level build.gradle file to pass credentials to Gradle:
allprojects {
repositories {
maven {
url "https://cardinalcommerceprod.jfrog.io/artifactory/android"
credentials {
username "paypal_sgerritz"
password "YOUR_JFROG_API_KEY"
}
}
}
}You'll need to set up authorization to use the Native Checkout SDK. To create a client ID and secret, follow the steps in Get Started. You need these values to generate an ACCESS_TOKEN.
Set up your sandbox business account to use native checkout as follows:
A return URL redirects users to the app after authenticating. Use the following steps to create a return URL:
BuildConfig.APPLICATION_ID) and register your return URL with ://paypalpay as the suffix. For example, if your application ID is com.paypal.app, input com.paypal.app://paypalpay as the return URL.Note: If you change the return URL in the Developer Dashboard, PayPal must review your app again. The review period automatically begins any time the return URL changes.
Use the following steps to set up the PayPal Native Checkout client for your app:
In your Android app, use the CLIENT_ID to construct a CoreConfig.
val coreConfig = CoreConfig("CLIENT_ID", environment = Environment.SANDBOX)Create a PayPalNativeCheckoutClient request to approve an order with a PayPal payment method and include the RETURN_URL:
val payPalNativeClient = PayPalNativeCheckoutClient(
application = requireActvitiy().application,
coreConfig = coreConfig,
returnUrl = "RETURN_URL"
)Implement the PayPalNativeCheckoutListener on the PayPalNativeCheckoutClient to listen for result notifications from the SDK.
payPalNativeClient.listener = object : PayPalNativeCheckoutListener {
override fun onPayPalCheckoutStart() {
// the PayPal paysheet is about to show up
}
override fun onPayPalCheckoutSuccess(result: PayPalNativeCheckoutResult) {
// order was approved and is ready to be captured/authorized
}
override fun onPayPalCheckoutFailure(error: PayPalSDKError) {
// handle the error
}
override fun onPayPalCheckoutCanceled() {
// the user canceled the flow
}
}When a payer chooses to use shipping details from their PayPal profile, use PayPalNativeShippingListener to listen for changes to their shipping address or shipping method.
You can only implement PayPalNativeShippingListener if the shipping_preference in the order ID is set to GET_FROM_FILE.
Note: Skip this step if you created your order ID with the shipping_preference set to NO_SHIPPING or SET_PROVIDED_ADDRESS.
Set a shippingListener on the PayPalNativeCheckoutClient to send notifications to your app when the user updates their shipping address or shipping method.
This code sample uses a try…catch function to handle the response:
payPalNativeClient.shippingListener = object : PayPalNativeShippingListener {
override fun onPayPalNativeShippingAddressChange(
actions: PayPalNativePaysheetActions,
shippingAddress: PayPalNativeShippingAddress
) {
// called when the user updates their chosen shipping address
// you must call actions.approve() or actions.reject() in this callback
actions.approve()
// OPTIONAL: you can optionally patch your order. Once complete, call actions.approve() if successful or actions.reject() if not.
}
override fun onPayPalNativeShippingMethodChange(
actions: PayPalNativePaysheetActions,
shippingMethod: PayPalNativeShippingMethod
) {
// called when the user updates their chosen shipping method
// patch your order server-side with the updated shipping amount.
// Once complete, call `actions.approve()` or `actions.reject()`
try {
// TODO: patch order on server side, notify SDK of success by calling actions.approve()
actions.approve()
} catch (e: Exception) {
// catch any errors from patching the order e.g. network unavailable
// and notify SDK that the update was unsuccessful
actions.reject()
}
}
}When the shipping method changes, update the order details on your server by sending a PATCH request to the Update order endpoint of the Orders API.
Approve or reject changes to the shipping information to see changes appear in the paysheet UI by calling either PayPalNativePaysheetActions.approve() or PayPalNativePaysheetActions.reject().
To update the paysheet with a new shipping method, or when onPayPalNativeShippingMethodChange is called:
replace operation with all shipping methods.
approve() or reject() to accept or reject the changes and continue the payment flow.For more information, visit the Update order endpoint of the Orders v2 API.
On your server:
Create an ORDER_ID by using the Orders v2 API.
Pass your ACCESS_TOKEN in the Authorization header. To get an ACCESS_TOKEN, use the Authentication API.
Note: This access token is only for the sandbox environment. When you're ready to go live, request a live access token by changing the request sandbox endpoint to https://api-m.paypal.com/v1/oauth2/token.
Pass the intent. You'll need to pass either AUTHORIZE or CAPTURE as the intent type. This type must match the /authorize or /capture endpoint you use to process your order.
curl --location --request POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw '{
"intent": "CAPTURE|AUTHORIZE",
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": "5.00"
}
}
]
}'When a buyer starts a payment, send the ORDER_ID from your server to your client app.
To start the PayPal Native Payments flow, call the startCheckout function in PayPalNativeCheckoutClient, with a PayPalNativeCheckoutRequest:
val request = PayPalNativeCheckoutRequest("ORDER_ID")
paypalNativeClient.startCheckout(request)Submit your ORDER_ID for authorization or capture when the PayPal Android SDK calls the onPayPalSuccess method.
Call the authorize endpoint of the Orders V2 API to place the money on hold:
curl --location --request POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/ORDER_ID/authorize' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw ''Call the capture endpoint of the Orders V2 API to capture the money immediately:
curl --location --request POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/ORDER_ID/capture' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw ''Integrate PayPalWebPayments to add a lighter checkout integration to your app. The checkout experience is launched in a browser within your application, reducing the size of the SDK.
Follow these steps to integrate PayPalWebPayments:
Add the paypal-web-payments package dependency in your app's build.gradle file:
dependencies {
implementation "com.paypal.android:paypal-web-payments:CURRENT-VERSION"
}PayPalWebPayments launches a checkout page in a browser within your application. Your app needs to handle the browser switch between the checkout page and your app. Set up a return URL that returns to your app from the browser.
Update your app's AndroidManifest.xml with details about the card payment activity that will return the user to your app after completing the payment. Include the following elements:
launchMode to singleTop.android:scheme on the Activity that will be responsible for handling the deep link back into the app.intent-filter.myapp:// custom URL scheme in the intent-filter.Note: android:exported is required if your app compile SDK version is API 31 (Android 12) or later.
<activity android:name="com.company.app.MyPaymentsActivity" android:exported="true" android:launchmode="singleTop">
...
<intent-filter>
<action android:name="android.intent.action.VIEW">
<data android:scheme="custom-url-scheme">
<category android:name="android.intent.category.DEFAULT">
<category android:name="android.intent.category.BROWSABLE">
</category></category></data></action></intent-filter>
</activity>Also, add onNewIntent to the host activity in your app:
override fun onNewIntent(newIntent: Intent?) {
super.onNewIntent(intent)
intent = newIntent
}Use the following steps to set up the PayPal Native Checkout client for your app:
In your Android app, use the CLIENT_ID to construct a CoreConfig.
val config = CoreConfig("CLIENT_ID", environment = Environment.SANDBOX)Set a return URL using the custom scheme you configured in the ActivityManifest.xml:
val returnUrl = "custom-url-scheme"Create a PayPalWebCheckoutClient to approve an order with a PayPal payment method:
val payPalWebCheckoutClient = PayPalWebCheckoutClient(requireActivity(), config, returnUrl)Set a PayPalWebCheckoutListener on the client to receive payment flow callbacks:
payPalWebCheckoutClient.listener = object : PayPalWebCheckoutListener {
override fun onPayPalWebSuccess(result: PayPalWebCheckoutResult) {
// order was approved and is ready to be captured/authorized (see step 7)
}
override fun onPayPalWebFailure(error: PayPalSDKError) {
// handle the error
}
override fun onPayPalWebCanceled() {
// the user canceled the flow
}
}Note: Collecting a billing address can reduce the number of authentication challenges to customers.
On your server:
Create an ORDER_ID by using the Orders v2 API.
Pass your ACCESS_TOKEN in the Authorization header. To get an ACCESS_TOKEN, use the Authentication API.
Note: This access token is only for the sandbox environment. When you're ready to go live, request a live access token by changing the request sandbox endpoint to https://api-m.paypal.com/v1/oauth2/token.
Pass the intent. You'll need to pass either AUTHORIZE or CAPTURE as the intent type. This type must match the /authorize or /capture endpoint you use to process your order.
curl --location --request POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw '{
"intent": "CAPTURE|AUTHORIZE",
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": "5.00"
}
}
]
}'When a buyer starts a payment, send the ORDER_ID from your server to your client app.
Configure your PayPalWebCheckoutRequest with the ORDER_ID. You can also specify one of the following funding sources for your order: PayPal (default), PayLater, or PayPalCredit.
val payPalWebCheckoutRequest = PayPalWebCheckoutRequest("ORDER_ID", fundingSource = PayPalWebCheckoutFundingSource.PAYPAL)Call payPalWebCheckoutClient.start() to process the payment.
class MyCardPaymentActivity: FragmentActivity {
fun payPalWebCheckoutTapped(payPalWebCheckoutRequest: PayPalWebCheckoutRequest) {
payPalWebCheckoutClient.start(payPalWebCheckoutRequest)
}
}Submit your ORDER_ID for authorization or capture when the PayPal Android SDK calls the onPayPalWebSuccess method on PayPalWebCheckoutListener.
Call the authorize endpoint of the Orders V2 API to place the money on hold:
curl --location --request POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/ORDER_ID/authorize' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw ''Call the capture endpoint of the Orders V2 API to capture the money immediately:
curl --location --request POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/ORDER_ID/capture' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS_TOKEN' \
--data-raw ''After you integrate a payment method, add a payment button to your page to start the payment process. You can also add fraud protection to your app.
The PaymentButtons module provides a set of PayPal-branded buttons to seamlessly integrate with PayPal web and native payments.
Follow these steps to add PayPal buttons to your integration:
Add the payment-buttons package dependency in your app's build.gradle file:
dependencies {
implementation "com.paypal.android:payment-buttons:CURRENT-VERSION"
}The PaymentButtons module provides 3 buttons that you can use in your application:
PayPalButton: A generic PayPal button.PayPalPayLater: A PayPal button with a PayLater label.PayPalCredit: A PayPal button with the PayPalCredit logo.These buttons include customization options such as color, edges, size, and labels. Here's how to style the button corner radius:
| Value | Description | Button |
|---|---|---|
rectangle | Button shape with sharp corners. | |
rounded | Recommended Button shape with rounded corner radius. The default button shape. | |
pill | Button in pill shape. | |
customCornerRadius | Customize the button's corner radius. The minimum value is 10 px and is applied to all 4 corners. |
Add PayPalButton to your layout XML:
<com.paypal.android.paymentbuttons.PayPalButton
android:id="@+id/paypal_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>Add the PayPal button to your code:
val payPalButton = findViewById<PayPalButton>(R.id.paypal_button)
payPalButton.setOnClickListener {
// start the PayPal web or native
}If you have fulfilled the requirements for accepting Advanced Credit and Debit Card Payments for your business account, review the Move your app to production page to learn how to test and go live. If this is your first time testing in a live environment, follow these steps:
Important: The code for the integration checks eligibility requirements, so the payment card fields only display when the production request is successful.