One-time Payments
Pay with PayPal's One-time Payments is a one-click solution that accelerates your buyer's checkout experience by skipping manual data entry. Buyers can use PayPal to check out anywhere in their shopping journey. Placing payment buttons on the cart, product details page, or another page as a checkout shortcut can reduce steps to pay.
Setup
Get the SDK
Add the following in your app-level build.gradle:
- Kotlin
- Groovy
dependencies {
implementation("com.braintreepayments.api:paypal:5.8.0")
}
Invoking the One-time Payments flow
Initialization
Create a PayPalLauncher inside of your Activity's onCreate(). Then, create a PayPalClient with a Tokenization Key or Client Token and an appLinkReturnUrl that is used to return to your app from the PayPal payment flows.
- Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// PayPalLauncher must be initialized in onCreate
payPalLauncher = PayPalLauncher()
// can initialize the PayPalClient outside of onCreate if desired
payPalClient = PayPalClient(
context = this,
authorization = "[TOKENIZATION_KEY or CLIENT_TOKEN]",
appLinkReturnUrl = Uri.parse("https://merchant-app.com"), // Merchant App Link
"com.merchant.app.deeplink.braintree" // Fallback deep link return url scheme
)
Request a payment
After the user clicks the PayPal button, create a PayPalCheckoutRequest and call PayPalClient.createPaymentAuthRequest() and handle its result. If the result is Failure, handle the error. If the result is ReadyToLaunch, invoke PayPalLauncher.launch() and check the returned PayPalPendingRequest.
If Started is returned, the PayPal flow was launched and the Started.pendingRequestString needs to be persisted. If Failure was returned, handle the error.
- Kotlin
private fun onPayPalButtonClick() {
val request = PayPalCheckoutRequest(
amount = "12.34",
hasUserLocationConsent = true
)
payPalClient.createPaymentAuthRequest(this, request) { paymentAuthRequest ->
when (paymentAuthRequest) {
is PayPalPaymentAuthRequest.ReadyToLaunch -> {
val pendingRequest = payPalLauncher.launch(this, paymentAuthRequest)
when (pendingRequest) {
is PayPalPendingRequest.Started -> {/* store pending request */ }
is PayPalPendingRequest.Failure -> { /* handle error */ }
}
}
is PayPalPaymentAuthRequest.Failure -> { /* handle paymentAuthRequest.error */ }
}
}
}
Handle the payment result
Once the user completes or cancels the PayPal Checkout flow the Intent needs to be handled. If your Activity's launch mode is SINGLE_TOP, the Intent needs to be handled in onNewIntent(). For all other launch modes, handle the Intent in onResume().
Call PayPalLauncher.handleReturnToApp() and pass in the persisted pendingRequestString and Intent. Handle the returned PayPalPaymentAuthResult.
If NoResult is returned, the user has canceled the payment flow or returned to the app without completing the payPal flow. If Failure is returned, handle the error. If Success is returned, pass the Success object to PayPalClient.tokenize().
The PayPalClient.tokenize() function takes a PayPalTokenizeCallback which returns a PayPalResult. When the result is Cancel, the user canceled the flow. When the result is Failure, handle the error. When the result is Success, a single-use token value is returned inside of PayPalAccountNonce.
You can query the PayPalAccountNonce result for specific customer information if needed.
- Kotlin
// ONLY REQUIRED IF YOUR ACTIVITY LAUNCH MODE IS SINGLE_TOP
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleReturnToApp(intent)
}
// ALL OTHER ACTIVITY LAUNCH MODES
override fun onResume() {
super.onResume()
handleReturnToApp(intent)
}
private fun handleReturnToApp(intent: Intent) {
// fetch stored PayPalPendingRequest.Success
val pendingRequest: String = fetchPendingRequestFromPersistantStore()
when (val paymentAuthResult = payPalLauncher.handleReturnToApp(
pendingRequest = PayPalPendingRequest.Started(pendingRequest),
intent = intent
)) {
is PayPalPaymentAuthResult.Success -> {
completePayPalFlow(paymentAuthResult)
// clear stored PayPalPendingRequest.Success
// clear the intent data
intent.data = null
}
is PayPalPaymentAuthResult.NoResult -> {
// user returned to app without completing PayPal flow, handle accordingly
}
is PayPalPaymentAuthResult.Failure -> {
// handle error case
}
}
}
private fun completePayPalFlow(paymentAuthResult: PayPalPaymentAuthResult.Success) {
payPalClient.tokenize(paymentAuthResult) { result ->
when (result) {
is PayPalResult.Success -> { /* handle result.nonce */ }
is PayPalResult.Failure -> { /* handle result.error */ }
is PayPalResult.Cancel -> { /* handle user canceled */ }
}
}
}
Complete integration
- Kotlin
class MyActivity : AppCompatActivity() {
private lateinit var payPalLauncher: PayPalLauncher
private lateinit var payPalClient: PayPalClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// PayPalLauncher must be initialized in onCreate
payPalLauncher = PayPalLauncher()
// can initialize the PayPalClient outside of onCreate if desired
payPalClient = PayPalClient(
context = this,
authorization = "[TOKENIZATION_KEY or CLIENT_TOKEN]",
appLinkReturnUrl = Uri.parse("https://merchant-app.com"), // Merchant App Link
"com.merchant.app.deeplink.braintree" // Fallback deep link return url scheme
)
}
// ONLY REQUIRED IF YOUR ACTIVITY LAUNCH MODE IS SINGLE_TOP
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleReturnToApp(intent)
}
// ALL OTHER ACTIVITY LAUNCH MODES
override fun onResume() {
super.onResume()
handleReturnToApp(intent)
}
private fun handleReturnToApp(intent: Intent) {
// fetch stored PayPalPendingRequest.Success
val pendingRequest: String = fetchPendingRequestFromPersistantStore()
when (val paymentAuthResult = payPalLauncher.handleReturnToApp(
pendingRequest = PayPalPendingRequest.Started(pendingRequest),
intent = intent
)) {
is PayPalPaymentAuthResult.Success -> {
completePayPalFlow(paymentAuthResult)
// clear stored PayPalPendingRequest.Success
}
is PayPalPaymentAuthResult.NoResult -> {
// user returned to app without completing PayPal flow, handle accordingly
}
is PayPalPaymentAuthResult.Failure -> {
// handle error case
}
}
}
private fun completePayPalFlow(paymentAuthResult: PayPalPaymentAuthResult.Success) {
payPalClient.tokenize(paymentAuthResult) { result ->
when (result) {
is PayPalResult.Success -> { /* handle result.nonce */ }
is PayPalResult.Failure -> { /* handle result.error */ }
is PayPalResult.Cancel -> { /* handle user canceled */ }
}
}
}
private fun onPayPalButtonClick() {
val request = PayPalCheckoutRequest(
amount = "12.34",
hasUserLocationConsent = true
)
payPalClient.createPaymentAuthRequest(this, request) { paymentAuthRequest ->
when (paymentAuthRequest) {
is PayPalPaymentAuthRequest.ReadyToLaunch -> {
val pendingRequest = payPalLauncher.launch(this, paymentAuthRequest)
when (pendingRequest) {
is PayPalPendingRequest.Started -> {/* store pending request */ }
is PayPalPendingRequest.Failure -> { /* handle error */ }
}
}
is PayPalPaymentAuthRequest.Failure -> { /* handle paymentAuthRequest.error */ }
}
}
}
}
Customizing One-time Payments
We can customize One-time Payments with the following features:
- Contact Module
- Shipping Module
- Pass Buyer Identifier
- Pass Line-item Details
- Pay Now or Continue
- App Switch
Integrating Contact Module
The Contact Module helps buyers to view and update the email address and phone number shared with merchants for a specific order. This feature gives buyers more control and flexibility, especially useful for gift orders, where alternate contact details may be needed.
Buyer benefits
The Contact Module offers buyers a personalized and convenient experience by allowing them to customize contact details for each transaction without updating information outside the checkout process.
This feature is currently available in the US only.
Merchant benefits
The Contact Module offers buyers a personalized and convenient experience by allowing them to customize contact details for each transaction without updating information outside the checkout process.
- Hide contact info: This setting is enabled by default. Buyers do not see any contact information during checkout.
- To hide contact information from buyers during checkout, a merchant can either set PayPalContactPreference.NO_CONTACT_INFORMATION or omit the request.contactPreference field.
- Display editable contact info: Buyers can see and edit their contact information.
- To allow buyers to edit their contact information during checkout, the merchant can set PayPalContactPreference.UPDATE_CONTACT_INFO.
- When this option is enabled, buyers can update their email addresses and phone numbers in PayPal. Merchants will then receive the most current contact information and should use this updated data for all communications with buyers.
- Buyers cannot set preferences for email and phone numbers individually; both can be edited at the same time when this option is activated.
- Display read only contact info: Buyers can see their contact information but can't edit it.
- To display contact information to buyers without allowing edits, the merchant should use the PayPalContactPreference.RETAIN_CONTACT_INFO setting.
- If only one contact field—either email or phone—is provided, PayPal will display just that field and assume it's the primary method of communication.
- If PayPalContactPreference.RETAIN_CONTACT_INFO is set without contact details, PayPal defaults to PayPalContactPreference.NO_CONTACT_INFO and hides all contact information from the buyer.
To integrate the Contact Module, use the code provided below
- kotlin
val request = PayPalCheckoutRequest()
request.contactInformation = PayPalContactInformation("[email protected]", PayPalPhoneNumber("1", "1234567890"))
request.contactPreference = PayPalContactPreference.RETAIN_CONTACT_INFORMATION
Integrating Shipping Module
The PayPal shipping module presents shipping details to a buyer during the PayPal flow. The merchant has several ways to determine how shipping addresses and shipping options are handled. The server-side shipping callbacks allow you to update the shipping and order amount information as buyers make changes on the PayPal review page.
Buyers can use the shipping module to specify the shipping address and shipping options on the PayPal paysheet. PayPal sends a callback to the merchant's URL with the updated shipping information (buyer’s address, state, city, country code, and zip code) using the server-side shipping callbacks. In response, the merchant can send PayPal the shipping options and updated order cost amounts.
To include Shipping Module in your integration, set the server side shipping callback URL as shown below. For more information, see the Integration code samples section, below.
- Kotlin
request.shippingCallbackUrl = Uri.parse("merchant-url.com")
Integrating Pass Line-item Details
The items a buyer purchases can be passed to PayPal through a request.lineItems request. When a buyer checks out their purchase, PayPal displays these invoice-line-item details (item name, quantity, detailed description, price, etc.) for buyer verification. The details passed to PayPal are presented to the buyer:
- On the PayPal review page during the Pay with PayPal flow.
- In the post-purchase email sent to the buyer about their payment transaction.
- In the buyer's PayPal account Activity > Transactions > All transactions section.
To integrate Pass Line-item Details, set the code as shown below, and for more information, see the Integration code samples section
- kotlin
request.lineItems = listOf(PayPalLineItem(PayPalLineItemKind.CREDIT, "item name", "1", "1.99"))
Integrating Pass Buyer Identifier
A buyer's email address can be passed as the buyer identifier to PayPal through a PayPal request. During one-time checkout, PayPal uses this email address to prefill the buyer's PayPal login page. This streamlines and quickens the buyer authentication process for an effortless login. When you run PayPal in your app, as part of the request body, send the buyer's email address in the PayPalRequest.userAuthenticationEmail field. After a successful request, the passed email address is used to pre-populate the PayPal login page when the buyer pays with PayPal.
To include Pass Buyer Identifier in your integration, set the code as shown below. For more information, see the Integration code samples section below.
- kotlin
request.userAuthenticationEmail = "[email protected]"
request.userPhoneNumber = PayPalPhoneNumber("1", "1234567890")
Integrating Pay Now or Continue
You can set User Action to control the message on the PayPal button at the bottom of the PayPal review page. There are two messaging options:
- Pay Now: The payer will complete the transaction on the PayPal review page.
- Continue: The payer will return to the merchant site to complete the transaction.
Pay Now
Use Pay Now for most PayPal flows. Pay Now streamlines checkout by using the payer's PayPal account information. For upstream placements (the button appears on the product or cart page), use the Pay Now User Action with the Shipping and Contact Modules to help the payer select shipping and payment details on the PayPal review page. For checkout presentment (button appears at checkout), payers can use PayPal to skip entering payment information. When a payer completes a Pay Now flow, they are returned to the merchant site. There, they will see a confirmation page with details about the transaction.
Continue
The Continue setting indicates that the payer will return to the merchant site to complete a transaction. Use this flow if the final amount will change after the payer returns to the merchant site. When the payer returns to the merchant site, they are presented with no more than one additional page to complete the transaction. When they complete a transaction, they see a confirmation page.
To integrate Pay Now or Continue, see the Integration code samples section below.
- kotlin
request.userAction = PayPalPaymentUserAction.USER_ACTION_COMMIT
Integrating App Switch
The App Switch initiative will enable PayPal users who have a PayPal installed on their phone to complete payments on the PayPal app when it is available.
In this experience the SDK will attempt to switch to the PayPal App after calling PayPalLauncher().launch if the PayPal App is installed and the user meets eligibility requirements. If the switch into the PayPal App cannot be completed, we will fall back to the default browser experience.
Key Benefits
- Faster checkout: Customers can complete payments without re-entering login credentials.
- Improved mobile experience: The native app interface provides smoother interaction than web-based flows.
- Higher conversion rates: Reduced friction leads to fewer abandoned transactions.
- Seamless fallback: Automatically falls back to standard web checkout when the app isn’t available.
Ready to implement App Switch? Continue to the App Switch Integration Guide for detailed implementation steps, code examples, and important considerations for your checkout flow.
Shipping address
Shipping addresses may or may not be collected during the Checkout with PayPal flow. However, if you choose to collect shipping addresses yourself, they can be passed along with the your server side Transaction.Sale
call. Look at the Server-side page for more information.
Country support
PayPal is available to merchants in all countries that we support and to customers in 140+ countries.
Currency presentment
The currency of the transaction is presented to the customer in the Checkout with PayPal flow. We support all currencies that PayPal REST APIs support.
See the server-side section for details on charging a transaction in a specific currency.