Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | 4x 4x 4x 4x 4x 20x 20x 22x 22x 22x 22x 105x 84x 22x 22x 22x 22x 63x 22x 21x 22x 22x 22x 22x 2x 22x 21x 22x 22x 22x | import { Injectable } from '@angular/core';
import { Observable, combineLatest, defer, iif, switchMap } from 'rxjs';
import { AppFacade } from 'ish-core/facades/app.facade';
import { PaymentMethod } from 'ish-core/models/payment-method/payment-method.model';
import { PaypalConfig } from 'ish-core/models/paypal-config/paypal-config.model';
import { ScriptLoaderService, ScriptType } from 'ish-core/utils/script-loader/script-loader.service';
/**
* PayPal page types used for configuring the PayPal SDK parameter data-page-type.
*/
export type PaypalPageType = 'cart' | 'checkout' | 'home' | 'product-details' | 'product-listing';
/**
* PayPal component types that can be loaded via the SDK.
*
* These components determine which PayPal UI elements are available in the application.
* The SDK can be loaded with different combinations of components based on the use case.
*/
export type PaypalAdapterTypes = 'Buttons' | 'Messages' | 'CardFields';
interface PaypalScriptParams {
/** The current application locale (e.g., 'en_US', 'de_DE') */
locale: string;
/** The current currency code (e.g., 'USD', 'EUR') */
currency: string;
/** Payment method capabilities that determine SDK behavior */
capabilities?: string[];
/** PayPal-specific configuration from the application */
paypalConfig: PaypalConfig;
/** The PayPal payment method configuration */
paymentMethod?: PaymentMethod;
}
@Injectable({ providedIn: 'root' })
export class PaypalConfigService {
/** Base URL for the PayPal JavaScript SDK */
private readonly scriptUrl = 'https://www.paypal.com/sdk/js';
constructor(private appFacade: AppFacade, private scriptLoader: ScriptLoaderService) {}
/**
* Loads the PayPal JavaScript SDK with the appropriate configuration.
*
* This method dynamically loads the PayPal SDK script based on whether a payment method
* is provided. If a payment method exists, it loads the full SDK with buttons, messages,
* and card-fields components. Otherwise, it loads only the messages component for
* Pay Later display.
*
* @param nameSpace - Unique namespace for this PayPal integration to avoid conflicts
* @param pageType - The type of page where PayPal is being loaded (e.g., 'cart', 'checkout', 'product-details')
* @param paymentMethod - Optional PayPal payment method configuration from ICM
* @returns Observable that emits the loaded script information
*/
loadPaypalScript(nameSpace: string, pageType: string, paymentMethod?: PaymentMethod): Observable<ScriptType> {
return combineLatest([
this.appFacade.currentLocale$,
this.appFacade.currentCurrency$,
this.appFacade.serverSetting$<PaypalConfig>('payment.paypal'),
]).pipe(
switchMap(([locale, currency, paypalConfig]) =>
iif(
() => !!paymentMethod,
defer(() =>
this.scriptLoader.load(
this.calculateURL({
paymentMethod,
locale,
currency,
paypalConfig,
capabilities: paymentMethod?.capabilities,
}),
{
attributes: [
...(paymentMethod?.hostedPaymentPageParameters?.filter(attr => attr.name.startsWith('data-')) ?? []),
{
name: 'data-namespace',
value: nameSpace,
},
{ name: 'data-page-type', value: pageType },
{
name: 'data-partner-attribution-id',
value: paymentMethod?.hostedPaymentPageParameters?.find(
attr => attr.name === 'data-partner-attribution-id'
)?.value,
},
],
}
)
),
defer(() =>
this.scriptLoader.load(this.calculateURL({ locale, currency, paypalConfig }), {
attributes: [
{
name: 'data-namespace',
value: nameSpace,
},
{ name: 'data-page-type', value: pageType },
],
})
)
)
)
);
}
/**
* Constructs the complete PayPal SDK URL with appropriate query parameters.
*
* Determines which URL construction method to use based on whether a payment method
* is provided:
* - With payment method: Full SDK with buttons, messages, and card-fields
* - Without payment method: Messages-only SDK for Pay Later display
*
* @param param - Script parameters including locale, currency, and optional payment method
* @returns Complete PayPal SDK URL with all required query parameters
*/
private calculateURL(param: PaypalScriptParams): string {
if (param.paymentMethod) {
return this.scriptUrl.concat(`?${this.getScriptQueryParameters(param)}`);
}
return this.scriptUrl.concat(`?${this.getScriptQueryParameterForMessages(param)}`);
}
/**
* Constructs query parameters for the PayPal SDK messages-only component.
*
* @param param - Script parameters including PayPal config, currency, and locale
* @returns Query string with parameters for messages-only SDK
*/
private getScriptQueryParameterForMessages(param: PaypalScriptParams): string {
let params = `client-id=${param.paypalConfig.clientId}`;
params = `${params}&merchant-id=${param.paypalConfig.merchantId}`;
params = `${params}¤cy=${param.currency}`;
params = `${params}&locale=${param.locale}`;
params = `${params}&components=messages`;
params = `${params}&enable-funding=paylater`;
return params;
}
/**
* Constructs query parameters for the full PayPal SDK with all components.
*
* This is used when a payment method is provided, typically on checkout pages.
*
* @param param - Script parameters including payment method, capabilities, currency, and locale
* @returns Query string with all parameters for full SDK
*/
private getScriptQueryParameters(param: PaypalScriptParams): string {
let params = `client-id=${param.paypalConfig.clientId}`;
params = `${params}&merchant-id=${param.paypalConfig.merchantId}`;
const intentParam = param.paymentMethod.hostedPaymentPageParameters?.find(attr => attr.name === 'intent');
if (intentParam) {
params = `${params}&intent=${intentParam.value}`;
}
params = `${params}&components=buttons,messages,card-fields`;
params = `${params}¤cy=${param.currency}`;
params = `${params}&locale=${param.locale}`;
// default commit value is true
if (!param.capabilities.includes('RedirectAfterCheckout')) {
params = `${params}&commit=false`;
}
if (
param.paypalConfig.payLaterPreferences.PayLaterEnabled &&
!param.capabilities.includes('PaypalExperienceContext')
) {
params = `${params}&enable-funding=paylater`;
}
if (!param.capabilities.includes('PaypalExperienceContext')) {
params = `${params}&disable-funding=card,sepa`;
}
return params;
}
}
|