@lightspeed/online-payments-sdk 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +37 -0
  2. package/dist/cjs/index.js +4 -1
  3. package/dist/cjs/v1/EventBroadcaster.js +1 -0
  4. package/dist/cjs/v1/stripe/localizedText.js +32 -0
  5. package/dist/cjs/v1/stripe/session.js +9 -0
  6. package/dist/cjs/v1/stripe/shared.js +55 -14
  7. package/dist/cjs/v1/stripe/terms.js +1 -1
  8. package/dist/cjs/v1/stripe/themes.js +2 -2
  9. package/dist/cjs/v1/stripe/widget.js +64 -25
  10. package/dist/cjs/v1/stripe/widgets/deferredMoto.js +203 -0
  11. package/dist/cjs/v1/widget.js +32 -4
  12. package/dist/cjs/version.js +1 -1
  13. package/dist/index.d.ts +3 -0
  14. package/dist/index.js +3 -0
  15. package/dist/v1/EventBroadcaster.d.ts +18 -1
  16. package/dist/v1/EventBroadcaster.js +1 -0
  17. package/dist/v1/common.d.ts +8 -2
  18. package/dist/v1/index.d.ts +2 -2
  19. package/dist/v1/stripe/localizedText.d.ts +3 -0
  20. package/dist/v1/stripe/localizedText.js +28 -0
  21. package/dist/v1/stripe/session.d.ts +29 -6
  22. package/dist/v1/stripe/session.js +7 -1
  23. package/dist/v1/stripe/shared.d.ts +8 -5
  24. package/dist/v1/stripe/shared.js +55 -15
  25. package/dist/v1/stripe/terms.d.ts +1 -1
  26. package/dist/v1/stripe/terms.js +1 -1
  27. package/dist/v1/stripe/themes.js +2 -2
  28. package/dist/v1/stripe/widget.js +62 -23
  29. package/dist/v1/stripe/widgets/deferredMoto.d.ts +5 -0
  30. package/dist/v1/stripe/widgets/deferredMoto.js +200 -0
  31. package/dist/v1/stripe/widgets/moto.d.ts +2 -2
  32. package/dist/v1/stripe/widgets/payment.d.ts +2 -2
  33. package/dist/v1/widget.js +32 -4
  34. package/dist/version.d.ts +1 -1
  35. package/dist/version.js +1 -1
  36. package/package.json +8 -3
  37. package/styles/lightspeed-global-styles.css +81 -1
@@ -45,9 +45,19 @@ var error_1 = require("./error");
45
45
  var logger_1 = require("./logging/logger");
46
46
  var common_1 = require("./logging/common");
47
47
  var EventBroadcaster_1 = require("./EventBroadcaster");
48
+ var version_1 = require("../version");
49
+ // Exposes the running SDK version on the mount point so it can be inspected
50
+ // in browser devtools (e.g. `document.querySelector('[data-lightspeed-payments-sdk-version]')`).
51
+ var SDK_VERSION_ATTRIBUTE = 'data-lightspeed-payments-sdk-version';
52
+ function markMountPointWithSdkVersion(mountPoint) {
53
+ mountPoint.setAttribute(SDK_VERSION_ATTRIBUTE, version_1.SDK_VERSION);
54
+ }
55
+ function unmarkMountPointWithSdkVersion(mountPoint) {
56
+ mountPoint.removeAttribute(SDK_VERSION_ATTRIBUTE);
57
+ }
48
58
  function mountPaymentWidget(session, config) {
49
59
  return __awaiter(this, void 0, void 0, function () {
50
- var eventBroadcaster, decodedSession, logger_2, configSummary, widgetController, _a, error_2;
60
+ var eventBroadcaster, decodedSession, logger_2, configSummary, widgetController, _a, unmount_1, error_2;
51
61
  return __generator(this, function (_b) {
52
62
  switch (_b.label) {
53
63
  case 0:
@@ -86,8 +96,17 @@ function mountPaymentWidget(session, config) {
86
96
  return [3 /*break*/, 7];
87
97
  case 6: throw new error_1.UnsupportedLocationError();
88
98
  case 7:
99
+ markMountPointWithSdkVersion(config.mountPoint);
89
100
  widgetController.submit = (0, common_1.withLogging)(logger_2, 'Submit', widgetController.submit);
90
- widgetController.unmount = (0, common_1.withLogging)(logger_2, 'Unmount', widgetController.unmount);
101
+ unmount_1 = widgetController.unmount;
102
+ widgetController.unmount = (0, common_1.withLogging)(logger_2, 'Unmount', function () {
103
+ try {
104
+ unmount_1();
105
+ }
106
+ finally {
107
+ unmarkMountPointWithSdkVersion(config.mountPoint);
108
+ }
109
+ });
91
110
  return [2 /*return*/, widgetController];
92
111
  case 8:
93
112
  error_2 = _b.sent();
@@ -104,7 +123,7 @@ function mountPaymentWidget(session, config) {
104
123
  // Internal impl of v1.mountMultiStepWidget (additive; event-based flows unchanged).
105
124
  function mountMultiStepWidget(session, config) {
106
125
  return __awaiter(this, void 0, void 0, function () {
107
- var eventBroadcaster, decodedSession, logger_3, controller, _a, error_3;
126
+ var eventBroadcaster, decodedSession, logger_3, controller, _a, unmount_2, error_3;
108
127
  return __generator(this, function (_b) {
109
128
  switch (_b.label) {
110
129
  case 0:
@@ -137,9 +156,18 @@ function mountMultiStepWidget(session, config) {
137
156
  return [3 /*break*/, 5];
138
157
  case 4: throw new error_1.UnsupportedLocationError();
139
158
  case 5:
159
+ markMountPointWithSdkVersion(config.mountPoint);
140
160
  controller.submit = (0, common_1.withLogging)(logger_3, 'Submit', controller.submit);
141
161
  controller.handleNextAction = (0, common_1.withLogging)(logger_3, 'HandleNextAction', controller.handleNextAction);
142
- controller.unmount = (0, common_1.withLogging)(logger_3, 'Unmount', controller.unmount);
162
+ unmount_2 = controller.unmount;
163
+ controller.unmount = (0, common_1.withLogging)(logger_3, 'Unmount', function () {
164
+ try {
165
+ unmount_2();
166
+ }
167
+ finally {
168
+ unmarkMountPointWithSdkVersion(config.mountPoint);
169
+ }
170
+ });
143
171
  return [2 /*return*/, controller];
144
172
  case 6:
145
173
  error_3 = _b.sent();
@@ -3,4 +3,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SDK_VERSION = void 0;
4
4
  // This file is auto-generated during the build process
5
5
  // DO NOT EDIT MANUALLY - Version is extracted from package.json
6
- exports.SDK_VERSION = '1.8.0';
6
+ exports.SDK_VERSION = '1.9.0';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,8 @@
1
+ import { SDK_VERSION } from './version';
1
2
  export type * from './v1';
2
3
  export { THEME_COLORS } from './v1';
4
+ export { SDK_VERSION };
3
5
  export declare const LightspeedPayments: {
4
6
  v1: import("./v1").LightspeedPaymentsV1;
7
+ version: string;
5
8
  };
package/dist/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { v1 } from './v1';
2
+ import { SDK_VERSION } from './version';
2
3
  export { THEME_COLORS } from './v1';
4
+ export { SDK_VERSION };
3
5
  export var LightspeedPayments = {
4
6
  v1: v1,
7
+ version: SDK_VERSION,
5
8
  };
@@ -25,6 +25,23 @@ export type ChangeEvent = BaseEvent & {
25
25
  paymentMethodType?: string;
26
26
  saveCheckboxChecked?: boolean;
27
27
  };
28
+ export type RequiresFinalizationResult = {
29
+ status: 'RequiresFinalization';
30
+ code: 'RequiresFinalization';
31
+ /**
32
+ * Provider-neutral backend command. Integrations should forward this object
33
+ * unchanged to their Processing Service finalization endpoint.
34
+ */
35
+ finalizationRequest: {
36
+ paymentData: {
37
+ type: 'payment-method';
38
+ id: string;
39
+ };
40
+ options: {
41
+ savePaymentMethod: boolean;
42
+ };
43
+ };
44
+ };
28
45
  export type ErrorResultEvent = BaseEvent & {
29
46
  code: SupportedErrorCodes;
30
47
  status: 'Error';
@@ -42,6 +59,6 @@ export declare class EventBroadcaster {
42
59
  private convertInternalEventToExternal;
43
60
  registerMiddleware(middleware: EventMiddleware): void;
44
61
  applyMiddlewares(event: InternalEvent): Event;
45
- publish(event: InternalEvent): void;
62
+ publish(event: InternalEvent): Event;
46
63
  }
47
64
  export {};
@@ -63,6 +63,7 @@ var EventBroadcaster = /** @class */ (function () {
63
63
  var processedEvent = this.applyMiddlewares(event);
64
64
  var eventListener = this.listenersRegistry[event.status];
65
65
  eventListener(processedEvent);
66
+ return processedEvent;
66
67
  };
67
68
  return EventBroadcaster;
68
69
  }());
@@ -1,10 +1,16 @@
1
- import { EventBroadcaster, Event } from './EventBroadcaster';
1
+ import { EventBroadcaster, Event, RequiresFinalizationResult } from './EventBroadcaster';
2
2
  import { Session } from './session';
3
3
  import { PaymentWidgetTheme } from './widget';
4
4
  export type SessionType = 'payment' | 'payment-with-save' | 'moto' | 'moto-with-save' | 'save';
5
+ export type WidgetSubmitResult = Event | RequiresFinalizationResult;
5
6
  export type WidgetController = {
6
7
  unmount: () => void;
7
- submit: () => Promise<Event>;
8
+ submit: () => Promise<WidgetSubmitResult>;
9
+ /**
10
+ * Deferred MOTO only. Reports a definitive backend decline so the widget can
11
+ * present it and collect a fresh PaymentMethod on the next submission.
12
+ */
13
+ reportFinalizationDecline?: (result: RequiresFinalizationResult, message: string) => void;
8
14
  };
9
15
  export type MultiStepResult = {
10
16
  status: 'RequiresFinalization';
@@ -6,8 +6,8 @@
6
6
  */
7
7
  import { WidgetController, MultiStepController } from './common';
8
8
  import { PaymentWidgetConfiguration } from './widget';
9
- export type { WidgetController, MultiStepController, MultiStepResult, SessionType, DefaultValues, } from './common';
10
- export type { Event } from './EventBroadcaster';
9
+ export type { WidgetController, MultiStepController, MultiStepResult, SessionType, DefaultValues, WidgetSubmitResult, } from './common';
10
+ export type { Event, RequiresFinalizationResult } from './EventBroadcaster';
11
11
  export { UnsupportedLocationError, InvalidSessionPayloadError, InvalidSessionError, ProcessingError, } from './error';
12
12
  export type { PaymentWidgetConfiguration, PaymentWidgetTheme } from './widget';
13
13
  export { THEME_COLORS } from './stripe/themes';
@@ -0,0 +1,3 @@
1
+ /** Locale-selected UI text rendered alongside Stripe Elements. */
2
+ export declare function getSavePaymentMethodText(locale?: string): string;
3
+ export declare function getCardDeclinedText(locale?: string): string;
@@ -0,0 +1,28 @@
1
+ /** Locale-selected UI text rendered alongside Stripe Elements. */
2
+ var SAVE_PAYMENT_METHOD_TEXT = {
3
+ de: 'Zahlungsinformationen für zukünftige Einkäufe speichern',
4
+ en: 'Save payment details for future purchases',
5
+ fr: 'Enregistrer les informations de paiement pour vos prochains achats',
6
+ nl: 'Betaalgegevens opslaan voor toekomstige aankopen',
7
+ };
8
+ var CARD_DECLINED_TEXT = {
9
+ de: 'Ihre Karte wurde abgelehnt.',
10
+ en: 'Your card was declined.',
11
+ fr: 'Votre carte a été refusée.',
12
+ nl: 'Uw kaart is geweigerd.',
13
+ };
14
+ function getLanguage(locale) {
15
+ var _a;
16
+ var resolvedLocale = !locale || locale === 'auto'
17
+ ? ((_a = globalThis.navigator) === null || _a === void 0 ? void 0 : _a.language) || 'en'
18
+ : locale;
19
+ return resolvedLocale.toLowerCase().split('-')[0];
20
+ }
21
+ export function getSavePaymentMethodText(locale) {
22
+ var language = getLanguage(locale);
23
+ return SAVE_PAYMENT_METHOD_TEXT[language] || SAVE_PAYMENT_METHOD_TEXT.en;
24
+ }
25
+ export function getCardDeclinedText(locale) {
26
+ var language = getLanguage(locale);
27
+ return CARD_DECLINED_TEXT[language] || CARD_DECLINED_TEXT.en;
28
+ }
@@ -1,15 +1,38 @@
1
1
  import { StripeElementLocale } from '@stripe/stripe-js';
2
2
  import { BaseSession } from '../session';
3
- export type StripeSession = BaseSession & {
3
+ export type StripeBaseSession = BaseSession & {
4
4
  psp: 'STRIPE';
5
- context: StripeContext;
6
5
  };
7
- export type StripeContext = {
8
- clientSecret: string;
6
+ type StripeContextBase = {
9
7
  publishableKey: string;
8
+ locale?: StripeElementLocale;
9
+ businessName?: string;
10
+ };
11
+ export type IntentBoundStripeContext = StripeContextBase & {
12
+ intentMode?: 'intent-bound';
13
+ clientSecret: string;
10
14
  customerSessionClientSecret?: string;
11
15
  intent: 'save-card' | 'payment';
12
- locale?: StripeElementLocale;
13
16
  redirectUrl?: string;
14
- businessName?: string;
15
17
  };
18
+ export type DeferredStripeContext = StripeContextBase & {
19
+ intentMode: 'deferred';
20
+ intent: 'payment';
21
+ amount: number;
22
+ currency: string;
23
+ paymentMethodTypes: string[];
24
+ onBehalfOf: string;
25
+ clientSecret?: never;
26
+ customerSessionClientSecret?: never;
27
+ };
28
+ export type StripeContext = IntentBoundStripeContext | DeferredStripeContext;
29
+ export type IntentBoundStripeSession = StripeBaseSession & {
30
+ context: IntentBoundStripeContext;
31
+ };
32
+ export type DeferredStripeSession = StripeBaseSession & {
33
+ context: DeferredStripeContext;
34
+ };
35
+ export type StripeSession = IntentBoundStripeSession | DeferredStripeSession;
36
+ export declare function isDeferredStripeSession(session: StripeSession): session is DeferredStripeSession;
37
+ export declare function isIntentBoundStripeSession(session: StripeSession): session is IntentBoundStripeSession;
38
+ export {};
@@ -1 +1,7 @@
1
- export {};
1
+ export function isDeferredStripeSession(session) {
2
+ return session.context.intentMode === 'deferred';
3
+ }
4
+ export function isIntentBoundStripeSession(session) {
5
+ return (session.context.intentMode === undefined ||
6
+ session.context.intentMode === 'intent-bound');
7
+ }
@@ -1,6 +1,6 @@
1
1
  import { StripePaymentElementOptions, StripeAddressElementOptions } from '@stripe/stripe-js';
2
2
  import type { StripeElements, Stripe, StripeAddressElement, StripePaymentElement } from '@stripe/stripe-js';
3
- import { StripeContext, StripeSession } from './session';
3
+ import { IntentBoundStripeSession, StripeContext } from './session';
4
4
  import { DefaultValues } from '../common';
5
5
  import { EventBroadcaster } from '../EventBroadcaster';
6
6
  import { PaymentWidgetTheme } from '../widget';
@@ -45,6 +45,7 @@ export declare function buildCardConsentText(businessName: string, reason: strin
45
45
  * text.
46
46
  */
47
47
  export declare function createCardConsentElement(consentText: string): HTMLElement;
48
+ export declare function createSavePaymentMethodElement(mountElement: HTMLElement, onChange: (checked: boolean) => void, locale?: string): HTMLElement;
48
49
  /**
49
50
  * Create and mount payment element
50
51
  */
@@ -61,7 +62,9 @@ export declare function createPaymentElement(elements: StripeElements, options:
61
62
  * Also includes the current save-payment-method checkbox state (when present) on every Change event,
62
63
  * and emits a Change event when that checkbox state changes.
63
64
  */
64
- export declare function setupChangeEventHandler(paymentElement: StripePaymentElement, eventBroadcaster: EventBroadcaster, addressElement?: StripeAddressElement | null): void;
65
+ export declare function setupChangeEventHandler(paymentElement: StripePaymentElement, eventBroadcaster: EventBroadcaster, addressElement?: StripeAddressElement | null, onPaymentValueChange?: (value: {
66
+ savePaymentMethod?: boolean;
67
+ }) => void, initialSaveCheckboxState?: boolean): (saveCheckboxChecked: boolean) => void;
65
68
  /**
66
69
  * Create and mount address element
67
70
  */
@@ -76,12 +79,12 @@ export declare function cleanupElements(paymentElement: StripePaymentElement, ad
76
79
  /**
77
80
  * Handle save card operation
78
81
  */
79
- export declare function handleSaveCard(stripe: Stripe, elements: StripeElements, session: StripeSession, addressElement?: StripeAddressElement | null): Promise<import("../EventBroadcaster").SucceededResultEvent | import("../EventBroadcaster").PendingResultEvent | import("../EventBroadcaster").DeclinedResultEvent | import("../EventBroadcaster").ErrorResultEvent>;
82
+ export declare function handleSaveCard(stripe: Stripe, elements: StripeElements, session: IntentBoundStripeSession, addressElement?: StripeAddressElement | null): Promise<import("../EventBroadcaster").SucceededResultEvent | import("../EventBroadcaster").PendingResultEvent | import("../EventBroadcaster").DeclinedResultEvent | import("../EventBroadcaster").ErrorResultEvent>;
80
83
  /**
81
84
  * Handle payment operation
82
85
  */
83
- export declare function handlePayment(stripe: Stripe, elements: StripeElements, session: StripeSession, addressElement?: StripeAddressElement | null): Promise<import("../EventBroadcaster").SucceededResultEvent | import("../EventBroadcaster").PendingResultEvent | import("../EventBroadcaster").DeclinedResultEvent | import("../EventBroadcaster").ErrorResultEvent>;
86
+ export declare function handlePayment(stripe: Stripe, elements: StripeElements, session: IntentBoundStripeSession, addressElement?: StripeAddressElement | null): Promise<import("../EventBroadcaster").SucceededResultEvent | import("../EventBroadcaster").PendingResultEvent | import("../EventBroadcaster").DeclinedResultEvent | import("../EventBroadcaster").ErrorResultEvent>;
84
87
  /**
85
88
  * Create common submit handler for widgets
86
89
  */
87
- export declare function createSubmitHandler(stripe: Stripe, elements: StripeElements, session: StripeSession, eventBroadcaster: EventBroadcaster, addressElement?: StripeAddressElement | null): () => Promise<import("../EventBroadcaster").SucceededResultEvent | import("../EventBroadcaster").PendingResultEvent | import("../EventBroadcaster").DeclinedResultEvent | import("../EventBroadcaster").ErrorResultEvent>;
90
+ export declare function createSubmitHandler(stripe: Stripe, elements: StripeElements, session: IntentBoundStripeSession, eventBroadcaster: EventBroadcaster, addressElement?: StripeAddressElement | null): () => Promise<import("../EventBroadcaster").SucceededResultEvent | import("../EventBroadcaster").PendingResultEvent | import("../EventBroadcaster").DeclinedResultEvent | import("../EventBroadcaster").ErrorResultEvent>;
@@ -49,17 +49,18 @@ import { loadStripe, } from '@stripe/stripe-js';
49
49
  import { EventBuilder } from './ResultBuilder';
50
50
  import { InvalidSessionPayloadError, ProcessingError } from '../error';
51
51
  import { getThemeConfig } from './themes';
52
- import { ACH_TERMS_TEXT, ACH_TERMS_URL, PARTNER_CARD_CONSENT_TEXT } from './terms';
52
+ import { ACH_TERMS_TEXT, ACH_TERMS_URL, PARTNER_CARD_CONSENT_TEXT, } from './terms';
53
+ import { getSavePaymentMethodText } from './localizedText';
53
54
  /**
54
55
  * Initialize Stripe with the given context
55
56
  */
56
57
  export function initStripe(context, theme) {
57
58
  return __awaiter(this, void 0, void 0, function () {
58
- var publishableKey, clientSecret, customerSessionClientSecret, loadedStripe, stripe, themeConfig, elementsOptions, elements;
59
+ var publishableKey, loadedStripe, stripe, themeConfig, sharedElementsOptions, elements, elementsOptions, elementsOptions;
59
60
  return __generator(this, function (_a) {
60
61
  switch (_a.label) {
61
62
  case 0:
62
- publishableKey = context.publishableKey, clientSecret = context.clientSecret, customerSessionClientSecret = context.customerSessionClientSecret;
63
+ publishableKey = context.publishableKey;
63
64
  return [4 /*yield*/, loadStripe(publishableKey)];
64
65
  case 1:
65
66
  loadedStripe = _a.sent();
@@ -68,8 +69,15 @@ export function initStripe(context, theme) {
68
69
  }
69
70
  stripe = loadedStripe;
70
71
  themeConfig = getThemeConfig(theme);
71
- elementsOptions = __assign(__assign(__assign({ clientSecret: clientSecret, customerSessionClientSecret: customerSessionClientSecret }, (context.locale && { locale: context.locale })), (themeConfig && { appearance: themeConfig.appearance })), (themeConfig && { fonts: themeConfig.fonts }));
72
- elements = stripe.elements(elementsOptions);
72
+ sharedElementsOptions = __assign(__assign(__assign({}, (context.locale && { locale: context.locale })), (themeConfig && { appearance: themeConfig.appearance })), (themeConfig && { fonts: themeConfig.fonts }));
73
+ if (context.intentMode === 'deferred') {
74
+ elementsOptions = __assign(__assign(__assign({}, sharedElementsOptions), { mode: 'payment', amount: context.amount, currency: context.currency.toLowerCase(), paymentMethodTypes: context.paymentMethodTypes, paymentMethodCreation: 'manual' }), (context.onBehalfOf && { onBehalfOf: context.onBehalfOf }));
75
+ elements = stripe.elements(elementsOptions);
76
+ }
77
+ else {
78
+ elementsOptions = __assign(__assign({}, sharedElementsOptions), { clientSecret: context.clientSecret, customerSessionClientSecret: context.customerSessionClientSecret });
79
+ elements = stripe.elements(elementsOptions);
80
+ }
73
81
  return [2 /*return*/, { stripe: stripe, elements: elements }];
74
82
  }
75
83
  });
@@ -206,6 +214,24 @@ export function createCardConsentElement(consentText) {
206
214
  container.appendChild(text);
207
215
  return container;
208
216
  }
217
+ export function createSavePaymentMethodElement(mountElement, onChange, locale) {
218
+ var label = document.createElement('label');
219
+ label.className = 'stripe-save-payment-method-container';
220
+ var checkbox = document.createElement('input');
221
+ checkbox.type = 'checkbox';
222
+ checkbox.className = 'stripe-save-payment-method-checkbox';
223
+ checkbox.dataset.testid = 'save-payment-method-checkbox';
224
+ var text = document.createElement('span');
225
+ text.className = 'stripe-save-payment-method-text';
226
+ text.textContent = getSavePaymentMethodText(locale);
227
+ label.appendChild(checkbox);
228
+ label.appendChild(text);
229
+ mountElement.appendChild(label);
230
+ // The label must remain clickable, but its consent text is not selectable.
231
+ label.addEventListener('selectstart', function (event) { return event.preventDefault(); });
232
+ checkbox.addEventListener('change', function () { return onChange(checkbox.checked); });
233
+ return label;
234
+ }
209
235
  /**
210
236
  * Create and mount payment element
211
237
  */
@@ -245,12 +271,12 @@ export function createPaymentElement(elements, options, mountElement, eventBroad
245
271
  * Also includes the current save-payment-method checkbox state (when present) on every Change event,
246
272
  * and emits a Change event when that checkbox state changes.
247
273
  */
248
- export function setupChangeEventHandler(paymentElement, eventBroadcaster, addressElement) {
274
+ export function setupChangeEventHandler(paymentElement, eventBroadcaster, addressElement, onPaymentValueChange, initialSaveCheckboxState) {
249
275
  var paymentComplete = false;
250
276
  var addressComplete = !addressElement; // If no address element, consider it complete
251
277
  var previousComplete = null;
252
278
  var previousMethodType;
253
- var previousSaveCheckboxState; // Track save checkbox state
279
+ var previousSaveCheckboxState = initialSaveCheckboxState;
254
280
  var emitChangeEvent = function () {
255
281
  var complete = paymentComplete && addressComplete;
256
282
  previousComplete = complete;
@@ -261,23 +287,30 @@ export function setupChangeEventHandler(paymentElement, eventBroadcaster, addres
261
287
  })));
262
288
  };
263
289
  paymentElement.on('change', function (event) {
264
- var _a, _b;
290
+ var _a, _b, _c;
265
291
  paymentComplete = event.complete;
292
+ onPaymentValueChange === null || onPaymentValueChange === void 0 ? void 0 : onPaymentValueChange({
293
+ savePaymentMethod: (_a = event.value) === null || _a === void 0 ? void 0 : _a.savePaymentMethod,
294
+ });
266
295
  var completenessChanged = (paymentComplete && addressComplete) !== previousComplete;
267
- var methodType = (_a = event.value) === null || _a === void 0 ? void 0 : _a.type;
296
+ var methodType = (_b = event.value) === null || _b === void 0 ? void 0 : _b.type;
268
297
  var methodTypeChanged = methodType !== undefined && methodType !== previousMethodType;
269
298
  if (methodType !== undefined) {
270
299
  previousMethodType = methodType;
271
300
  }
272
301
  // `savePaymentMethod` reflects the "save for future use" checkbox; it is only
273
302
  // present on the change event when the element renders that checkbox.
274
- var saveCheckboxChanged = false;
275
- var saveCheckboxState = (_b = event.value) === null || _b === void 0 ? void 0 : _b.savePaymentMethod;
276
- if (saveCheckboxState !== undefined) {
277
- saveCheckboxChanged = previousSaveCheckboxState !== saveCheckboxState;
278
- previousSaveCheckboxState = saveCheckboxState;
303
+ var stripeSaveCheckboxChanged = false;
304
+ var stripeSaveCheckboxState = (_c = event.value) === null || _c === void 0 ? void 0 : _c.savePaymentMethod;
305
+ if (initialSaveCheckboxState === undefined &&
306
+ stripeSaveCheckboxState !== undefined) {
307
+ stripeSaveCheckboxChanged =
308
+ previousSaveCheckboxState !== stripeSaveCheckboxState;
309
+ previousSaveCheckboxState = stripeSaveCheckboxState;
279
310
  }
280
- if (completenessChanged || methodTypeChanged || saveCheckboxChanged) {
311
+ if (completenessChanged ||
312
+ methodTypeChanged ||
313
+ stripeSaveCheckboxChanged) {
281
314
  emitChangeEvent();
282
315
  }
283
316
  });
@@ -290,6 +323,13 @@ export function setupChangeEventHandler(paymentElement, eventBroadcaster, addres
290
323
  }
291
324
  });
292
325
  }
326
+ return function (saveCheckboxChecked) {
327
+ if (previousSaveCheckboxState === saveCheckboxChecked) {
328
+ return;
329
+ }
330
+ previousSaveCheckboxState = saveCheckboxChecked;
331
+ emitChangeEvent();
332
+ };
293
333
  }
294
334
  /**
295
335
  * Create and mount address element
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Translatable strings for Stripe payment elements.
2
+ * Terms and consent text for Stripe payment elements.
3
3
  * These strings are prepared for Transifex extraction.
4
4
  *
5
5
  * Usage with Transifex:
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Translatable strings for Stripe payment elements.
2
+ * Terms and consent text for Stripe payment elements.
3
3
  * These strings are prepared for Transifex extraction.
4
4
  *
5
5
  * Usage with Transifex:
@@ -47,7 +47,7 @@ function ensureThemeStylesheet() {
47
47
  }
48
48
  var styleElement = document.createElement('style');
49
49
  styleElement.id = THEME_STYLESHEET_ID;
50
- styleElement.textContent = "\n /* Dark theme styles for the mount element */\n .lsp-theme-dark {\n background-color: ".concat(THEME_COLORS.DARK.BACKGROUND, " !important;\n color: ").concat(THEME_COLORS.DARK.TEXT, " !important;\n }\n /* Dark theme styles for Stripe custom terms elements */\n .lsp-theme-dark .stripe-terms-chevron-icon {\n color: ").concat(THEME_COLORS.DARK.TEXT, " !important;\n }\n .lsp-theme-dark .stripe-custom-terms-text {\n color: ").concat(THEME_COLORS.DARK.TEXT, " !important;\n }\n .lsp-theme-dark .stripe-expandable-terms-content p {\n color: ").concat(THEME_COLORS.DARK.TEXT, " !important;\n }\n ");
50
+ styleElement.textContent = "\n /* Dark theme styles for the mount element */\n .lsp-theme-dark {\n background-color: ".concat(THEME_COLORS.DARK.BACKGROUND, " !important;\n color: ").concat(THEME_COLORS.DARK.TEXT, " !important;\n }\n /* Dark theme styles for Stripe custom terms elements */\n .lsp-theme-dark .stripe-terms-chevron-icon {\n color: ").concat(THEME_COLORS.DARK.TEXT, " !important;\n }\n .lsp-theme-dark .stripe-custom-terms-text {\n color: ").concat(THEME_COLORS.DARK.TEXT, " !important;\n }\n .lsp-theme-dark .stripe-expandable-terms-content p {\n color: ").concat(THEME_COLORS.DARK.TEXT, " !important;\n }\n .lsp-theme-dark .stripe-save-payment-method-container {\n color: ").concat(THEME_COLORS.DARK.TEXT, " !important;\n }\n .lsp-theme-dark .stripe-finalization-error {\n color: #ff8a8a !important;\n }\n .lsp-theme-dark .stripe-save-payment-method-checkbox {\n border-color: #697386;\n background-color: #30313d;\n }\n .lsp-theme-dark .stripe-save-payment-method-checkbox:checked {\n border-color: #007ea3;\n background-color: #007ea3;\n }\n .lsp-theme-dark .stripe-save-payment-method-checkbox:focus-visible {\n border-color: #00d4ff;\n box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.25);\n }\n ");
51
51
  document.head.appendChild(styleElement);
52
52
  }
53
53
  /**
@@ -71,7 +71,7 @@ function ensureInvoicingDarkStylesheet() {
71
71
  }
72
72
  var styleElement = document.createElement('style');
73
73
  styleElement.id = INVOICING_DARK_STYLESHEET_ID;
74
- styleElement.textContent = "\n /*\n * Mirror the non-colour .lsp-theme-invoicing rules from\n * lightspeed-global-styles.css. Class selectors match whole tokens, so an\n * element carrying the class lsp-theme-invoicing-dark is never matched by\n * the selector .lsp-theme-invoicing; without these the terms text would\n * fall back to the system font while the Elements iframe renders Inter.\n */\n .lsp-theme-invoicing-dark {\n color: #e3e4e9;\n padding-bottom: 0 !important;\n }\n .lsp-theme-invoicing-dark .stripe-custom-terms-text,\n .lsp-theme-invoicing-dark .stripe-expandable-terms-content p {\n color: #e3e4e9;\n font-family: 'Inter', system-ui, sans-serif;\n }\n /*\n * Links and the expand toggle. lightspeed-global-styles.css colours these\n * #635bff, which is close to unreadable on a dark surface, so they take the\n * consumer dark interactive colour instead. The chevron inherits from its\n * toggle so the two can never drift apart.\n */\n .lsp-theme-invoicing-dark .stripe-custom-terms-text a,\n .lsp-theme-invoicing-dark .stripe-expandable-terms-content a,\n .lsp-theme-invoicing-dark .stripe-expandable-terms-toggle {\n color: #6cb5fb;\n }\n .lsp-theme-invoicing-dark .stripe-terms-chevron-icon {\n color: inherit;\n }\n ";
74
+ styleElement.textContent = "\n /*\n * Mirror the non-colour .lsp-theme-invoicing rules from\n * lightspeed-global-styles.css. Class selectors match whole tokens, so an\n * element carrying the class lsp-theme-invoicing-dark is never matched by\n * the selector .lsp-theme-invoicing; without these the terms text would\n * fall back to the system font while the Elements iframe renders Inter.\n */\n .lsp-theme-invoicing-dark {\n color: #e3e4e9;\n padding-bottom: 0 !important;\n }\n .lsp-theme-invoicing-dark .stripe-custom-terms-text,\n .lsp-theme-invoicing-dark .stripe-expandable-terms-content p,\n .lsp-theme-invoicing-dark .stripe-save-payment-method-container,\n .lsp-theme-invoicing-dark .stripe-finalization-error {\n color: #e3e4e9;\n font-family: 'Inter', system-ui, sans-serif;\n }\n .lsp-theme-invoicing-dark .stripe-finalization-error {\n color: #ffa597;\n }\n .lsp-theme-invoicing-dark .stripe-save-payment-method-checkbox {\n border-color: #525256;\n background-color: #242427;\n }\n .lsp-theme-invoicing-dark .stripe-save-payment-method-checkbox:checked {\n border-color: #286ea8;\n background-color: #286ea8;\n }\n .lsp-theme-invoicing-dark .stripe-save-payment-method-checkbox:focus-visible {\n border-color: #6cb5fb;\n box-shadow: 0 0 0 3px rgba(108, 181, 251, 0.25);\n }\n /*\n * Links and the expand toggle. lightspeed-global-styles.css colours these\n * #635bff, which is close to unreadable on a dark surface, so they take the\n * consumer dark interactive colour instead. The chevron inherits from its\n * toggle so the two can never drift apart.\n */\n .lsp-theme-invoicing-dark .stripe-custom-terms-text a,\n .lsp-theme-invoicing-dark .stripe-expandable-terms-content a,\n .lsp-theme-invoicing-dark .stripe-expandable-terms-toggle {\n color: #6cb5fb;\n }\n .lsp-theme-invoicing-dark .stripe-terms-chevron-icon {\n color: inherit;\n }\n ";
75
75
  document.head.appendChild(styleElement);
76
76
  }
77
77
  /**
@@ -34,15 +34,18 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
34
34
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
35
  }
36
36
  };
37
+ import { isDeferredStripeSession, isIntentBoundStripeSession, } from './session';
38
+ import { InvalidSessionPayloadError } from '../error';
37
39
  import { mountPaymentWidget } from './widgets/payment';
38
40
  import { mountMotoWidget } from './widgets/moto';
41
+ import { mountDeferredMotoWidget } from './widgets/deferredMoto';
39
42
  import { applyThemeClass, removeThemeClass } from './themes';
40
43
  /**
41
44
  * Main mount function that delegates to appropriate widget based on session type
42
45
  */
43
46
  export function mount(mountElement, session, eventBroadcaster, defaultValues, theme) {
44
47
  return __awaiter(this, void 0, void 0, function () {
45
- var sessionType, widgetController, _a, originalUnmount_1, error_1;
48
+ var sessionType, widgetController_1, widgetController, _a, error_1;
46
49
  return __generator(this, function (_b) {
47
50
  switch (_b.label) {
48
51
  case 0:
@@ -53,43 +56,79 @@ export function mount(mountElement, session, eventBroadcaster, defaultValues, th
53
56
  sessionType = session.metadata.sessionType;
54
57
  _b.label = 1;
55
58
  case 1:
56
- _b.trys.push([1, 8, , 9]);
59
+ _b.trys.push([1, 10, , 11]);
60
+ if (!isDeferredStripeSession(session)) return [3 /*break*/, 3];
61
+ validateDeferredSession(session, sessionType);
62
+ return [4 /*yield*/, mountDeferredMotoWidget(mountElement, session, eventBroadcaster, defaultValues, theme)];
63
+ case 2:
64
+ widgetController_1 = _b.sent();
65
+ return [2 /*return*/, withThemeCleanup(widgetController_1, mountElement, theme)];
66
+ case 3:
67
+ if (!isIntentBoundStripeSession(session)) {
68
+ throw new InvalidSessionPayloadError('Invalid Stripe session context');
69
+ }
57
70
  widgetController = void 0;
58
71
  _a = sessionType;
59
72
  switch (_a) {
60
- case 'payment': return [3 /*break*/, 2];
61
- case 'payment-with-save': return [3 /*break*/, 2];
62
- case 'moto': return [3 /*break*/, 4];
63
- case 'moto-with-save': return [3 /*break*/, 4];
64
- case 'save': return [3 /*break*/, 4];
73
+ case 'payment': return [3 /*break*/, 4];
74
+ case 'payment-with-save': return [3 /*break*/, 4];
75
+ case 'moto': return [3 /*break*/, 6];
76
+ case 'moto-with-save': return [3 /*break*/, 6];
77
+ case 'save': return [3 /*break*/, 6];
65
78
  }
66
- return [3 /*break*/, 6];
67
- case 2: return [4 /*yield*/, mountPaymentWidget(mountElement, session, eventBroadcaster, defaultValues, theme)];
68
- case 3:
69
- widgetController = _b.sent();
70
- return [3 /*break*/, 7];
71
- case 4: return [4 /*yield*/, mountMotoWidget(mountElement, session, eventBroadcaster, defaultValues, theme)];
79
+ return [3 /*break*/, 8];
80
+ case 4: return [4 /*yield*/, mountPaymentWidget(mountElement, session, eventBroadcaster, defaultValues, theme)];
72
81
  case 5:
73
82
  widgetController = _b.sent();
74
- return [3 /*break*/, 7];
75
- case 6: throw new Error("Unsupported session type: ".concat(sessionType));
83
+ return [3 /*break*/, 9];
84
+ case 6: return [4 /*yield*/, mountMotoWidget(mountElement, session, eventBroadcaster, defaultValues, theme)];
76
85
  case 7:
77
- originalUnmount_1 = widgetController.unmount;
78
- widgetController.unmount = function () {
79
- removeThemeClass(mountElement, theme);
80
- originalUnmount_1();
81
- };
82
- return [2 /*return*/, widgetController];
83
- case 8:
86
+ widgetController = _b.sent();
87
+ return [3 /*break*/, 9];
88
+ case 8: throw new Error("Unsupported session type: ".concat(sessionType));
89
+ case 9:
90
+ // Wrap unmount to include theme cleanup
91
+ return [2 /*return*/, withThemeCleanup(widgetController, mountElement, theme)];
92
+ case 10:
84
93
  error_1 = _b.sent();
85
94
  // Clean up theme class if mounting fails
86
95
  removeThemeClass(mountElement, theme);
87
96
  throw error_1;
88
- case 9: return [2 /*return*/];
97
+ case 11: return [2 /*return*/];
89
98
  }
90
99
  });
91
100
  });
92
101
  }
102
+ function validateDeferredSession(session, sessionType) {
103
+ var context = session.context;
104
+ if (!['moto', 'moto-with-save'].includes(sessionType || '') ||
105
+ context.intentMode !== 'deferred' ||
106
+ context.intent !== 'payment' ||
107
+ !Number.isSafeInteger(context.amount) ||
108
+ context.amount <= 0 ||
109
+ typeof context.currency !== 'string' ||
110
+ !/^[a-z]{3}$/i.test(context.currency) ||
111
+ typeof context.publishableKey !== 'string' ||
112
+ context.publishableKey.length === 0 ||
113
+ typeof context.onBehalfOf !== 'string' ||
114
+ context.onBehalfOf.length === 0 ||
115
+ context.onBehalfOf.trim() !== context.onBehalfOf ||
116
+ !Array.isArray(context.paymentMethodTypes) ||
117
+ context.paymentMethodTypes.length !== 1 ||
118
+ context.paymentMethodTypes[0] !== 'card' ||
119
+ 'clientSecret' in context ||
120
+ 'customerSessionClientSecret' in context) {
121
+ throw new InvalidSessionPayloadError('Invalid deferred Stripe MOTO session context');
122
+ }
123
+ }
124
+ function withThemeCleanup(controller, mountElement, theme) {
125
+ var originalUnmount = controller.unmount;
126
+ controller.unmount = function () {
127
+ removeThemeClass(mountElement, theme);
128
+ originalUnmount();
129
+ };
130
+ return controller;
131
+ }
93
132
  export var StripeWidget = {
94
133
  mount: mount,
95
134
  };
@@ -0,0 +1,5 @@
1
+ import { DefaultValues, WidgetController } from '../../common';
2
+ import { EventBroadcaster } from '../../EventBroadcaster';
3
+ import { PaymentWidgetTheme } from '../../widget';
4
+ import { DeferredStripeSession } from '../session';
5
+ export declare function mountDeferredMotoWidget(mountElement: HTMLElement, session: DeferredStripeSession, eventBroadcaster: EventBroadcaster, defaultValues?: DefaultValues, theme?: PaymentWidgetTheme): Promise<WidgetController>;