Overview
App Switch enables PayPal users who have a PayPal app installed on their phone to complete payment on the app when it is available.
This design requires a merchant to adopt a new integration pattern called "redirect flow" that supports the ability for a full-page client-side redirect within the browser either to App Switch or to redirect to PayPal in the same tab.
Eligibility
- Now available in the United States. We will roll out this feature to the EU and other markets.
- App switch is available with One-time payments and Vaulted payments in Pay Now flows only.
- App Switch does not work with the following JavaScript SDK features:
onShippingChange
onShippingAddressChange
onShippingOptionsChange
- App Switch doesn't work with iframes. Running the Javascript SDK in an iframe will block App Switch capabilities.
Client-Side integration
createOrderCallback
has three new options added to it to opt into the app switch flow: returnUrl
, cancelUrl
and, appSwitchPreference
. Merchants can pass in the new options in their paypalCheckoutInstance.createPayment()
call like this:
- javascript
createOrder: function() {
return paypalCheckoutInstance.createPayment({
// other options
returnUrl: 'example.com/return',
cancelUrl: 'example.com/cancel',
appSwitchPreference: {
launchPaypalApp: true
},
})
};
- javascript
const buttons = paypal
.Buttons({
// Sets up the transaction when a payment button is clicked
appSwitchWhenAvailable: true, // Need an indicator to trigger app switch
createOrder: createOrderCallback, //call API to create order
onApprove: onApproveCallback,
onError: function (error) {
// Do something with the error from the SDK
},
});
// If you suport app switch, depending on the browser the buyer may end up in a new tab
// To trigger completion of the flow, execute the code below after re-instantiating buttons
if (buttons.hasReturned()) {
buttons.resume();
} else {
buttons.render("#paypal-button-container");
}
Resume flow
The resume flow solves for some edge cases by helping a buyer resume a transaction completed in a different window or browser tab. However, this resume flow can lead to additional issues:
- Client-side state loss: If a merchant relies on a client-side state, that state can be lost in a new browser or tab. For example, if a buyer is authenticated in a tab where they initially started a transaction, but they end up in a new browser, they won't be authenticated in the new browser. Prepare for this possibility by including enough of the original state in the redirect URL so that the buyer can complete the transaction in a new browser.
- Open tab behavior: If a buyer is redirected to a new browser tab, the original tab remains open. The buyer can close the original tab after completing the transaction. If the buyer doesn't close the tab, the tab remains open in a loading state until it times out.
- Client-side action calls: The resume flow won't work with client-side action calls. Replace
client-side actions.*
with server-side calls. - Callbacks: Client-side callbacks such as
onApprove()
andonCancel()
will continue to work.
Server Side Integration
Check out our server-side integration page for more information. .
Domain Registration
To use the redirect flow, register every domain you plan to use as returnUrl
and cancelUrl
with PayPal. Do this in the Braintree Control Panel in both your sandbox and production environments.
Rules for domain names
- Must not start with a scheme (e.g. "https://").
- Must be between 4 and 255 characters.
- Cannot contain spaces or wildcards (*).
- Can consist of a top-level domain, a second-level domain, and 0 or more subdomains etc. a.b.example.com where example is the top-level domain, com is the second-level domain, and a and b are subdomains.
- The top-level domain must consist of 2 to 63 letters.
- Each subdomain and the second-level domain must consist of 1 to 63 alphanumeric characters and or hyphens.
- Each subdomain and the second-level domain can not start or end with a hyphen.
- The top-level domain, second-level domain, and the subdomains must be separated by a dot(.).
- Can end with a trailing dot.
Sandbox environment
Register your sandbox domain in the Braintree Control Panel:
- Log in to your Sandbox Control Panel.
- Click the gear icon (top-right).
- Select Account Settings.
- Scroll to the Payment Methods section.
- Next to PayPal, click Options.
- Click View Domain Names.
- In Specify Your Domain Names, enter the domain of your return page.
- Click Add Domain Names.
Production environment
Register your production domain in the Braintree Control Panel:
- Log in to your Production Control Panel.
- Click the gear icon (top-right).
- Select Account Settings.
- Scroll to the Payment Methods section.
- Next to PayPal, click Options.
- Click View Domain Names.
- In Specify Your Domain Names, enter the domain of your return page.
- Click Add Domain Names.
Complete App Switch Sample for One-time Payments
Here's a complete example integrating App Switch with the one-time payments flow:
// Create a PayPal Checkout component.
braintree.paypalCheckout.create({
client: clientInstance
}, function (paypalCheckoutErr, paypalCheckoutInstance) {
// Base PayPal SDK script options
var loadPayPalSDKOptions = {
currency: 'USD', // Must match the currency passed in with createPayment
intent: 'capture', // Must match the intent passed in with createPayment
commit: true // Required for Pay Now flow with App Switch
}
paypalCheckoutInstance.loadPayPalSDK(loadPayPalSDKOptions, function () {
const button = paypal.Buttons({
fundingSource: paypal.FUNDING.PAYPAL,
appSwitchWhenAvailable: true, // Indicator to trigger app switch
createOrder: function () {
// Base payment request options for one-time payments
var createPaymentRequestOptions = {
flow: 'checkout', // Required
amount: 10.00, // Required
currency: 'USD', // Required, must match the currency passed in with loadPayPalSDK
intent: 'capture', // Must match the intent passed in with loadPayPalSDK
// App Switch specific options
returnUrl: 'example.com/return',
cancelUrl: 'example.com/cancel'
};
return paypalCheckoutInstance.createPayment(createPaymentRequestOptions);
},
onApprove: function (data, actions) {
return paypalCheckoutInstance.tokenizePayment(data, function (err, payload) {
// Submit 'payload.nonce' to your server
});
},
onCancel: function (data) {
console.log('PayPal payment cancelled', JSON.stringify(data, 0, 2));
},
onError: function (err) {
console.error('PayPal error', err);
}
});
// If you support app switch, depending on the browser the buyer may end up in a new tab
// To trigger completion of the flow, execute the code below after re-instantiating buttons
if (button.hasReturned()) {
button.resume();
} else {
button.render('#paypal-button-container').then(function () {
// The PayPal button will be rendered in an html element with the ID
// 'paypal-button-container'. This function will be called when the PayPal button
// is set up and ready to be used
});
}
});
});
Complete App Switch Sample for Vaulted Payments
// Create a client.
braintree.client.create({
authorization: CLIENT_AUTHORIZATION
}, function (clientErr, clientInstance) {
// Stop if there was a problem creating the client.
// This could happen if there is a network error or if the authorization
// is invalid.
if (clientErr) {
console.error('Error creating client:', clientErr);
return;
}
// Create a PayPal Checkout component.
braintree.paypalCheckout.create({
client: clientInstance
}, function (paypalCheckoutErr, paypalCheckoutInstance) {
if (paypalCheckoutErr) {
console.error('Error creating PayPal Checkout:', paypalCheckoutErr);
return;
}
paypalCheckoutInstance.loadPayPalSDK({
vault: true
}, function () {
const buttons = paypal
.Buttons({
fundingSource: paypal.FUNDING.PAYPAL,
// Sets up the transaction when a payment button is clicked
appSwitchWhenAvailable: true, // Need an indicator to trigger app switch
createBillingAgreement: function () {
return paypalCheckoutInstance.createPayment({
flow: 'vault', // Required
// App Switch specific options
returnUrl: 'example.com/return',
cancelUrl: 'example.com/cancel',
// The following are optional params
billingAgreementDescription: 'Your agreement description',
enableShippingAddress: true,
shippingAddressEditable: false,
shippingAddressOverride: {
recipientName: 'Scruff McGruff',
line1: '1234 Main St.',
line2: 'Unit 1',
city: 'Chicago',
countryCode: 'US',
postalCode: '60652',
state: 'IL',
phone: '123.456.7890'
}
});
},
onApprove: function (data, actions) {
return paypalCheckoutInstance.tokenizePayment(data, function (err, payload) {
if (err) {
console.error('Tokenization error:', err);
return;
}
// Submit 'payload.nonce' to your server
console.log('PayPal nonce for vaulting:', payload.nonce);
});
},
onCancel: function (data) {
console.log('PayPal payment canceled', JSON.stringify(data, 0, 2));
},
onError: function (error) {
// Do something with the error from the SDK
console.error('PayPal error:', error);
},
});
// If you support app switch, depending on the browser the buyer may end up in a new tab
// To trigger completion of the flow, execute the code below after re-instantiating buttons
if (buttons.hasReturned()) {
buttons.resume();
} else {
buttons.render("#paypal-button-container").then(function () {
// The PayPal button will be rendered in an html element with the ID
// 'paypal-button-container'. This function will be called when the PayPal button
// is set up and ready to be used
});
}
});
});
});
Use the paypalCheckoutInstance
in the onApprove
function of the JavaScript SDK setup method to tokenize the PayPal account. After the customer completes the consent flow and the PayPal pop-up closes, successful tokenization will return a payment method nonce.
Send the nonce to your server and use a Braintree server SDK to call Transaction.sale
to create a transaction.
For detailed testing procedures and production deployment guidance, see the Testing and Go Live section.