@lightspeed/online-payments-sdk 1.7.1 → 1.7.2

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.
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * Helpers for identifying partner-initiated sessions from the session
4
+ * metadata `product` value.
5
+ *
6
+ * The processing service encodes the product as
7
+ * `partner:{grantor}::{applicationId}` for integrator (partner) sessions
8
+ * (see `formatPartnerProductValue` in lsp-processing-service). We only need to
9
+ * read the grantor segment here to branch on partner-specific behavior such as
10
+ * custom card consent text.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.getPartnerGrantor = getPartnerGrantor;
14
+ var PARTNER_PRODUCT_PREFIX = 'partner:';
15
+ var PARTNER_PRODUCT_SEGMENT_SEPARATOR = '::';
16
+ /**
17
+ * Parse the grantor segment out of a partner product value.
18
+ * Returns `undefined` for non-partner or malformed product values.
19
+ */
20
+ function getPartnerGrantor(product) {
21
+ if (typeof product !== 'string') {
22
+ return undefined;
23
+ }
24
+ if (!product.startsWith(PARTNER_PRODUCT_PREFIX)) {
25
+ return undefined;
26
+ }
27
+ var suffix = product.slice(PARTNER_PRODUCT_PREFIX.length);
28
+ var separatorIndex = suffix.indexOf(PARTNER_PRODUCT_SEGMENT_SEPARATOR);
29
+ if (separatorIndex <= 0) {
30
+ return undefined;
31
+ }
32
+ var grantor = suffix.slice(0, separatorIndex);
33
+ var applicationId = suffix.slice(separatorIndex + PARTNER_PRODUCT_SEGMENT_SEPARATOR.length);
34
+ // Require both segments: `partner:golf::` (empty applicationId) is malformed.
35
+ if (grantor.length === 0 || applicationId.length === 0) {
36
+ return undefined;
37
+ }
38
+ return grantor;
39
+ }
@@ -50,6 +50,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
50
50
  exports.initStripe = initStripe;
51
51
  exports.createPaymentElementOptions = createPaymentElementOptions;
52
52
  exports.createAddressElementOptions = createAddressElementOptions;
53
+ exports.buildCardConsentText = buildCardConsentText;
54
+ exports.createCardConsentElement = createCardConsentElement;
53
55
  exports.createPaymentElement = createPaymentElement;
54
56
  exports.setupChangeEventHandler = setupChangeEventHandler;
55
57
  exports.createAddressElement = createAddressElement;
@@ -93,11 +95,10 @@ function initStripe(context, theme) {
93
95
  * @param theme - Theme configuration
94
96
  * @param hideNameField - Whether to hide the billing details name field (used when address element is present to avoid duplicates with ACH)
95
97
  * @param businessName - Overrides the merchant name Stripe renders in mandate text (passed to `business.name`)
96
- * @param showCardMandate - Forces Stripe to render its card mandate text (which includes `business.name`). Used for partner payment-with-save flows where Stripe would otherwise not show terms.
98
+ * @param cardMandate - Maps directly to Stripe's `terms.card`: `'always'` forces Stripe's card mandate (with `business.name`), `'never'` hides it (used when the SDK renders its own consent text). Omit to leave Stripe's `'auto'` default.
97
99
  */
98
- function createPaymentElementOptions(defaultValues, theme, hideNameField, businessName, showCardMandate) {
100
+ function createPaymentElementOptions(defaultValues, theme, hideNameField, businessName, cardMandate) {
99
101
  if (hideNameField === void 0) { hideNameField = false; }
100
- if (showCardMandate === void 0) { showCardMandate = false; }
101
102
  var options = __assign(__assign(__assign({ wallets: {
102
103
  applePay: 'never',
103
104
  googlePay: 'never',
@@ -108,7 +109,7 @@ function createPaymentElementOptions(defaultValues, theme, hideNameField, busine
108
109
  name: 'never', // prevent duplicate Name field on the form when using ACH (only when address element is present)
109
110
  },
110
111
  },
111
- })), { terms: __assign({ usBankAccount: 'never' }, (showCardMandate && businessName && { card: 'always' })) }), (businessName && { business: { name: businessName } }));
112
+ })), { terms: __assign({ usBankAccount: 'never' }, (cardMandate && { card: cardMandate })) }), (businessName && { business: { name: businessName } }));
112
113
  if (defaultValues) {
113
114
  options.defaultValues = {
114
115
  billingDetails: {
@@ -186,6 +187,39 @@ function createCustomTermsElement() {
186
187
  }
187
188
  return termsContainer;
188
189
  }
190
+ /**
191
+ * Build a card consent sentence by interpolating the business name and reason
192
+ * into the flow-specific template.
193
+ * @param variant - 'mandatory' for the save-card flow (consent by providing card
194
+ * details), 'checkbox' for the pay-and-save flow (consent by checking the box).
195
+ */
196
+ function buildCardConsentText(businessName, reason, variant) {
197
+ var template = variant === 'checkbox'
198
+ ? terms_1.PARTNER_CARD_CONSENT_TEXT.CHECKBOX_TEMPLATE
199
+ : terms_1.PARTNER_CARD_CONSENT_TEXT.MANDATORY_TEMPLATE;
200
+ return template
201
+ .replace(/\{business\}/g, function () { return businessName; })
202
+ .replace(/\{reason\}/g, function () { return reason; });
203
+ }
204
+ /**
205
+ * Render a card consent element that replaces Stripe's built-in card mandate
206
+ * (suppressed via `terms.card: 'never'`) with the given copy. Pure render
207
+ * primitive — the caller composes the sentence (e.g. via `buildCardConsentText`)
208
+ * so this makes no assumption about the wording. Uses dedicated
209
+ * `.stripe-card-consent-*` classes whose styling mirrors the ACH custom-terms
210
+ * text.
211
+ */
212
+ function createCardConsentElement(consentText) {
213
+ var container = document.createElement('div');
214
+ container.className = 'stripe-card-consent-container';
215
+ var text = document.createElement('p');
216
+ text.className = 'stripe-card-consent-text';
217
+ // Plain text (no links) — use textContent to avoid injecting the composed
218
+ // business name as HTML.
219
+ text.textContent = consentText;
220
+ container.appendChild(text);
221
+ return container;
222
+ }
189
223
  /**
190
224
  * Create and mount payment element
191
225
  */
@@ -285,7 +319,7 @@ function createAddressElement(elements, options, mountElement) {
285
319
  /**
286
320
  * Clean up elements and containers
287
321
  */
288
- function cleanupElements(paymentElement, addressElement, paymentContainer, addressContainer, customTermsContainer) {
322
+ function cleanupElements(paymentElement, addressElement, paymentContainer, addressContainer, customTermsContainer, cardConsentContainer) {
289
323
  paymentElement.unmount();
290
324
  if (addressElement) {
291
325
  addressElement.unmount();
@@ -300,6 +334,9 @@ function cleanupElements(paymentElement, addressElement, paymentContainer, addre
300
334
  if (customTermsContainer) {
301
335
  customTermsContainer.remove();
302
336
  }
337
+ if (cardConsentContainer) {
338
+ cardConsentContainer.remove();
339
+ }
303
340
  }
304
341
  /**
305
342
  * Handle save card operation
@@ -8,7 +8,8 @@
8
8
  * - Replace with translated versions based on locale
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.ACH_TERMS_URL = exports.ACH_TERMS_TEXT = void 0;
11
+ exports.ACH_TERMS_URL = exports.PARTNER_CARD_CONSENT_TEXT = exports.ACH_TERMS_TEXT = void 0;
12
+ exports.getPartnerCardConsentReason = getPartnerCardConsentReason;
12
13
  exports.ACH_TERMS_TEXT = {
13
14
  /**
14
15
  * Main terms text displayed for US Bank Account payments.
@@ -37,6 +38,45 @@ exports.ACH_TERMS_TEXT = {
37
38
  EXPANDED_TERMS_PRIVACY_PREFIX: 'Lightspeed may collect or access certain payment-related information as described in the platform agreement and',
38
39
  PRIVACY_POLICY_LINK_TEXT: 'Privacy Policy',
39
40
  };
41
+ /**
42
+ * Partner card consent text — replaces Stripe's built-in card mandate (whose
43
+ * wording is fixed by Stripe and cannot be customized) with copy the SDK renders
44
+ * itself for partner card payments. `{business}` and `{reason}` are interpolated
45
+ * at render time (reason supplied by the caller, e.g. via
46
+ * `getPartnerCardConsentReason`).
47
+ *
48
+ * NOTE: The SDK does not translate these strings at runtime — they render in
49
+ * English regardless of the session `locale` (which only localizes Stripe's own
50
+ * Element UI). Like the ACH strings above, they are kept as plain constants so
51
+ * they *could* be extracted for Transifex later, but no runtime i18n exists
52
+ * today.
53
+ */
54
+ exports.PARTNER_CARD_CONSENT_TEXT = {
55
+ // save-card flow: consent given by providing card details.
56
+ MANDATORY_TEMPLATE: 'By providing your card information, you authorize {business} to charge your card for {reason} in accordance with their terms.',
57
+ // pay-and-save flow: consent tied to the save checkbox.
58
+ CHECKBOX_TEMPLATE: 'By checking the box above, you authorize {business} to charge your card for {reason} in accordance with their terms.',
59
+ };
60
+ /**
61
+ * Consent reason per partner grantor — what the saved card may be charged for,
62
+ * keyed by the grantor segment of the product (`partner:{grantor}::...`). Add an
63
+ * entry to offer custom card consent to another partner. Hardcoded for now; may
64
+ * become a per-session input from Processing Service later.
65
+ */
66
+ var PARTNER_CARD_CONSENT_REASONS = {
67
+ golf: 'future payments towards charges related to tee-time bookings and/or house accounts as applicable',
68
+ };
69
+ /**
70
+ * The configured consent reason for a partner grantor, or `undefined` if the
71
+ * partner has none (in which case no custom card consent is rendered).
72
+ */
73
+ function getPartnerCardConsentReason(grantor) {
74
+ if (grantor &&
75
+ Object.prototype.hasOwnProperty.call(PARTNER_CARD_CONSENT_REASONS, grantor)) {
76
+ return PARTNER_CARD_CONSENT_REASONS[grantor];
77
+ }
78
+ return undefined;
79
+ }
40
80
  exports.ACH_TERMS_URL = {
41
81
  STRIPE_ACH_AUTHORIZATION: 'https://stripe.com/legal/ach-payments/authorization',
42
82
  PRIVACY_POLICY: 'https://www.lightspeedhq.com/legal/privacy-policy/',
@@ -38,25 +38,39 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.mountMotoWidget = mountMotoWidget;
40
40
  var shared_1 = require("../shared");
41
+ var partnerProduct_1 = require("../partnerProduct");
42
+ var terms_1 = require("../terms");
41
43
  /**
42
44
  * Mount MOTO widget for moto and moto-with-save session types
43
45
  * MOTO (Mail Order/Telephone Order) forms don't require address collection
44
46
  */
45
47
  function mountMotoWidget(mountElement, session, eventBroadcaster, defaultValues, theme) {
46
48
  return __awaiter(this, void 0, void 0, function () {
47
- var _a, stripe, elements, paymentOptions, _b, paymentElement, paymentContainer, customTermsContainer;
49
+ var _a, stripe, elements, businessName, consentReason, renderPartnerConsent, cardMandate, paymentOptions, _b, paymentElement, paymentContainer, customTermsContainer, cardConsentContainer;
48
50
  return __generator(this, function (_c) {
49
51
  switch (_c.label) {
50
52
  case 0: return [4 /*yield*/, (0, shared_1.initStripe)(session.context, theme)];
51
53
  case 1:
52
54
  _a = _c.sent(), stripe = _a.stripe, elements = _a.elements;
53
- paymentOptions = (0, shared_1.createPaymentElementOptions)(defaultValues, theme, false, session.context.businessName);
55
+ businessName = session.context.businessName;
56
+ consentReason = (0, terms_1.getPartnerCardConsentReason)((0, partnerProduct_1.getPartnerGrantor)(session.metadata.product));
57
+ renderPartnerConsent = session.metadata.sessionType === 'save' &&
58
+ !!businessName &&
59
+ !!consentReason;
60
+ cardMandate = renderPartnerConsent
61
+ ? 'never'
62
+ : undefined;
63
+ paymentOptions = (0, shared_1.createPaymentElementOptions)(defaultValues, theme, false, businessName, cardMandate);
54
64
  _b = (0, shared_1.createPaymentElement)(elements, paymentOptions, mountElement, eventBroadcaster), paymentElement = _b.element, paymentContainer = _b.container, customTermsContainer = _b.customTermsContainer;
65
+ if (renderPartnerConsent) {
66
+ cardConsentContainer = (0, shared_1.createCardConsentElement)((0, shared_1.buildCardConsentText)(businessName, consentReason, 'mandatory'));
67
+ mountElement.appendChild(cardConsentContainer);
68
+ }
55
69
  // Setup change handler for payment element only (MOTO forms don't have address element)
56
70
  (0, shared_1.setupChangeEventHandler)(paymentElement, eventBroadcaster);
57
71
  return [2 /*return*/, {
58
72
  unmount: function () {
59
- (0, shared_1.cleanupElements)(paymentElement, null, paymentContainer, null, customTermsContainer);
73
+ (0, shared_1.cleanupElements)(paymentElement, null, paymentContainer, null, customTermsContainer, cardConsentContainer);
60
74
  },
61
75
  submit: (0, shared_1.createSubmitHandler)(stripe, elements, session, eventBroadcaster, null),
62
76
  }];
@@ -38,17 +38,21 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.mountPaymentWidget = mountPaymentWidget;
40
40
  var shared_1 = require("../shared");
41
+ var partnerProduct_1 = require("../partnerProduct");
42
+ var terms_1 = require("../terms");
41
43
  /**
42
44
  * Mount payment widget for payment and payment-with-save session types
43
45
  */
44
46
  function mountPaymentWidget(mountElement, session, eventBroadcaster, defaultValues, theme) {
45
47
  return __awaiter(this, void 0, void 0, function () {
46
- var _a, stripe, elements, productMetadata, isPartnerProduct, addressElement, addressContainer, addressOptions, created, showCardMandate, paymentOptions, _b, paymentElement, paymentContainer, customTermsContainer;
48
+ var _a, stripe, elements, businessName, isPayWithSave, productMetadata, isPartnerProduct, addressElement, addressContainer, addressOptions, created, consentReason, renderPartnerConsent, cardMandate, paymentOptions, _b, paymentElement, paymentContainer, customTermsContainer, cardConsentContainer;
47
49
  return __generator(this, function (_c) {
48
50
  switch (_c.label) {
49
51
  case 0: return [4 /*yield*/, (0, shared_1.initStripe)(session.context, theme)];
50
52
  case 1:
51
53
  _a = _c.sent(), stripe = _a.stripe, elements = _a.elements;
54
+ businessName = session.context.businessName;
55
+ isPayWithSave = session.metadata.sessionType === 'payment-with-save';
52
56
  productMetadata = session.metadata.product;
53
57
  isPartnerProduct = typeof productMetadata === 'string' &&
54
58
  productMetadata.startsWith('partner:');
@@ -60,16 +64,24 @@ function mountPaymentWidget(mountElement, session, eventBroadcaster, defaultValu
60
64
  addressElement = created.element;
61
65
  addressContainer = created.container;
62
66
  }
63
- showCardMandate = session.metadata.sessionType === 'payment-with-save' &&
64
- !!session.context.businessName &&
65
- isPartnerProduct;
66
- paymentOptions = (0, shared_1.createPaymentElementOptions)(defaultValues, theme, !!addressElement, session.context.businessName, showCardMandate);
67
+ consentReason = (0, terms_1.getPartnerCardConsentReason)((0, partnerProduct_1.getPartnerGrantor)(productMetadata));
68
+ renderPartnerConsent = isPayWithSave && !!businessName && !!consentReason;
69
+ cardMandate = renderPartnerConsent
70
+ ? 'never'
71
+ : isPayWithSave && !!businessName && isPartnerProduct
72
+ ? 'always'
73
+ : undefined;
74
+ paymentOptions = (0, shared_1.createPaymentElementOptions)(defaultValues, theme, !!addressElement, businessName, cardMandate);
67
75
  _b = (0, shared_1.createPaymentElement)(elements, paymentOptions, mountElement, eventBroadcaster), paymentElement = _b.element, paymentContainer = _b.container, customTermsContainer = _b.customTermsContainer;
76
+ if (renderPartnerConsent) {
77
+ cardConsentContainer = (0, shared_1.createCardConsentElement)((0, shared_1.buildCardConsentText)(businessName, consentReason, 'checkbox'));
78
+ mountElement.appendChild(cardConsentContainer);
79
+ }
68
80
  // Setup change handler that emits Complete only when both elements are complete
69
81
  (0, shared_1.setupChangeEventHandler)(paymentElement, eventBroadcaster, addressElement);
70
82
  return [2 /*return*/, {
71
83
  unmount: function () {
72
- (0, shared_1.cleanupElements)(paymentElement, addressElement, paymentContainer, addressContainer, customTermsContainer);
84
+ (0, shared_1.cleanupElements)(paymentElement, addressElement, paymentContainer, addressContainer, customTermsContainer, cardConsentContainer);
73
85
  },
74
86
  submit: (0, shared_1.createSubmitHandler)(stripe, elements, session, eventBroadcaster, addressElement),
75
87
  }];
@@ -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.7.1';
6
+ exports.SDK_VERSION = '1.7.2';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Helpers for identifying partner-initiated sessions from the session
3
+ * metadata `product` value.
4
+ *
5
+ * The processing service encodes the product as
6
+ * `partner:{grantor}::{applicationId}` for integrator (partner) sessions
7
+ * (see `formatPartnerProductValue` in lsp-processing-service). We only need to
8
+ * read the grantor segment here to branch on partner-specific behavior such as
9
+ * custom card consent text.
10
+ */
11
+ /**
12
+ * Parse the grantor segment out of a partner product value.
13
+ * Returns `undefined` for non-partner or malformed product values.
14
+ */
15
+ export declare function getPartnerGrantor(product?: string): string | undefined;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Helpers for identifying partner-initiated sessions from the session
3
+ * metadata `product` value.
4
+ *
5
+ * The processing service encodes the product as
6
+ * `partner:{grantor}::{applicationId}` for integrator (partner) sessions
7
+ * (see `formatPartnerProductValue` in lsp-processing-service). We only need to
8
+ * read the grantor segment here to branch on partner-specific behavior such as
9
+ * custom card consent text.
10
+ */
11
+ var PARTNER_PRODUCT_PREFIX = 'partner:';
12
+ var PARTNER_PRODUCT_SEGMENT_SEPARATOR = '::';
13
+ /**
14
+ * Parse the grantor segment out of a partner product value.
15
+ * Returns `undefined` for non-partner or malformed product values.
16
+ */
17
+ export function getPartnerGrantor(product) {
18
+ if (typeof product !== 'string') {
19
+ return undefined;
20
+ }
21
+ if (!product.startsWith(PARTNER_PRODUCT_PREFIX)) {
22
+ return undefined;
23
+ }
24
+ var suffix = product.slice(PARTNER_PRODUCT_PREFIX.length);
25
+ var separatorIndex = suffix.indexOf(PARTNER_PRODUCT_SEGMENT_SEPARATOR);
26
+ if (separatorIndex <= 0) {
27
+ return undefined;
28
+ }
29
+ var grantor = suffix.slice(0, separatorIndex);
30
+ var applicationId = suffix.slice(separatorIndex + PARTNER_PRODUCT_SEGMENT_SEPARATOR.length);
31
+ // Require both segments: `partner:golf::` (empty applicationId) is malformed.
32
+ if (grantor.length === 0 || applicationId.length === 0) {
33
+ return undefined;
34
+ }
35
+ return grantor;
36
+ }
@@ -22,13 +22,29 @@ export declare function initStripe(context: StripeContext, theme?: PaymentWidget
22
22
  * @param theme - Theme configuration
23
23
  * @param hideNameField - Whether to hide the billing details name field (used when address element is present to avoid duplicates with ACH)
24
24
  * @param businessName - Overrides the merchant name Stripe renders in mandate text (passed to `business.name`)
25
- * @param showCardMandate - Forces Stripe to render its card mandate text (which includes `business.name`). Used for partner payment-with-save flows where Stripe would otherwise not show terms.
25
+ * @param cardMandate - Maps directly to Stripe's `terms.card`: `'always'` forces Stripe's card mandate (with `business.name`), `'never'` hides it (used when the SDK renders its own consent text). Omit to leave Stripe's `'auto'` default.
26
26
  */
27
- export declare function createPaymentElementOptions(defaultValues?: DefaultValues, theme?: PaymentWidgetTheme, hideNameField?: boolean, businessName?: string, showCardMandate?: boolean): StripePaymentElementOptions;
27
+ export declare function createPaymentElementOptions(defaultValues?: DefaultValues, theme?: PaymentWidgetTheme, hideNameField?: boolean, businessName?: string, cardMandate?: 'always' | 'never'): StripePaymentElementOptions;
28
28
  /**
29
29
  * Create address element options
30
30
  */
31
31
  export declare function createAddressElementOptions(defaultValues?: DefaultValues): StripeAddressElementOptions;
32
+ /**
33
+ * Build a card consent sentence by interpolating the business name and reason
34
+ * into the flow-specific template.
35
+ * @param variant - 'mandatory' for the save-card flow (consent by providing card
36
+ * details), 'checkbox' for the pay-and-save flow (consent by checking the box).
37
+ */
38
+ export declare function buildCardConsentText(businessName: string, reason: string, variant: 'mandatory' | 'checkbox'): string;
39
+ /**
40
+ * Render a card consent element that replaces Stripe's built-in card mandate
41
+ * (suppressed via `terms.card: 'never'`) with the given copy. Pure render
42
+ * primitive — the caller composes the sentence (e.g. via `buildCardConsentText`)
43
+ * so this makes no assumption about the wording. Uses dedicated
44
+ * `.stripe-card-consent-*` classes whose styling mirrors the ACH custom-terms
45
+ * text.
46
+ */
47
+ export declare function createCardConsentElement(consentText: string): HTMLElement;
32
48
  /**
33
49
  * Create and mount payment element
34
50
  */
@@ -56,7 +72,7 @@ export declare function createAddressElement(elements: StripeElements, options:
56
72
  /**
57
73
  * Clean up elements and containers
58
74
  */
59
- export declare function cleanupElements(paymentElement: StripePaymentElement, addressElement: StripeAddressElement | null, paymentContainer: HTMLElement, addressContainer: HTMLElement | null, customTermsContainer?: HTMLElement): void;
75
+ export declare function cleanupElements(paymentElement: StripePaymentElement, addressElement: StripeAddressElement | null, paymentContainer: HTMLElement, addressContainer: HTMLElement | null, customTermsContainer?: HTMLElement, cardConsentContainer?: HTMLElement): void;
60
76
  /**
61
77
  * Handle save card operation
62
78
  */
@@ -49,7 +49,7 @@ 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 } from './terms';
52
+ import { ACH_TERMS_TEXT, ACH_TERMS_URL, PARTNER_CARD_CONSENT_TEXT } from './terms';
53
53
  /**
54
54
  * Initialize Stripe with the given context
55
55
  */
@@ -81,11 +81,10 @@ export function initStripe(context, theme) {
81
81
  * @param theme - Theme configuration
82
82
  * @param hideNameField - Whether to hide the billing details name field (used when address element is present to avoid duplicates with ACH)
83
83
  * @param businessName - Overrides the merchant name Stripe renders in mandate text (passed to `business.name`)
84
- * @param showCardMandate - Forces Stripe to render its card mandate text (which includes `business.name`). Used for partner payment-with-save flows where Stripe would otherwise not show terms.
84
+ * @param cardMandate - Maps directly to Stripe's `terms.card`: `'always'` forces Stripe's card mandate (with `business.name`), `'never'` hides it (used when the SDK renders its own consent text). Omit to leave Stripe's `'auto'` default.
85
85
  */
86
- export function createPaymentElementOptions(defaultValues, theme, hideNameField, businessName, showCardMandate) {
86
+ export function createPaymentElementOptions(defaultValues, theme, hideNameField, businessName, cardMandate) {
87
87
  if (hideNameField === void 0) { hideNameField = false; }
88
- if (showCardMandate === void 0) { showCardMandate = false; }
89
88
  var options = __assign(__assign(__assign({ wallets: {
90
89
  applePay: 'never',
91
90
  googlePay: 'never',
@@ -96,7 +95,7 @@ export function createPaymentElementOptions(defaultValues, theme, hideNameField,
96
95
  name: 'never', // prevent duplicate Name field on the form when using ACH (only when address element is present)
97
96
  },
98
97
  },
99
- })), { terms: __assign({ usBankAccount: 'never' }, (showCardMandate && businessName && { card: 'always' })) }), (businessName && { business: { name: businessName } }));
98
+ })), { terms: __assign({ usBankAccount: 'never' }, (cardMandate && { card: cardMandate })) }), (businessName && { business: { name: businessName } }));
100
99
  if (defaultValues) {
101
100
  options.defaultValues = {
102
101
  billingDetails: {
@@ -174,6 +173,39 @@ function createCustomTermsElement() {
174
173
  }
175
174
  return termsContainer;
176
175
  }
176
+ /**
177
+ * Build a card consent sentence by interpolating the business name and reason
178
+ * into the flow-specific template.
179
+ * @param variant - 'mandatory' for the save-card flow (consent by providing card
180
+ * details), 'checkbox' for the pay-and-save flow (consent by checking the box).
181
+ */
182
+ export function buildCardConsentText(businessName, reason, variant) {
183
+ var template = variant === 'checkbox'
184
+ ? PARTNER_CARD_CONSENT_TEXT.CHECKBOX_TEMPLATE
185
+ : PARTNER_CARD_CONSENT_TEXT.MANDATORY_TEMPLATE;
186
+ return template
187
+ .replace(/\{business\}/g, function () { return businessName; })
188
+ .replace(/\{reason\}/g, function () { return reason; });
189
+ }
190
+ /**
191
+ * Render a card consent element that replaces Stripe's built-in card mandate
192
+ * (suppressed via `terms.card: 'never'`) with the given copy. Pure render
193
+ * primitive — the caller composes the sentence (e.g. via `buildCardConsentText`)
194
+ * so this makes no assumption about the wording. Uses dedicated
195
+ * `.stripe-card-consent-*` classes whose styling mirrors the ACH custom-terms
196
+ * text.
197
+ */
198
+ export function createCardConsentElement(consentText) {
199
+ var container = document.createElement('div');
200
+ container.className = 'stripe-card-consent-container';
201
+ var text = document.createElement('p');
202
+ text.className = 'stripe-card-consent-text';
203
+ // Plain text (no links) — use textContent to avoid injecting the composed
204
+ // business name as HTML.
205
+ text.textContent = consentText;
206
+ container.appendChild(text);
207
+ return container;
208
+ }
177
209
  /**
178
210
  * Create and mount payment element
179
211
  */
@@ -273,7 +305,7 @@ export function createAddressElement(elements, options, mountElement) {
273
305
  /**
274
306
  * Clean up elements and containers
275
307
  */
276
- export function cleanupElements(paymentElement, addressElement, paymentContainer, addressContainer, customTermsContainer) {
308
+ export function cleanupElements(paymentElement, addressElement, paymentContainer, addressContainer, customTermsContainer, cardConsentContainer) {
277
309
  paymentElement.unmount();
278
310
  if (addressElement) {
279
311
  addressElement.unmount();
@@ -288,6 +320,9 @@ export function cleanupElements(paymentElement, addressElement, paymentContainer
288
320
  if (customTermsContainer) {
289
321
  customTermsContainer.remove();
290
322
  }
323
+ if (cardConsentContainer) {
324
+ cardConsentContainer.remove();
325
+ }
291
326
  }
292
327
  /**
293
328
  * Handle save card operation
@@ -29,6 +29,28 @@ export declare const ACH_TERMS_TEXT: {
29
29
  readonly EXPANDED_TERMS_PRIVACY_PREFIX: "Lightspeed may collect or access certain payment-related information as described in the platform agreement and";
30
30
  readonly PRIVACY_POLICY_LINK_TEXT: "Privacy Policy";
31
31
  };
32
+ /**
33
+ * Partner card consent text — replaces Stripe's built-in card mandate (whose
34
+ * wording is fixed by Stripe and cannot be customized) with copy the SDK renders
35
+ * itself for partner card payments. `{business}` and `{reason}` are interpolated
36
+ * at render time (reason supplied by the caller, e.g. via
37
+ * `getPartnerCardConsentReason`).
38
+ *
39
+ * NOTE: The SDK does not translate these strings at runtime — they render in
40
+ * English regardless of the session `locale` (which only localizes Stripe's own
41
+ * Element UI). Like the ACH strings above, they are kept as plain constants so
42
+ * they *could* be extracted for Transifex later, but no runtime i18n exists
43
+ * today.
44
+ */
45
+ export declare const PARTNER_CARD_CONSENT_TEXT: {
46
+ readonly MANDATORY_TEMPLATE: "By providing your card information, you authorize {business} to charge your card for {reason} in accordance with their terms.";
47
+ readonly CHECKBOX_TEMPLATE: "By checking the box above, you authorize {business} to charge your card for {reason} in accordance with their terms.";
48
+ };
49
+ /**
50
+ * The configured consent reason for a partner grantor, or `undefined` if the
51
+ * partner has none (in which case no custom card consent is rendered).
52
+ */
53
+ export declare function getPartnerCardConsentReason(grantor?: string): string | undefined;
32
54
  export declare const ACH_TERMS_URL: {
33
55
  readonly STRIPE_ACH_AUTHORIZATION: "https://stripe.com/legal/ach-payments/authorization";
34
56
  readonly PRIVACY_POLICY: "https://www.lightspeedhq.com/legal/privacy-policy/";
@@ -34,6 +34,45 @@ export var ACH_TERMS_TEXT = {
34
34
  EXPANDED_TERMS_PRIVACY_PREFIX: 'Lightspeed may collect or access certain payment-related information as described in the platform agreement and',
35
35
  PRIVACY_POLICY_LINK_TEXT: 'Privacy Policy',
36
36
  };
37
+ /**
38
+ * Partner card consent text — replaces Stripe's built-in card mandate (whose
39
+ * wording is fixed by Stripe and cannot be customized) with copy the SDK renders
40
+ * itself for partner card payments. `{business}` and `{reason}` are interpolated
41
+ * at render time (reason supplied by the caller, e.g. via
42
+ * `getPartnerCardConsentReason`).
43
+ *
44
+ * NOTE: The SDK does not translate these strings at runtime — they render in
45
+ * English regardless of the session `locale` (which only localizes Stripe's own
46
+ * Element UI). Like the ACH strings above, they are kept as plain constants so
47
+ * they *could* be extracted for Transifex later, but no runtime i18n exists
48
+ * today.
49
+ */
50
+ export var PARTNER_CARD_CONSENT_TEXT = {
51
+ // save-card flow: consent given by providing card details.
52
+ MANDATORY_TEMPLATE: 'By providing your card information, you authorize {business} to charge your card for {reason} in accordance with their terms.',
53
+ // pay-and-save flow: consent tied to the save checkbox.
54
+ CHECKBOX_TEMPLATE: 'By checking the box above, you authorize {business} to charge your card for {reason} in accordance with their terms.',
55
+ };
56
+ /**
57
+ * Consent reason per partner grantor — what the saved card may be charged for,
58
+ * keyed by the grantor segment of the product (`partner:{grantor}::...`). Add an
59
+ * entry to offer custom card consent to another partner. Hardcoded for now; may
60
+ * become a per-session input from Processing Service later.
61
+ */
62
+ var PARTNER_CARD_CONSENT_REASONS = {
63
+ golf: 'future payments towards charges related to tee-time bookings and/or house accounts as applicable',
64
+ };
65
+ /**
66
+ * The configured consent reason for a partner grantor, or `undefined` if the
67
+ * partner has none (in which case no custom card consent is rendered).
68
+ */
69
+ export function getPartnerCardConsentReason(grantor) {
70
+ if (grantor &&
71
+ Object.prototype.hasOwnProperty.call(PARTNER_CARD_CONSENT_REASONS, grantor)) {
72
+ return PARTNER_CARD_CONSENT_REASONS[grantor];
73
+ }
74
+ return undefined;
75
+ }
37
76
  export var ACH_TERMS_URL = {
38
77
  STRIPE_ACH_AUTHORIZATION: 'https://stripe.com/legal/ach-payments/authorization',
39
78
  PRIVACY_POLICY: 'https://www.lightspeedhq.com/legal/privacy-policy/',
@@ -34,26 +34,40 @@ 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 { initStripe, createPaymentElementOptions, createPaymentElement, cleanupElements, createSubmitHandler, setupChangeEventHandler, } from '../shared';
37
+ import { initStripe, createPaymentElementOptions, createPaymentElement, buildCardConsentText, createCardConsentElement, cleanupElements, createSubmitHandler, setupChangeEventHandler, } from '../shared';
38
+ import { getPartnerGrantor } from '../partnerProduct';
39
+ import { getPartnerCardConsentReason } from '../terms';
38
40
  /**
39
41
  * Mount MOTO widget for moto and moto-with-save session types
40
42
  * MOTO (Mail Order/Telephone Order) forms don't require address collection
41
43
  */
42
44
  export function mountMotoWidget(mountElement, session, eventBroadcaster, defaultValues, theme) {
43
45
  return __awaiter(this, void 0, void 0, function () {
44
- var _a, stripe, elements, paymentOptions, _b, paymentElement, paymentContainer, customTermsContainer;
46
+ var _a, stripe, elements, businessName, consentReason, renderPartnerConsent, cardMandate, paymentOptions, _b, paymentElement, paymentContainer, customTermsContainer, cardConsentContainer;
45
47
  return __generator(this, function (_c) {
46
48
  switch (_c.label) {
47
49
  case 0: return [4 /*yield*/, initStripe(session.context, theme)];
48
50
  case 1:
49
51
  _a = _c.sent(), stripe = _a.stripe, elements = _a.elements;
50
- paymentOptions = createPaymentElementOptions(defaultValues, theme, false, session.context.businessName);
52
+ businessName = session.context.businessName;
53
+ consentReason = getPartnerCardConsentReason(getPartnerGrantor(session.metadata.product));
54
+ renderPartnerConsent = session.metadata.sessionType === 'save' &&
55
+ !!businessName &&
56
+ !!consentReason;
57
+ cardMandate = renderPartnerConsent
58
+ ? 'never'
59
+ : undefined;
60
+ paymentOptions = createPaymentElementOptions(defaultValues, theme, false, businessName, cardMandate);
51
61
  _b = createPaymentElement(elements, paymentOptions, mountElement, eventBroadcaster), paymentElement = _b.element, paymentContainer = _b.container, customTermsContainer = _b.customTermsContainer;
62
+ if (renderPartnerConsent) {
63
+ cardConsentContainer = createCardConsentElement(buildCardConsentText(businessName, consentReason, 'mandatory'));
64
+ mountElement.appendChild(cardConsentContainer);
65
+ }
52
66
  // Setup change handler for payment element only (MOTO forms don't have address element)
53
67
  setupChangeEventHandler(paymentElement, eventBroadcaster);
54
68
  return [2 /*return*/, {
55
69
  unmount: function () {
56
- cleanupElements(paymentElement, null, paymentContainer, null, customTermsContainer);
70
+ cleanupElements(paymentElement, null, paymentContainer, null, customTermsContainer, cardConsentContainer);
57
71
  },
58
72
  submit: createSubmitHandler(stripe, elements, session, eventBroadcaster, null),
59
73
  }];
@@ -34,18 +34,22 @@ 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 { initStripe, createPaymentElementOptions, createAddressElementOptions, createPaymentElement, createAddressElement, cleanupElements, createSubmitHandler, setupChangeEventHandler, } from '../shared';
37
+ import { initStripe, createPaymentElementOptions, createAddressElementOptions, createPaymentElement, createAddressElement, buildCardConsentText, createCardConsentElement, cleanupElements, createSubmitHandler, setupChangeEventHandler, } from '../shared';
38
+ import { getPartnerGrantor } from '../partnerProduct';
39
+ import { getPartnerCardConsentReason } from '../terms';
38
40
  /**
39
41
  * Mount payment widget for payment and payment-with-save session types
40
42
  */
41
43
  export function mountPaymentWidget(mountElement, session, eventBroadcaster, defaultValues, theme) {
42
44
  return __awaiter(this, void 0, void 0, function () {
43
- var _a, stripe, elements, productMetadata, isPartnerProduct, addressElement, addressContainer, addressOptions, created, showCardMandate, paymentOptions, _b, paymentElement, paymentContainer, customTermsContainer;
45
+ var _a, stripe, elements, businessName, isPayWithSave, productMetadata, isPartnerProduct, addressElement, addressContainer, addressOptions, created, consentReason, renderPartnerConsent, cardMandate, paymentOptions, _b, paymentElement, paymentContainer, customTermsContainer, cardConsentContainer;
44
46
  return __generator(this, function (_c) {
45
47
  switch (_c.label) {
46
48
  case 0: return [4 /*yield*/, initStripe(session.context, theme)];
47
49
  case 1:
48
50
  _a = _c.sent(), stripe = _a.stripe, elements = _a.elements;
51
+ businessName = session.context.businessName;
52
+ isPayWithSave = session.metadata.sessionType === 'payment-with-save';
49
53
  productMetadata = session.metadata.product;
50
54
  isPartnerProduct = typeof productMetadata === 'string' &&
51
55
  productMetadata.startsWith('partner:');
@@ -57,16 +61,24 @@ export function mountPaymentWidget(mountElement, session, eventBroadcaster, defa
57
61
  addressElement = created.element;
58
62
  addressContainer = created.container;
59
63
  }
60
- showCardMandate = session.metadata.sessionType === 'payment-with-save' &&
61
- !!session.context.businessName &&
62
- isPartnerProduct;
63
- paymentOptions = createPaymentElementOptions(defaultValues, theme, !!addressElement, session.context.businessName, showCardMandate);
64
+ consentReason = getPartnerCardConsentReason(getPartnerGrantor(productMetadata));
65
+ renderPartnerConsent = isPayWithSave && !!businessName && !!consentReason;
66
+ cardMandate = renderPartnerConsent
67
+ ? 'never'
68
+ : isPayWithSave && !!businessName && isPartnerProduct
69
+ ? 'always'
70
+ : undefined;
71
+ paymentOptions = createPaymentElementOptions(defaultValues, theme, !!addressElement, businessName, cardMandate);
64
72
  _b = createPaymentElement(elements, paymentOptions, mountElement, eventBroadcaster), paymentElement = _b.element, paymentContainer = _b.container, customTermsContainer = _b.customTermsContainer;
73
+ if (renderPartnerConsent) {
74
+ cardConsentContainer = createCardConsentElement(buildCardConsentText(businessName, consentReason, 'checkbox'));
75
+ mountElement.appendChild(cardConsentContainer);
76
+ }
65
77
  // Setup change handler that emits Complete only when both elements are complete
66
78
  setupChangeEventHandler(paymentElement, eventBroadcaster, addressElement);
67
79
  return [2 /*return*/, {
68
80
  unmount: function () {
69
- cleanupElements(paymentElement, addressElement, paymentContainer, addressContainer, customTermsContainer);
81
+ cleanupElements(paymentElement, addressElement, paymentContainer, addressContainer, customTermsContainer, cardConsentContainer);
70
82
  },
71
83
  submit: createSubmitHandler(stripe, elements, session, eventBroadcaster, addressElement),
72
84
  }];
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "1.7.1";
1
+ export declare const SDK_VERSION = "1.7.2";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // This file is auto-generated during the build process
2
2
  // DO NOT EDIT MANUALLY - Version is extracted from package.json
3
- export var SDK_VERSION = '1.7.1';
3
+ export var SDK_VERSION = '1.7.2';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightspeed/online-payments-sdk",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "description": "Process online-payments with Lightspeed Payments",
5
5
  "author": "Lightspeed Commerce Inc.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -66,6 +66,24 @@
66
66
  margin-bottom: 0;
67
67
  }
68
68
 
69
+ /* Custom card consent text for partner card payments. Replaces Stripe's
70
+ built-in card mandate (suppressed via terms.card: 'never'). Typography matches
71
+ the ACH custom terms text above. */
72
+
73
+ .stripe-card-consent-container {
74
+ padding: 0;
75
+ }
76
+
77
+ .stripe-card-consent-text {
78
+ margin: 16px 0 0 0;
79
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
80
+ Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
81
+ font-size: 0.93rem;
82
+ font-weight: 400;
83
+ line-height: 1.5;
84
+ color: #30313d;
85
+ }
86
+
69
87
  /* Invoicing theme: only override font-family to match the Inter font
70
88
  passed to Stripe Elements via the invoicing theme config. */
71
89
 
@@ -74,7 +92,8 @@
74
92
  }
75
93
 
76
94
  .lsp-theme-invoicing .stripe-custom-terms-text,
77
- .lsp-theme-invoicing .stripe-expandable-terms-content p {
95
+ .lsp-theme-invoicing .stripe-expandable-terms-content p,
96
+ .lsp-theme-invoicing .stripe-card-consent-text {
78
97
  font-family: 'Inter', system-ui, sans-serif;
79
98
  }
80
99