Skip to main content

PayPal

PayPal is self-submitting: its branded button is the Pay button. The tap creates the PayPal order, opens the approval in the PayPal app or a Custom Tab, and the return runs /validate, which captures.

Setup​

Artifacteu.purse:headless-checkout-paypal (brings the PayPal Android SDK)
DashboardPayPal wallet activated on the entity, in popup integration mode, with the merchant's PayPal REST client id. The client id reaches the app inside the client session
ManifestNothing. The return Activity and its deep link, scheme ${applicationId}.paypal, ship in the artifact
KeysNone in the app
EnvironmentPurseEnvironment.PRODUCTION maps to PayPal live; every other environment to the PayPal sandbox
// app/build.gradle.kts
dependencies {
implementation("eu.purse:headless-checkout-paypal:${SDK_VERSION}")
}

A PayPal method configured in redirection mode is not driven by this artifact: it renders as a row and pays through the PSP page, see Redirection methods.

Render​

Render the element as soon as the method is listed. Never call setPrimary for it, and never place a Pay button of yours next to it.

val element = rememberPaymentElement(checkout, paypalMethod, listener)
PaymentElementUi(element, Modifier.fillMaxWidth().height(56.dp)) // the branded button is the submit

Creating the element costs nothing: /init, which creates the PayPal order, runs on the tap, so a button the shopper never uses leaves no orphan order behind. element.ready is true immediately.

Outcomes​

OutcomeWhen
CompletedThe shopper approved in PayPal and /validate captured. status is the API's verdict
ReturnedFromRedirect/validate chained a further step (rare) and the shopper came back. Not an outcome. Show a processing state; your backend confirms the payment
CanceledThe shopper dismissed PayPal without approving
Failed/init failed, the approval could not open, or /validate threw

onRequiresAction() fires when the approval is being opened: a good moment to show that the shopper is leaving the app.

Saved PayPal accounts​

A PayPal account the shopper vaulted earlier arrives on paymentTokens as a PaymentToken with isVaultedCard == false. It has nothing to approve and no CVV: select it with setPrimaryToken, its element has no UI, and submit() captures directly. See Saved cards and accounts. Creating such a token from the app (vaulting) is not available in this SDK today.

Not available on Android

PayPal Pay Later (BNPL) and vaulting are not built. A BNPL method configured in redirection mode works through the redirection guide.

Troubleshooting​

SymptomCause
Failed with API_ERROR, message mentions merchant_specifics.client_idThe session offers PayPal without a REST client id. Configure it in the dashboard
Failed with API_ERROR, message mentions no order_id/init did not create a PayPal order. Check the method's configuration
Canceled right after PayPal opensThe shopper dismissed the Custom Tab or the PayPal app
PayPal opens but never returns to the appThe artifact's return Activity was overridden in your manifest. Remove the override
The button never appears, the method shows as a rowPayPal is in redirection mode on this entity
The method is listed but has no UI, submit() fails with METHOD_NOT_IMPLEMENTEDThe -paypal artifact is missing. See Payment methods