-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[INTER-2970] Fastlane implementation
- Loading branch information
Showing
23 changed files
with
708 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,25 @@ | ||
import {braintreeConstants, IBraintreeUrls} from 'src'; | ||
|
||
export function getBraintreeJsUrls(): IBraintreeUrls { | ||
/** | ||
* @param version If provided, URLs will be built with this version instead | ||
*/ | ||
export function getBraintreeJsUrls(version?: string): IBraintreeUrls { | ||
const { | ||
BASE_JS_URL: base, | ||
APPLE_JS: appleJs, | ||
GOOGLE_JS: googleJs, | ||
CLIENT_JS: clientJs, | ||
FASTLANE_JS: fastlaneJs, | ||
DATA_COLLECTOR_JS: dataCollectorJs, | ||
GOOGLE_JS_URL: googleJsUrl, | ||
JS_VERSION: jsVersion | ||
} = braintreeConstants; | ||
const clientJsURL = `${base}/${jsVersion}/${clientJs}`; | ||
const appleJsURL = `${base}/${jsVersion}/${appleJs}`; | ||
const braintreeGoogleJsURL = `${base}/${jsVersion}/${googleJs}`; | ||
const dataCollectorJsURL = `${base}/${jsVersion}/${dataCollectorJs}`; | ||
version ??= jsVersion; | ||
const clientJsURL = `${base}/${version}/${clientJs}`; | ||
const appleJsURL = `${base}/${version}/${appleJs}`; | ||
const braintreeGoogleJsURL = `${base}/${version}/${googleJs}`; | ||
const dataCollectorJsURL = `${base}/${version}/${dataCollectorJs}`; | ||
const fastlaneJsURL = `${base}/${version}/${fastlaneJs}`; | ||
|
||
return {appleJsURL, clientJsURL, dataCollectorJsURL, googleJsUrl, braintreeGoogleJsURL}; | ||
return {appleJsURL, clientJsURL, dataCollectorJsURL, googleJsUrl, braintreeGoogleJsURL, fastlaneJsURL}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './initFastlane'; | ||
export * from './manageFastlaneState'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { getPublicOrderId, getEnvironment, getShopIdentifier, getJwtToken } from '@boldcommerce/checkout-frontend-library'; | ||
import { loadScript } from '@paypal/paypal-js'; | ||
import { | ||
loadJS, | ||
getBraintreeJsUrls, | ||
braintreeOnLoadClient, | ||
IFastlaneInstance, | ||
getBraintreeClient, | ||
IBraintreeClient, | ||
FastlaneLoadingError, | ||
} from 'src'; | ||
|
||
interface TokenResponse { | ||
is_test_mode: boolean; | ||
client_token: string; | ||
} | ||
|
||
interface BraintreeTokenResponse extends TokenResponse { | ||
type: 'braintree'; | ||
client_id: null; | ||
} | ||
|
||
interface PPCPTokenResponse extends TokenResponse { | ||
type: 'ppcp'; | ||
client_id: string; | ||
} | ||
|
||
export async function initFastlane(): Promise<IFastlaneInstance> { | ||
const {clientJsURL, dataCollectorJsURL, fastlaneJsURL} = getBraintreeJsUrls('3.101.0-fastlane-beta.7.2'); | ||
|
||
try { | ||
// TODO move this request to the checkout frontend library | ||
const env = getEnvironment(); | ||
const shopId = getShopIdentifier(); | ||
const publicOrderId = getPublicOrderId(); | ||
const jwt = getJwtToken(); | ||
const resp = await fetch(`${env.url}/checkout/storefront/${shopId}/${publicOrderId}/paypal_fastlane/client_token`, { | ||
headers: { | ||
Authorization: `Bearer ${jwt}`, | ||
}, | ||
}); | ||
|
||
// Getting client token and which SDK to use | ||
const { | ||
client_token: clientToken, | ||
client_id: clientId, | ||
type, | ||
is_test_mode: isTest, | ||
} = await resp.json().then(r => r.data) as BraintreeTokenResponse | PPCPTokenResponse; | ||
|
||
switch (type) { | ||
case 'braintree': { | ||
await Promise.all([ | ||
loadJS(clientJsURL), | ||
loadJS(fastlaneJsURL), | ||
loadJS(dataCollectorJsURL), | ||
]).then(braintreeOnLoadClient); | ||
|
||
const braintree = getBraintreeClient() as IBraintreeClient; | ||
const client = await braintree.client.create({authorization: clientToken}); | ||
const dataCollector = await braintree.dataCollector.create({ | ||
client: client, | ||
riskCorrelationId: getPublicOrderId(), | ||
}); | ||
const fastlane = await braintree.fastlane.create({ | ||
client, | ||
authorization: clientToken, | ||
deviceData: dataCollector.deviceData, | ||
}); | ||
|
||
return fastlane; | ||
} | ||
case 'ppcp': { | ||
const paypal = await loadScript({ | ||
dataUserIdToken: clientToken, | ||
clientId: clientId, | ||
components: 'fastlane', | ||
debug: isTest, | ||
}) as unknown as {Fastlane: () => Promise<IFastlaneInstance>}; | ||
const fastlane = await paypal.Fastlane(); | ||
|
||
return fastlane; | ||
} | ||
default: | ||
throw new Error(`unknown type: ${type}`); | ||
} | ||
} catch (error) { | ||
if (error instanceof Error) { | ||
error.name = FastlaneLoadingError.name; | ||
throw error; | ||
} | ||
|
||
throw new FastlaneLoadingError(`Error loading Fastlane: ${error}`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import {IFastlaneInstance} from 'src/types'; | ||
import {fastlaneState} from 'src/variables'; | ||
import {initFastlane} from './initFastlane'; | ||
|
||
/** | ||
* Gets an instance of Fastlane. If the instance has not yet been initialized then | ||
* one will be initialized and returned asynchronously. Calls to `getFastlaneInstance` while | ||
* and instance is being initialized will return the same promise, avoiding duplicate initializations | ||
* of the Fastlane instance. | ||
*/ | ||
export const getFastlaneInstance = async (): Promise<IFastlaneInstance> => { | ||
return fastlaneState.instance ?? (fastlaneState.instance = initFastlane().catch((e) => { | ||
// Clearing the rejected promise from state so we can try again | ||
fastlaneState.instance = null; | ||
throw e; | ||
})); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
interface IFastlaneAddress { | ||
firstName: string; | ||
lastName: string; | ||
company?: string; | ||
streetAddress: string; | ||
extendedAddress?: string; | ||
locality: string; // City | ||
region: string; // State | ||
postalCode: string; | ||
countryCodeNumeric?: number; | ||
countryCodeAlpha2: string; | ||
countryCodeAlpha3?: string; | ||
phoneNumber: string; | ||
} | ||
|
||
export interface IFastlanePaymentToken { | ||
id: string; | ||
paymentSource: { | ||
card: { | ||
brand: string; | ||
expiry: string; // "YYYY-MM" | ||
lastDigits: string; // "1111" | ||
name: string; | ||
billingAddress: IFastlaneAddress; | ||
} | ||
} | ||
} | ||
|
||
export interface IFastlanePaymentComponent { | ||
render: (container: string) => IFastlanePaymentComponent; | ||
getPaymentToken: () => Promise<IFastlanePaymentToken>; | ||
setShippingAddress: (shippingAddress: IFastlaneAddress) => void; | ||
} | ||
|
||
export interface IFastlaneCardComponent { | ||
render: (container: string) => IFastlaneCardComponent; | ||
getPaymentToken: (options: { | ||
billingAddress: IFastlaneAddress; | ||
}) => Promise<IFastlanePaymentToken>; | ||
} | ||
|
||
interface Field { | ||
placeholder?: string; | ||
prefill?: string; | ||
} | ||
|
||
export interface IFastlaneComponentOptions { | ||
styles?: unknown; | ||
fields?: { | ||
number?: Field; | ||
expirationDate?: Field; | ||
expirationMonth?: Field; | ||
expirationYear?: Field | ||
cvv?: Field; | ||
postalCode?: Field; | ||
cardholderName?: Field; | ||
phoneNumber?: Field; | ||
}; | ||
shippingAddress?: IFastlaneAddress; | ||
} | ||
|
||
export interface IFastlaneAuthenticatedCustomerResult { | ||
authenticationState: 'succeeded'|'failed'|'canceled'|'not_found'; | ||
profileData: { | ||
name: { | ||
firstName: string; | ||
lastName: string; | ||
}; | ||
shippingAddress: IFastlaneAddress; | ||
card: IFastlanePaymentToken; | ||
} | ||
} | ||
|
||
export interface IFastlaneInstance { | ||
profile: { | ||
showShippingAddressSelector: () => Promise<{ | ||
selectionChanged: true; | ||
selectedAddress: IFastlaneAddress; | ||
} | { | ||
selectionChanged: false; | ||
selectedAddress: null; | ||
}>; | ||
showCardSelector: () => Promise<{ | ||
selectionChanged: true; | ||
selectedCard: IFastlanePaymentToken; | ||
} | { | ||
selectionChanged: false; | ||
selectedCard: null; | ||
}>; | ||
}; | ||
setLocale: (locale: string) => void; | ||
identity: { | ||
lookupCustomerByEmail: (email: string) => Promise<{customerContextId: string}>; | ||
triggerAuthenticationFlow: (customerContextId: string) => Promise<IFastlaneAuthenticatedCustomerResult> | ||
}; | ||
FastlanePaymentComponent: (options: IFastlaneComponentOptions) => Promise<IFastlanePaymentComponent>; | ||
FastlaneCardComponent: (options: Omit<IFastlaneComponentOptions, 'shippingAddress'>) => IFastlaneCardComponent; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.