Strategic partner distribution integration guide

DocsLast updated: November 25th 2024, @ 6:06:17 pm


Overview

This Strategic Partner Distribution Integration Guide is for merchants that want to implement Fastlane either with their current Payment Service Provider or their ecommerce partner platform.

Prerequisites

Fastlane is available to integrate for merchants in the USA:

  • Fastlane supports payments using desktop and mobile web browsers.
  • If the merchant's current checkout process doesn't collect a billing address, you need to collect it to use Fastlane.

Fastlane profiles

Fastlane supports 2 main types of profiles or personas:

  • Non-Accelerated User: A guest user who doesn't have a Fastlane profile.
  • Accelerated User: An existing PayPal or Fastlane user that we can recognize and accelerate.

Generate Client Token

You first need to generate a client token to initialize the JavaScript SDK. Client tokens are expected to be cached and reused for the merchant and payment partner combination they are processing until the token expires. You need to generate and include an auth assertion JSON Web Token (JWT).

Generate with Client and Secret

curl -s -X POST "https://api-m.sandbox.paypal.com/v1/oauth2/token" \
    -u "CLIENT_ID:CLIENT_SECRET" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -H "PayPal-Request-Id: YOUR-PAYPAL-REQUEST-ID" \
    -H "PayPal-Auth-Assertion: PAYPAL-AUTH-ASSERTION" \
    -H "PayPal-Partner-Attribution-Id: BN-CODE" \
    -d "grant_type=client_credentials" \
    -d "response_type=client_token" \
    -d "intent=sdk_init" \
    -d "domains[]=example.com,example2.com"

Generate with an access token

curl -s -X POST "https://api-m.sandbox.paypal.com/v1/oauth2/token" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -H "Authorization: Bearer ACCESS-TOKEN" \
    -H "PayPal-Request-Id: YOUR-PAYPAL-REQUEST-ID" \
    -H "PayPal-Auth-Assertion: PAYPAL-AUTH-ASSERTION" \
    -H "PayPal-Partner-Attribution-Id: BN-CODE" \
    -d "grant_type=token_exchange" \
    -d "response_type=client_token" \
    -d "intent=sdk_init" \
    -d "domains[]=example.com,example2.com"

Modify the code

  1. Copy the sample request code.
  2. Change the ACCESS-TOKEN to your sandbox-access-token.
  3. Change REQUEST_ID to a unique alphanumeric set of characters, such as a time stamp.
  4. Add AUTH-ASSERTION-TOKEN to your PayPal-Auth-Assertion token if transacting on behalf of another merchant. The PayPal-Auth-Assertion header is optional for a client token generation call, but you can include it when you are processing transactions on behalf of another merchant.
  5. Add the BN Code associated with your PayPal account.

Step result

A successful result returns the following:

  • An HTTP response code of 200 or 201. Returns 200 for an idempotent request.
  • An access token.
RelMethodDescription
confirmPostMake a POST request to generate the access token.

Response

{
    "access_token": "eyJraW...",
    "app_id": "APP-80W2...",
    "expires_in": 32400,
    "nonce": "2024-01...",
    "scope": "...",
    "token_type": "Bearer"
}
  • Send domain name in client token generate call: When generating a client token, you need to send a domain name where Fastlane will be displayed to customers to protect against cross-site scripting attacks.
  • Provide the root domain name: You must provide the base or root domain name. For example, if https://checkout.example.com is the URL where you are loading Fastlane components, pass the root domain name example.com.
URLRoot Domain Name
https://www.paypal.compaypal.com
https://checkout.paypal.compaypal.com
https://www.paypal.co.ukpaypal.co.uk
https://developer.paypal.compaypal.com
https://www.paypal.capaypal.ca
  • If you specify subdomains, wildcards or protocols, the system return an error.

The following list explains the limitations you need to follow when passing domain names for your Fastlane integration:

  • No subdomains: Don't include subdomains, such as sub.example.com.
  • No wildcards: Don't use wildcard characters, such as *.example.com.
  • No protocols: Don't include the HTTP or HTTPS protocols in the domain name. For example: https://example.com.

Error Handling: If you specify subdomains, wildcards, or protocols, the system generates an error.

Load SDK

Initialize the PayPal JavaScript (JS) SDK with your client ID and the client token from the previous step. If you are using the namespace feature of the JavaScript SDK to load buttons and Fastlane separately, review the documentation here.

<script
    src="https://www.paypal.com/sdk/js?client-id=<CLIENT_ID>>&components=buttons,fastlane"
    data-sdk-client-token="<SDK_CLIENT_TOKEN>">
</script>

Dynamically load SDK

If you want to fetch the client token async, you can load the JavaScript SDK using a function similar to the one below.

function loadSDK(clientId, clientToken) {
    return new Promise((resolve, reject) => {
        const pyplScript = document.createElement('script');
        pyplScript.setAttribute('src', `https://www.paypal.com/sdk/js?client-id=${clientId}&components=buttons,fastlane`);

        pyplScript.setAttribute('data-user-id-token', clientToken);
        pyplScript.onload = () => { resolve(); };
        document.head.appendChild(pyplScript);
    });
}

Initialize SDK

The following code gets the identity and profile objects from the JavaScript SDK needed to integrate the consumer experience. For alpha merchants, the JavaScript SDK supports only Visa and Mastercard payments.

const {
    identity,
    profile,
    FastlaneConsentComponent,
} = await window.paypal.Fastlane();

Look up email address

const email = document.getElementById('email').value;
const { customerContextId } = await identity.lookupCustomerByEmail(email);

Integrate consumer persona

The response of the lookupCustomerByEmail function shows the type of User persona.

PersonacustomerContextId
Fastlane guestEmpty string
Fastlane memberstring

Integrate Fastlane member

Trigger authentication

const authResponse = await identity.triggerAuthenticationFlow(
    customerContextId
);

Example response

{
    "authenticationState": "succeeded",
    "profileData": {
        "shippingAddress": {
            "company": "PayPal",
            "streetAddress": "123 Fake St, Suite 100",
            "locality": "Springfield",
            "region": "IL",
            "postalCode": "62701",
            "countryCodeAlpha2": "US",
            "firstName": "Alice",
            "lastName": "Johnson",
            "phoneNumber": "14155559876"
        },
        "card": {
            "id": "21b800c9-8568-15c5-8df6-0d67d9f46771",
            "paymentSource": {
                "card": {
                    "brand": "MasterCard",
                    "expiry": "2025-12",
                    "lastDigits": "1234",
                    "name": "Jane Doe",
                    "billingAddress": {
                        "streetAddress": "123 Fake St, Suite 100",
                        "locality": "Springfield",
                        "region": "IL",
                        "postalCode": "62701",
                        "countryCodeAlpha2": "US"
                    }
                }
            }
        },
        "name": {
            "firstName": "Alice",
            "lastName": "Johnson"
        }
    }
}

Address selector modal

Use the profile object from the JavaScript SDK initialization step to render the address selector. Render your HTML element to invoke the address selector.

document.getElementById('changeAddress').addEventListener('click', async (event) => {
    const { selectionChanged, selectedAddress } = await profile.showShippingAddressSelector();
    if (selectionChanged) {
        // use selectedAddress in your own UI
    }
});

Card selector modal

Use the profile object from the JavaScript SDK initialization step to render the card selector. Render your HTML element to invoke the card selector.

document.getElementById('changeCard').addEventListener('click', async (event) => {
    const {
        selectionChanged,
        selectedCard
    } = await profile.showCardSelector();
    if (selectionChanged) {
        // Use selectedCard in your UI
    }
});

To determine whether to show consent, the payment services provider must call the initialize consent component.

const consent = await axoSdk.ConsentComponent();
consent.getRenderState()

Object returned by getRenderState

return {
    showConsent: true | false,
    defaultToggleState: true | false,
    termsAndConditionsLink: string,
    privacyPolicyLink: string,
    termsAndConditionsVersion: string
};

The showConsent parameter determines whether to show custom consent. If showConsent is false, no other properties will be returned.

Note: showConsent, termsAndConditions, and version are required when creating the profile.

Token Exchange

The Token Exchange endpoint is leveraged by partners during a transaction that is being accelerated by Fastlane. In other words, when a Fastlane member has been authenticated and is leveraging their profile for checkout, we will provide the partner with a client-safe token that can then be swapped for a network token and cryptogram.

Send the token returned to your server. We are sharing an example of the Fastlane token.

Front end

const paymentToken = paymentToken.id;
const headers = new Headers();

headers.append("Content-Type", "application/json");

const body = JSON.stringify({
    token: paymentToken,
});

const response = await fetch("/<MERCHANT ENDPOINT FOR TOKEN GENERATION>", {
    method: "POST",
    headers,
    body,
});

return await response.json();

Back end

const url = `${PAYPAL_API_BASE_URL}/v3/vault/payment-method-credentials`;
const auth = `Bearer ${accessToken}`;
const requestId = new Date().getTime();

const metadataId = '0fd1215774704ad8b1080ef79bb64950';

const authAssertion = getAuthAssertionToken(
    PAYPAL_CLIENT_ID,
    PAYPAL_MERCHANT_ID,
);

const headers = new Headers();
headers.append('Authorization', auth);
headers.append('PayPal-Request-Id', requestId);
headers.append('PayPal-Auth-Assertion', authAssertion);
headers.append('PayPal-Partner-Attribution-Id', PAYPAL_BN_CODE);
headers.append('PayPal-Client-Metadata-Id', metadataId);

const body = {
    token: {
        id: accessToken,
        type: 'SINGLE_USE_TOKEN',
    },
};

const options = {
    method: 'POST',
    headers,
    body: JSON.stringify(body),
};

const response = await fetch(url, options);
const data = await response.json();

Modify the code

  1. Copy the sample request code.
  2. Use the bearer token that you get from the token generate call.
  3. Change REQUEST_ID to a unique alphanumeric set of characters, such as a time stamp.
  4. Add AUTH-ASSERTION-TOKEN to your PayPal-Auth-Assertion token if transacting on behalf of another merchant.
  5. Add the BN Code associated with your PayPal account.

Step result

A successful result returns the following :

  • An HTTP response code of 200 or 201. Returns 200 for an idempotent request.
  • An access token.
RelMethodDescription
confirmPostMake a POST request to generate the access token.

Expected response

{
    "card": {
        "network_token": {
            "id": "NDg5NTM3MDAxMzM0Njk2Nw==",
            "expiry": "2025-11",
            "cryptogram": "eyJjcmVkZW50aWFsIjoiU2FEQTBHdzljUjM3ajh4clpQNlZGQ0pwWiIsImVjb21tZXJjZV9pbmRpY2F0b3IiOiIwNyJ9"
        },
        "last_digits": "2643",
        "expiry": "2030-10",
        "brand": "VISA"
    }
}

Errors

If you see errors, use these codes and their descriptions to troubleshoot:

IssueResponse CodeDescription
INTERNAL_SERVER_ERROR500An internal server error occurred
NETWORK_TOKEN_NOT_SUPPORTED422Support for network token is unavailable for the provided token ID
TOKEN_NOT_FOUND422The specified token id does not exist.
INVALID_TOKEN_REFERENCE422The internal reference ID linked to the provided token could not be located. Please contact PayPal account manager for assistance.
INVALID_TOKEN_STATE422Token provided is not usable due to its invalid state. Please contact PayPal account manager for assistance.
NOT_ENABLED_FOR_TOKENIZATION403You do not have permission to access or perform operations on this resource. Please contact PayPal account manager for assistance.
PERMISSION_DENIED403You do not have permission to access or perform operations on this resource.
AUTHENTICATION_FAILURE401Authentication failed due to missing Authorization header, or invalid authentication credentials.
MISSING_REQUIRED_PARAMETER400A required field / parameter is missing.
INVALID_STRING_LENGTH400The value of a field is either too short or too long.
INVALID_PARAMETER_SYNTAX400The value of a field does not conform to the expected format.
INVALID_PARAMETER_VALUE400The value of a field is invalid.

Process Payment

The network Token and cryptogram can now be used to process the payment on your existing payment infrastructure.

Integrate Fastlane guest

You will write the UI exactly as what has been agreed upon by Fastlane and your design teams. The lookupCustomerByEmail function executed above will return some additional data, which will direct your functionality in the consent UI.

If we have this response:

{
    customerContextId: string;
}

If we have a customerContextId, it means we have a saved Fastlane profile, and if we don't have a customerContextId, it means we are a guest user.

Process Payment

Since you own the card fields that collected the card information, you can collect and process it as you do today.

Create profile

After a successful transaction you will execute the create profile API if the consent toggle is on. So you will want to send the results of the consent UI to the server. If details toggle is True:

curl -v -k -X POST 'https://api-m.sandbox.qa.paypal.com:18582/v3/vault/customers' \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ACCESS-TOKEN" \
    -H "PayPal-Request-Id: REQUEST-ID" \
    -H "PayPal-Auth-Assertion: AUTH-ASSERTION-TOKEN" \
    -H "PayPal-Client-Metadata-Id: 0fd1215774704ad8b1080ef79bb64950" \
    -d '{
        "email": "[email protected]",
        "name": {
        "given_name": "Gary",
        "surname": "Guest"
    },
    "phones": [
        {
        "national_number": "3398240084",
        "country_code": "1"
        }
    ],
    "product_type": "FASTLANE",
    "payment_tokens": [
        {
            "payment_source": {
                "card": {
                    "number": "4111111111111111",
                    "expiry": "2027-02",
                    "name": "Gary Guest",
                    "billing_address": {
                        "address_line_1": "2211 N First Street",
                        "address_line_2": "Building 17",
                        "admin_area_1": "CA",
                        "admin_area_2": "San Jose",
                        "country_code": "US",
                        "postal_code": "95131"
                    }
                }
            }
        }
    ],
    "addresses": [
        {
            "name": {
                "given_name": "Gary",
                "surname": "Guest"
            },
            "address_line_1": "2211 N First Street",
            "address_line_2": "Building 17",
            "admin_area_1": "CA",
            "admin_area_2": "San Jose",
            "country_code": "US",
            "postal_code": "95131"
        }
    ],
    "legal_agreements": [
            {
                "type": "ACCELERATED_CHECKOUT_USER_AGREEMENT",
                "major_version": 3,
                "minor_version": 2
            }
        ]
    }'

Modify the code

  1. Copy the sample request code.
  2. Change the ACCESS-TOKEN to your sandbox-access-token.
  3. Change REQUEST_ID to a unique alphanumeric set of characters, such as a time stamp.
  4. Add AUTH-ASSERTION-TOKEN to your PayPal-Auth-Assertion token if transacting on behalf of another merchant.
IssueResponse CodeDescription
INTERNAL_SERVER_ERROR500 Internal Server ErrorAn internal server error occurred.
UNPROCESSABLE_ENTITY422 Unprocessable EntityThe requested action could not be performed, semantically incorrect, or failed business validation.
UNSUPPORTED_COUNTRYNon US billing Address
NOT_AUTHORIZED403Authorization failed due to insufficient permissions.
AUTHENTICATION_FAILURE401Authentication failed due to missing Authorization header, or invalid authentication credentials.
INVALID_REQUEST400The request is not well-formed, is syntactically incorrect, or violates schema.
INVALID_PARAMETER_SYNTAX
MISSING_REQUIRED_PARAMETER
INVALID_STRING_LENGTH
INVALID_STRING_MAX_LENGTH

Step result

A successful result returns the following:

  • An HTTP response code of 200 or 201. Returns 200 for an idempotent request.
  • The access token should be returned.
RelMethodDescription
confirmPostMake a POST request to generate the access token.

Response

HTTP status code: 201 CREATED
HTTP Headers:  
"Content-Type": "application/json"  
Response payload:
{
    "create_time": "2024-07-26T15:28:23Z",
    "name": {
        "given_name": "Gary",
        "surname": "Guest"
    },
    "email": "[email protected]",
    "phones": [
        {
            "country_code": "1",
            "national_number": "3398240084"
        }
    ],
    "id": "5WGAQJMNUJXKS",
    "payment_tokens": [
        {
            "ordinal": 1,
            "payment_source": {
                "card": {
                    "name": "Gary Guest",
                    "last_digits": "1111",
                    "brand": "VISA",
                    "expiry": "2027-02",
                    "billing_address": {
                        "address_line_1": "2211 N First Street",
                        "address_line_2": "Building 17",
                        "admin_area_2": "San Jose",
                        "admin_area_1": "CA",
                        "postal_code": "95131",
                        "country_code": "US"
                    }
                }
            }
        }
    ],
    "addresses": [
        {
            "id": "7KA64580FB957254X",
            "create_time": "2024-07-26T15:28:23Z",
            "address_line_1": "2211 N First Street",
            "address_line_2": "Building 17",
            "admin_area_2": "San Jose",
            "admin_area_1": "CA",
            "postal_code": "95131",
            "country_code": "US",
            "name": {
                "given_name": "Gary",
                "surname": "Guest"
            },
            "phone_number": {
                "country_code": "1",
                "national_number": "3398240084"
            },
            "ordinal": 1
        }
    ]
}

Integrate Network Token usage event

Any time there is an email lookup and a customer checkout for both a Fastlane Guest and a Fastlane Member whether successful or not, you need to send us a network token usage event.

curl -s -X POST "https://api-m.sandbox.paypal.com/v1/notifications/events" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer ACCESS-TOKEN" \
    -H "PayPal-Request-Id: YOUR-PAYPAL-REQUEST-ID" \
    -H "PayPal-Auth-Assertion: PAYPAL-AUTH-ASSERTION" \
    -H "PayPal-Partner-Attribution-Id: BN-CODE" \
    -d "{
        "event_name": "FASTLANE_PARTNER_TX_PROCESSED",
        "session_id": string, // Fastlane CMID (customerMetadataId - unique identifier   //of Buyer session in browser)
        "partner_merchant_id": string, // Partner's ID for merchant
        "partner_merchant_name": string, // Optional name of merchant as stored in   //Partners' systems
        "payment_method_credential_token_id": string, // Payment Method Credential ID   //as set by Vault
        "transaction_amount": string, // Amount of transaction request
        "transaction_currency_code": string, // Currency code of transaction request
        "transaction_id": string, // Id of transaction in PSP system
        "transaction_status": transactionStatus,
        "transaction_timestamp": string, // Epoch time
        "processing_code": string, // By default, "ISO8583" processing code used for   //txn processing
        "processing_code_version": string, // Either "ISO8485:1987" or "ISO8485:1993" R //equired if "processing_code" is not ISO8583
        "fastlane_customer_id": string // Required if network token used in txn was   //acquired using Fastlane profile
        "fastlane_consent_shown": boolean, // Required for both guest and member use   //cases.  
        "fastlane_consent_given": boolean // Optional - provided if //fastlane_consent_shown = true
        "version": string // Follows semantic versioning specifications
    }
    transactionStatus: {
        'PROCESSED',
        'DECLINED',
        'FAILED'
    }"

Modify the code

  1. Copy the sample request code.
  2. Change the ACCESS-TOKEN to your sandbox-access-token.
  3. Change REQUEST_ID to a unique alphanumeric set of characters, such as a time stamp.
  4. Add AUTH-ASSERTION-TOKEN to your PayPal-Auth-Assertion token if transacting on behalf of another merchant.
  5. Add the BN Code associated with your PayPal account.

Step result

A successful result returns the following:

  • An HTTP response code of 200 or 201. Returns 200 for an idempotent request.
  • An access token.
RelMethodDescription
confirmPostMake a POST request to generate the access token.

Response

{
    "event_name": "FASTLANE_PARTNER_TRANSACTION_PROCESSED",
    "session_id": "123456789",
    "partner_merchant_id": "123456789",
    "partner_merchant_name": "Fastlane",
    "payment_method_credential_token_id": "abc123xyz",
    "transaction_amount": "100.00",
    "transaction_currency_code": "USD",
    "transaction_id": "987654321",
    "transaction_status": "PROCESSED",
    "transaction_timestamp": "1577836800",
    "processing_code": "ISO8583",
    "processing_code_version": "ISO8485:1993",
    "fastlane_customer_id": "123456789",
    "fastlane_consent_shown": true,
    "fastlane_consent_given": true,
    "version": "0.1.0"
}