# AXOv2 Integration Guide # Components PayPal Accelerated Checkout (AXO) WebComponents are published to the NPM registry. You can install them with any supported package manager ```javascript= npm i --save @paypal/axo-authentication @paypal/axo-address @paypal/axo-paywall ``` Alternatively, you can include the components via third party CDN ```htmlembedded= <script src="https://unpkg.com/axo-address@2.1.29/lib/index.js"></script> ``` ## Authentication Component The authentication component is a required component for accelerated checkout. ![](https://i.imgur.com/MLPeL4F.png) * Identify email in PayPal network * If email is associated with a member account, prompt the user for OTP validation * If email is associated with guest account prompt the user to validate OTP in order to create PayPal member account and retrieve data * Return authToken to use in other components. ### Interfaces ```typescript interface EmailBlurEvent { email: string; } interface AuthValidatedEvent { email: string; authToken: string; } ``` ### Render Component ```htmlembedded= <pp-email clientToken="server-generated-client-token" email="spatel2@paypal.com" /> <script type="text/javascript"> let authToken, email; document .querySelector('pp-email') .addEventListener('paypal-axo-auth-validated', (event: AuthValidatedEvent) => { authToken = event.authToken; email = event.email; }); </script> ``` ### Properties | Property | Description | Type | Required | -------- | -------- | -------- | -------- | | clientToken | [PayPal](https://developer.paypal.com/braintree/docs/reference/request/payment-method/create#token) [Braintree](https://developer.paypal.com/braintree/docs/reference/request/client-token/generate) | String | Yes | email | Prefills the email in the input and triggers AXO lookup | String | No ### Events | Name | Description | Argument | | -------- | -------- | -------- | | paypal-axo-email-blur | Fired when the email address loses focus | EmailBlurEvent | | paypal-axo-auth-validated | Fired user validates OTP code | AuthValidatedEvent | ## Address Component Address component is an optional component for accelerated checkout. ![](https://i.imgur.com/IEiqYfs.png) * Address normalization, suggestion and input for guests * CRUD operations for PayPal Member addresses ### Interfaces ```typescript interface Address { addressLine1: string; adminArea2: string; adminArea1: string; postalCode: string; countryCode: string; } interface AddressEvent { address: Address; } ``` ### Render Component ```htmlembedded= <pp-address clientToken="server-generated-client-token" authToken = "authentication-component-token" addressLine1 = "123 Bedrock Street" adminArea2 = "Omaha" adminArea1 = "NE" postalCode = "68136" countryCode = "US" /> <script type="text/javascript"> let shippingAddress; document .querySelector('pp-address') .addEventListener('paypal-axo-address-selected', (event: AddressEvent) => { shippingAddress = event; }); </script> ``` ### Properties | Property | Description | Type | | -------- | -------- | -------- | | clientToken | [PayPal](https://developer.paypal.com/braintree/docs/reference/request/payment-method/create#token) [Braintree](https://developer.paypal.com/braintree/docs/reference/request/client-token/generate) | String | Yes | authToken | If set it will retrieve PayPal Member data | String | No | addressLine1 | Prepopulate street | String | No | adminArea2 | Prepopulate City | String | No | adminArea1 | Prepoulate State | String | No | postalCode | Prepopulate Postal Code | String | No | countryCode | Prepopulate Country Code | String | No | suggestion | Enables/Disables address suggestion | Boolean | No | suggestions | Array of address suggestions | Address[] | No ### Events | Name | Description | Argument | | -------- | -------- | -------- | | paypal-axo-address-selected | Fired when the address is selected | AddressSelectedEvent | | paypal-axo-address-input | Fired when the street address is input | AddressInputEvent | ## PayWall Component PayWall component is a required component for accelerated checkout. ![](https://i.imgur.com/lS8nVkr.png) * Secure card input for guest transactions * Display PayPal wallet funding instruments for members ### Interfaces ```typescript interface FundingInstrumentSelected { type: string; cardType: string; nonce: string; lastFour: string; } ``` ### Render Component ```htmlembedded= <pp-paywal clientToken="server-generated-client-token" authToken = "authentication-component-token" /> <script type="text/javascript"> let nonce; document .querySelector('pp-paywall') .addEventListener('paypal-axo-funding-selected', (event: FundingSelectedEvent) => { nonce = event.nonce }); </script> ``` ### Properties | Property | Description | Type | | -------- | -------- | -------- | | clientToken | [PayPal](https://developer.paypal.com/braintree/docs/reference/request/payment-method/create#token) [Braintree](https://developer.paypal.com/braintree/docs/reference/request/client-token/generate) | String | Yes | authToken | If set it will retrieve PayPal Member data | String | No ### Events | Name | Description | Argument | | -------- | -------- | -------- | | paypal-axo-paywall-selected | Fired when funding instrument is selected in paywall | FundingInstrumentSelected # Payment Processing Once you get a nonce, You will process the transaction using one of PayPals exiting payment processing platforms Braintree or PPCP. You will likely package up the email, shippingAddress, and nonce and pass it to your server for secure payment processing. ```htmlembedded= <script> document .querySelector('#my-payment-button') .addEventListener('click', async (event) => { await fetch('my-server-endpoint/processPayment', { method: 'POST', body: JSON.stringify({ nonce, email, shippingAddress, }) }) }); </script> ``` ## Braintree Integration The nonce that is returned from the PayWall component is usable in your current Braintree integration. If you do not already have an existing Braintree integration have a look at the [Getting Started](https://developer.paypal.com/braintree/docs/start/overview). ## Third Party Integration For PayPal Vault as a Service, you will use the nonce to retreive either the raw PAN or the network token from PayPal ```javascript= export async function fetchFundingInstrument(req: ExpressRequest) { const response = await createAccessToken(); const accessToken = await response.json(); const options = { method: "POST", headers: { Authorization: `Bearer ${accessToken.access_token}`, "Content-Type": "application/json" }, body: JSON.stringify({ axoToken: req.body.nonce }) }; return await fetch(`https://${HOSTNAME}/v2/checkout/axo/raw`, options); } ``` ```javascript= interface MemberResponse { networkToken: string; cryptogram: string; networkTransactionId: string; threeDSecureAuthenticationId: string; } interface GuestResponse { networkToken: string; cryptogram: string; pan: string; expdate: string; networkTransactionId: string; threeDSecureAuthenticationId: string; } ``` * [Network Transaction Id](https://developer.paypal.com/braintree/docs/reference/response/transaction/node#network_transaction_id) * [threeDSecureAuthenticationId](https://developer.paypal.com/braintree/docs/reference/request/transaction/sale#three_d_secure_pass_thru) # Integration Patterns ## Single Page Application In a single page application the components can listen to their events and react accordingly. You should not have to manage any state in the basic integration. ```htmlembedded= <script src="https://unpkg.com/axo-email@3.2.2/lib/index.js"></script> <script src="https://unpkg.com/axo-address@2.1.29/lib/index.js"></script> <script src="https://unpkg.com/axo-paywall@1.3.38/lib/index.js"></script> <pp-email clientToken="server-generated-client-token" /> <pp-address clientToken="server-generated-client-token" /> <pp-paywal clientToken="server-generated-client-token" /> <script type="text/javascript"> let email, shippingAddress, nonce; document .querySelector('pp-email') .addEventListener('paypal-axo-email-blur', (event: AuthValidatedEvent) => { email = event.email; }); document .querySelector('pp-address') .addEventListener('paypal-axo-address-selected', (event: AddressEvent) => { shippingAddress = event; }); document .querySelector('pp-paywall') .addEventListener('paypal-axo-funding-selected', (event: FundingSelectedEvent) => { nonce = event.nonce; }); document .querySelector('#my-payment-button') .addEventListener('click', async (event) => { await fetch('my-server-endpoint/processPayment', { method: 'POST', body: JSON.stringify({ nonce, email, shippingAddress, }) }) }); </script> ``` ## Multiple Pages In a multi-page integration, you will be responsible for controlling state. There are multiple ways to accomplish this. In this example the output of the AXO components are simply appended to the URL when navigating to a new page. On the new page the components are initialized with the data from the URL parameters. ### Email Page ```htmlembedded= <pp-email clientToken="server-generated-client-token" /> <script type="text/javascript"> document .querySelector('pp-email') .addEventListener('paypal-axo-auth-validated', (event: AuthValidatedEvent) => { const sParams = Object.keys(event).map(key => `${key}=${event[key]}`).join('&'); window.location.href = `/address?${sParams}` }); </script> ``` ### Address Page ```htmlembedded= <pp-address /> <script type="text/javascript"> const params = new URLSearchParams(window.location.search); document .querySelector('pp-address') .setAttribute('authToken', params.get('authToken')) document .querySelector('pp-address') .addEventListener('paypal-axo-address-selected', (event: AddressEvent) => { const sParams = Object .keys(event) .map(key => `${key}=${event[key]}`) .join('&'); window.location.href = `/payment${window.location.search}&${sParams}; }); </script> ``` ### Payment Page ```javascript= <pp-paywall /> <script type="text/javascript"> const params = new URLSearchParams(window.location.search); let nonce; document .querySelector('pp-paywall') .setAttribute('authToken', params.get('authToken')) document .querySelector('pp-paywall') .addEventListener('paypal-axo-funding-selected', (event: FundingSelectedEvent) => { nonce = event.nonce; }); document .querySelector('#my-payment-button') .addEventListener('click', async (event) => { await fetch('my-server-endpoint/processPayment', { method: 'POST', body: JSON.stringify({ nonce, email: params.get('email'), shippingAddress: { street: params.get('line1'), city: params.get('adminArea2'), state: params.get('adminArea1'), zip: params.get('postalCode'), country: params.get('countryCode') }, }) }) }); </script> ```