@delopay/sdk 0.163.0 → 0.165.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.
package/dist/index.cjs CHANGED
@@ -63,6 +63,7 @@ __export(index_exports, {
63
63
  DEFAULT_BADGES_DARK: () => DEFAULT_BADGES_DARK,
64
64
  DEFAULT_BRANDING: () => DEFAULT_BRANDING,
65
65
  DEFAULT_BRANDING_DARK: () => DEFAULT_BRANDING_DARK,
66
+ DEFAULT_NOTICES: () => DEFAULT_NOTICES,
66
67
  Delopay: () => Delopay,
67
68
  DelopayAuthenticationError: () => DelopayAuthenticationError,
68
69
  DelopayError: () => DelopayError,
@@ -84,6 +85,12 @@ __export(index_exports, {
84
85
  NATIVE_PANE_CATEGORY_KEYS: () => NATIVE_PANE_CATEGORY_KEYS,
85
86
  NATIVE_PANE_ICON_KEYS: () => NATIVE_PANE_ICON_KEYS,
86
87
  NOMUPAY_FORM_STYLES: () => NOMUPAY_FORM_STYLES,
88
+ NOTICES_PER_SECTION: () => NOTICES_PER_SECTION,
89
+ NOTICE_ICONS: () => NOTICE_ICONS,
90
+ NOTICE_ICON_FALLBACK: () => NOTICE_ICON_FALLBACK,
91
+ NOTICE_ICON_PATHS: () => NOTICE_ICON_PATHS,
92
+ NOTICE_LABEL_MAX_LENGTH: () => NOTICE_LABEL_MAX_LENGTH,
93
+ NOTICE_SECTIONS: () => NOTICE_SECTIONS,
87
94
  OperationLimits: () => OperationLimits,
88
95
  PANES_MAX: () => PANES_MAX,
89
96
  PANE_CATEGORY_KEYS: () => PANE_CATEGORY_KEYS,
@@ -141,6 +148,7 @@ __export(index_exports, {
141
148
  cloneBranding: () => cloneBranding,
142
149
  cloneCustomField: () => cloneCustomField,
143
150
  cloneNativePane: () => cloneNativePane,
151
+ cloneNotice: () => cloneNotice,
144
152
  clonePane: () => clonePane,
145
153
  compareSections: () => compareSections,
146
154
  contrastRatio: () => contrastRatio,
@@ -156,11 +164,13 @@ __export(index_exports, {
156
164
  decodeMethodSectionLabels: () => decodeMethodSectionLabels,
157
165
  decodeMethodSectionOrder: () => decodeMethodSectionOrder,
158
166
  decodeNativePanes: () => decodeNativePanes,
167
+ decodeNotices: () => decodeNotices,
159
168
  decodePanes: () => decodePanes,
160
169
  defaultBranding: () => defaultBranding,
161
170
  defaultCustomFieldVisibility: () => defaultCustomFieldVisibility,
162
171
  defaultMethodSectionOrder: () => defaultMethodSectionOrder,
163
172
  defaultNativePane: () => defaultNativePane,
173
+ defaultNotices: () => defaultNotices,
164
174
  defaultOperatorForSource: () => defaultOperatorForSource,
165
175
  defaultPane: () => defaultPane,
166
176
  emptyPaneCatalog: () => emptyPaneCatalog,
@@ -170,6 +180,7 @@ __export(index_exports, {
170
180
  encodeMethodSectionLabels: () => encodeMethodSectionLabels,
171
181
  encodeMethodSectionOrder: () => encodeMethodSectionOrder,
172
182
  encodeNativePanes: () => encodeNativePanes,
183
+ encodeNotices: () => encodeNotices,
173
184
  encodePanes: () => encodePanes,
174
185
  evaluateCustomFieldCondition: () => evaluateCustomFieldCondition,
175
186
  evaluateCustomFieldVisibility: () => evaluateCustomFieldVisibility,
@@ -180,13 +191,17 @@ __export(index_exports, {
180
191
  inputPadValue: () => inputPadValue,
181
192
  isCheckboxChecked: () => isCheckboxChecked,
182
193
  isDarkSurface: () => isDarkSurface,
194
+ isDefaultBadgeSet: () => isDefaultBadgeSet,
183
195
  isHexColor: () => isHexColor,
196
+ isNoticeIcon: () => isNoticeIcon,
184
197
  isSubscriptionDisputeWebhook: () => isSubscriptionDisputeWebhook,
185
198
  isSubscriptionLifecycleWebhook: () => isSubscriptionLifecycleWebhook,
186
199
  knownSectionLabelKey: () => knownSectionLabelKey,
187
200
  leaf: () => leaf,
188
201
  logoDimensions: () => logoDimensions,
189
202
  nativePaneMethodInfo: () => nativePaneMethodInfo,
203
+ noticeText: () => noticeText,
204
+ noticesIn: () => noticesIn,
190
205
  offerablePaneMethods: () => offerablePaneMethods,
191
206
  paneCatalogFor: () => paneCatalogFor,
192
207
  paneDisplayDefaults: () => paneDisplayDefaults,
@@ -195,6 +210,7 @@ __export(index_exports, {
195
210
  paneViewVisibility: () => paneViewVisibility,
196
211
  parseCustomFieldsLoose: () => parseCustomFieldsLoose,
197
212
  parseImportedBranding: () => parseImportedBranding,
213
+ parseNoticesLoose: () => parseNoticesLoose,
198
214
  programToTree: () => programToTree,
199
215
  radiusValue: () => radiusValue,
200
216
  readableInkOn: () => readableInkOn,
@@ -202,12 +218,14 @@ __export(index_exports, {
202
218
  resolveAirwallexLabelStyle: () => resolveAirwallexLabelStyle,
203
219
  resolveApplePayButtonStyle: () => resolveApplePayButtonStyle,
204
220
  resolveGooglePayButtonColor: () => resolveGooglePayButtonColor,
221
+ resolveNoticesAndBadges: () => resolveNoticesAndBadges,
205
222
  resolveStripeTheme: () => resolveStripeTheme,
206
223
  ruleMatchToTree: () => ruleMatchToTree,
207
224
  sanitizeCustomCss: () => sanitizeCustomCss,
208
225
  sectionLabel: () => sectionLabel,
209
226
  sectionOrderOf: () => sectionOrderOf,
210
227
  shadowFor: () => shadowFor,
228
+ shownNotices: () => shownNotices,
211
229
  stripeSpacingValues: () => stripeSpacingValues,
212
230
  surchargeFigurePlan: () => surchargeFigurePlan,
213
231
  surchargeShowsLabel: () => surchargeShowsLabel,
@@ -1986,6 +2004,16 @@ var Payments = class {
1986
2004
  * Not the hosted-shop settlement API under `settlement.*`, which is host →
1987
2005
  * shop owner and manual; this is acquirer → merchant bank, per payment.
1988
2006
  *
2007
+ * Each attempt also carries what the rail itself took for it —
2008
+ * `processor_cost_amount` with its own `processor_cost_currency` and
2009
+ * `processor_cost_exponent`, the `processor_cost_source` that says how the
2010
+ * figure was obtained, the `processor_cost_basis` behind an estimate, and
2011
+ * the `connector_method_code` it was priced under. Recorded for **every**
2012
+ * merchant, not only those that host shops. Never present an `estimated`
2013
+ * cost as an observation, and read an absent `processor_cost_source` as
2014
+ * "nothing was ever written", which is not the `unavailable` the rail
2015
+ * answers with.
2016
+ *
1989
2017
  * `GET /payments/{paymentId}/settlement`
1990
2018
  *
1991
2019
  * @param paymentId - The payment intent ID.
@@ -2004,6 +2032,9 @@ var Payments = class {
2004
2032
  * if (attempt.status === 'paid_out') {
2005
2033
  * console.log(attempt.paid_out_at, attempt.payout_reference);
2006
2034
  * }
2035
+ * if (attempt.processor_cost_source === 'estimated') {
2036
+ * // Label it an estimate — it was derived from a cost schedule.
2037
+ * }
2007
2038
  * }
2008
2039
  * ```
2009
2040
  */
@@ -5239,6 +5270,65 @@ var Settlement = class {
5239
5270
  ...options
5240
5271
  });
5241
5272
  }
5273
+ /**
5274
+ * What a period's payments cost at the rails, grouped by shop, connector
5275
+ * and payment method. Answers for **every** merchant, including one that
5276
+ * hosts nobody — it reads the per-attempt settlement record rather than the
5277
+ * settlement lines, which exist only where hosting fees are configured.
5278
+ *
5279
+ * A sibling of {@link Settlement.cost} rather than a widening of it. That
5280
+ * endpoint is a profit statement whose revenue term is hosting income, and
5281
+ * a merchant who hosts nobody has none. This publishes cost and never
5282
+ * margin.
5283
+ *
5284
+ * Send `year` and `month` together to report one UTC calendar month, or
5285
+ * neither for the running month so far.
5286
+ *
5287
+ * **The response carries no period total, and a caller must not compute
5288
+ * one here.** Every figure keeps the unit the rail reported it in, and the
5289
+ * unit is part of each bucket's key, so adding two buckets is not addition.
5290
+ * Convert caller-side if you need one number, and say which rates you used.
5291
+ * The counts are totalled, because a count has no unit.
5292
+ *
5293
+ * Two distinctions not to flatten when rendering a bucket:
5294
+ * `cost_amount` covers `reported_count + estimated_count` attempts out of
5295
+ * `attempt_count` and is a lower bound whenever those disagree; and
5296
+ * `unavailable_count` (the rail says it has no figure) means something
5297
+ * different from `unwritten_count` (a gap in our own recording).
5298
+ *
5299
+ * **Merchant scope.** A token scoped to a single shop, a role narrowed to
5300
+ * specific connector accounts, or a role without the settlement-read
5301
+ * permission group is refused `403`: what a rail charged is the merchant's
5302
+ * own cost base, and here it is the whole response, so there is nothing
5303
+ * coherent to redact down to. Gate the surface on the caller's scope rather
5304
+ * than calling it and handling the failure.
5305
+ *
5306
+ * `GET /settlement/processor-cost`
5307
+ *
5308
+ * @example
5309
+ * ```typescript
5310
+ * const period = await delopay.settlement.processorCost({
5311
+ * test_mode: false,
5312
+ * year: 2026,
5313
+ * month: 7,
5314
+ * });
5315
+ * for (const bucket of period.buckets) {
5316
+ * // Each bucket is its own unit — render per bucket, never a sum.
5317
+ * console.log(bucket.connector, bucket.cost_amount, bucket.cost_currency);
5318
+ * }
5319
+ * ```
5320
+ */
5321
+ async processorCost(params, options) {
5322
+ return this.request("GET", "/settlement/processor-cost", {
5323
+ query: {
5324
+ profile_id: params?.profile_id,
5325
+ test_mode: params?.test_mode,
5326
+ year: params?.year,
5327
+ month: params?.month
5328
+ },
5329
+ ...options
5330
+ });
5331
+ }
5242
5332
  /**
5243
5333
  * List generated settlement statements, newest first.
5244
5334
  *
@@ -6647,6 +6737,66 @@ function surchargeWordFor(branding, isDiscount, locale) {
6647
6737
  locale
6648
6738
  );
6649
6739
  }
6740
+ var NOTICE_SECTIONS = ["summary", "payment"];
6741
+ var NOTICES_PER_SECTION = 3;
6742
+ var NOTICE_LABEL_MAX_LENGTH = 80;
6743
+ var NOTICE_ICONS = [
6744
+ "lock",
6745
+ "shield",
6746
+ "check",
6747
+ "truck",
6748
+ "package",
6749
+ "refresh",
6750
+ "star",
6751
+ "support",
6752
+ "globe",
6753
+ "tag",
6754
+ "heart",
6755
+ "leaf",
6756
+ "clock",
6757
+ "bank"
6758
+ ];
6759
+ var NOTICE_ICON_FALLBACK = "check";
6760
+ var NOTICE_ICON_PATHS = {
6761
+ lock: [
6762
+ "M6 10h12a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2z",
6763
+ "M8 10V7a4 4 0 0 1 8 0v3"
6764
+ ],
6765
+ shield: ["M12 3l7 3v6c0 4.2-2.9 7.6-7 9-4.1-1.4-7-4.8-7-9V6z", "M9 12l2.5 2.5 4-4.5"],
6766
+ check: ["M21 12a9 9 0 1 1-18 0a9 9 0 1 1 18 0z", "M8 12.5l3 3 5-6"],
6767
+ truck: [
6768
+ "M3 7.5h10v9H3z",
6769
+ "M13 11h4l3 3v2.5h-7z",
6770
+ "M8.8 18a1.8 1.8 0 1 1-3.6 0a1.8 1.8 0 1 1 3.6 0z",
6771
+ "M18.8 18a1.8 1.8 0 1 1-3.6 0a1.8 1.8 0 1 1 3.6 0z"
6772
+ ],
6773
+ package: [
6774
+ "M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z",
6775
+ "M12 22V12",
6776
+ "M3.29 7L12 12l8.71-5"
6777
+ ],
6778
+ refresh: ["M20 12a8 8 0 1 1-2.5-5.8", "M20 4v5h-5"],
6779
+ star: ["m12 3.6 2.6 5.3 5.9.9-4.3 4.1 1 5.8-5.2-2.7-5.2 2.7 1-5.8-4.3-4.1 5.9-.9z"],
6780
+ support: [
6781
+ "M5 13v-1a7 7 0 0 1 14 0v1",
6782
+ "M3.5 14.5a1.8 1.8 0 0 1 1.8-1.8H7v5H5.3a1.8 1.8 0 0 1-1.8-1.8z",
6783
+ "M20.5 14.5a1.8 1.8 0 0 0-1.8-1.8H17v5h1.7a1.8 1.8 0 0 0 1.8-1.8z",
6784
+ "M19 17.7v.8a2.5 2.5 0 0 1-2.5 2.5H13"
6785
+ ],
6786
+ globe: [
6787
+ "M21 12a9 9 0 1 1-18 0a9 9 0 1 1 18 0z",
6788
+ "M3 12h18",
6789
+ "M12 3a14 14 0 0 1 0 18a14 14 0 0 1 0-18z"
6790
+ ],
6791
+ tag: ["M11 3H4v7l10 10 7-7z", "M9.1 7a1.3 1.3 0 1 1-2.6 0a1.3 1.3 0 1 1 2.6 0z"],
6792
+ heart: ["M12 20s-7-4.4-7-9.3A3.9 3.9 0 0 1 12 8a3.9 3.9 0 0 1 7 2.7c0 4.9-7 9.3-7 9.3z"],
6793
+ leaf: ["M20 4c0 9-5.4 13-9.5 13A4.5 4.5 0 0 1 6 12.5C6 7 12 4 20 4z", "M4 20c2-4.5 5-7.5 9-9.5"],
6794
+ clock: ["M21 12a9 9 0 1 1-18 0a9 9 0 1 1 18 0z", "M12 7v5l3 2"],
6795
+ bank: ["M3 9.5 12 4l9 5.5", "M5 10v8M9.5 10v8M14.5 10v8M19 10v8", "M3 20h18"]
6796
+ };
6797
+ function isNoticeIcon(value) {
6798
+ return typeof value === "string" && NOTICE_ICONS.includes(value);
6799
+ }
6650
6800
  var FONT_STACKS = {
6651
6801
  inter: "'Inter Variable', 'Inter', system-ui, -apple-system, sans-serif",
6652
6802
  system: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif",
@@ -6816,6 +6966,96 @@ var DEFAULT_BADGES_DARK = [
6816
6966
  borderColor: "#1e40af"
6817
6967
  }
6818
6968
  ];
6969
+ var DEFAULT_NOTICES = [
6970
+ {
6971
+ id: "secure",
6972
+ section: "summary",
6973
+ icon: "shield",
6974
+ label: "Secure payment",
6975
+ labelTranslations: {
6976
+ de: "Sichere Zahlung",
6977
+ fr: "Paiement s\xE9curis\xE9",
6978
+ es: "Pago seguro",
6979
+ it: "Pagamento sicuro",
6980
+ nl: "Veilig betalen",
6981
+ zh: "\u5B89\u5168\u652F\u4ED8",
6982
+ "zh-Hant": "\u5B89\u5168\u4ED8\u6B3E",
6983
+ hi: "\u0938\u0941\u0930\u0915\u094D\u0937\u093F\u0924 \u092D\u0941\u0917\u0924\u093E\u0928",
6984
+ ar: "\u062F\u0641\u0639 \u0622\u0645\u0646"
6985
+ }
6986
+ },
6987
+ {
6988
+ id: "ssl",
6989
+ section: "summary",
6990
+ icon: "lock",
6991
+ label: "Encrypted \xB7 256-bit SSL",
6992
+ labelTranslations: {
6993
+ de: "Verschl\xFCsselt \xB7 256-Bit-SSL",
6994
+ fr: "Chiffr\xE9 \xB7 SSL 256 bits",
6995
+ es: "Cifrado \xB7 SSL de 256 bits",
6996
+ it: "Crittografato \xB7 SSL a 256 bit",
6997
+ nl: "Versleuteld \xB7 256-bit SSL",
6998
+ zh: "\u52A0\u5BC6 \xB7 256 \u4F4D SSL",
6999
+ "zh-Hant": "\u52A0\u5BC6 \xB7 256 \u4F4D\u5143 SSL",
7000
+ hi: "\u090F\u0928\u094D\u0915\u094D\u0930\u093F\u092A\u094D\u091F\u0947\u0921 \xB7 256-\u092C\u093F\u091F SSL",
7001
+ ar: "\u0645\u0634\u0641\u0651\u0631 \xB7 SSL \u0628\u0637\u0648\u0644 256 \u0628\u062A"
7002
+ }
7003
+ }
7004
+ ];
7005
+ function defaultNotices() {
7006
+ return DEFAULT_NOTICES.map(cloneNotice);
7007
+ }
7008
+ function cloneNotice(notice) {
7009
+ return { ...notice, labelTranslations: { ...notice.labelTranslations } };
7010
+ }
7011
+ function hexChannels(hex) {
7012
+ const raw = /^#([0-9a-f]{6})$/i.exec(hex.trim())?.[1];
7013
+ if (!raw) return null;
7014
+ return [
7015
+ parseInt(raw.slice(0, 2), 16),
7016
+ parseInt(raw.slice(2, 4), 16),
7017
+ parseInt(raw.slice(4, 6), 16)
7018
+ ];
7019
+ }
7020
+ function isThemeTintedBadgeColouring(textColor, fill, border) {
7021
+ const primary = hexChannels(textColor);
7022
+ const bg = hexChannels(fill);
7023
+ const line = hexChannels(border);
7024
+ if (!primary || !bg || !line) return false;
7025
+ const mixed = (surface, to, share) => Math.round(surface + (to - surface) * share);
7026
+ return primary.every((p, i) => {
7027
+ for (let surface = 0; surface <= 255; surface += 1) {
7028
+ if (mixed(surface, p, 0.08) === bg[i] && mixed(surface, p, 0.3) === line[i]) return true;
7029
+ }
7030
+ return false;
7031
+ });
7032
+ }
7033
+ function isDefaultBadgeSet(badges) {
7034
+ if (badges.length !== DEFAULT_BADGES.length) return false;
7035
+ const sameContent = badges.every(
7036
+ (badge, i) => badge.id === DEFAULT_BADGES[i]?.id && badge.label === DEFAULT_BADGES[i]?.label
7037
+ );
7038
+ if (!sameContent) return false;
7039
+ const colouring = (set) => set.map((b) => [b.textColor, b.backgroundColor, b.borderColor ?? ""].join("|").toLowerCase()).join("/");
7040
+ const mine = colouring(badges);
7041
+ if (mine === colouring(DEFAULT_BADGES) || mine === colouring(DEFAULT_BADGES_DARK)) return true;
7042
+ const [first] = badges;
7043
+ const border = first?.borderColor;
7044
+ if (!first || !border) return false;
7045
+ const same = (a, b) => (a ?? "").toLowerCase() === b.toLowerCase();
7046
+ return badges.every(
7047
+ (b) => same(b.textColor, first.textColor) && same(b.backgroundColor, first.backgroundColor) && same(b.borderColor, border)
7048
+ ) && isThemeTintedBadgeColouring(first.textColor, first.backgroundColor, border);
7049
+ }
7050
+ function resolveNoticesAndBadges(savedBadges, savedNotices) {
7051
+ if (savedNotices !== null) {
7052
+ return { trustBadges: savedBadges ?? [], notices: savedNotices };
7053
+ }
7054
+ if (savedBadges === null || isDefaultBadgeSet(savedBadges)) {
7055
+ return { trustBadges: [], notices: defaultNotices() };
7056
+ }
7057
+ return { trustBadges: savedBadges, notices: [] };
7058
+ }
6819
7059
  var METHOD_SECTION_ORDER_KEY = "methodSectionOrder";
6820
7060
  var METHOD_SECTION_LABELS_KEY = "methodSectionLabels";
6821
7061
  var MIN_SECTION_ORDER = 1;
@@ -7102,7 +7342,11 @@ var DEFAULT_BRANDING_BASE = {
7102
7342
  var DEFAULT_BRANDING = {
7103
7343
  ...DEFAULT_BRANDING_BASE,
7104
7344
  ...LIGHT_PALETTE,
7105
- trustBadges: DEFAULT_BADGES.map((b) => ({ ...b })),
7345
+ // No badges by default since todos#218 — notices took their place.
7346
+ // `DEFAULT_BADGES` stays exported because `isDefaultBadgeSet` still has to
7347
+ // recognise the pair on shops saved before.
7348
+ trustBadges: [],
7349
+ notices: defaultNotices(),
7106
7350
  customFields: [],
7107
7351
  methodSectionOrder: defaultMethodSectionOrder(),
7108
7352
  methodSectionLabels: {}
@@ -7125,7 +7369,8 @@ var DEFAULT_BRANDING_DARK = {
7125
7369
  // end of the ramp — 10.69 and 9.29 against the dark surface, also pinned.
7126
7370
  surchargeColor: "#fbbf24",
7127
7371
  discountColor: "#34d399",
7128
- trustBadges: DEFAULT_BADGES_DARK.map((b) => ({ ...b })),
7372
+ trustBadges: [],
7373
+ notices: defaultNotices(),
7129
7374
  customFields: [],
7130
7375
  methodSectionOrder: defaultMethodSectionOrder(),
7131
7376
  methodSectionLabels: {}
@@ -7137,6 +7382,7 @@ function cloneBranding(b) {
7137
7382
  return {
7138
7383
  ...b,
7139
7384
  trustBadges: b.trustBadges.map((badge) => ({ ...badge })),
7385
+ notices: b.notices.map(cloneNotice),
7140
7386
  customFields: b.customFields.map(cloneCustomField),
7141
7387
  methodSectionOrder: { ...b.methodSectionOrder },
7142
7388
  methodSectionLabels: Object.fromEntries(
@@ -7246,6 +7492,80 @@ function encodeBadges(badges) {
7246
7492
  }))
7247
7493
  );
7248
7494
  }
7495
+ var NOTICE_ID_MAX_LENGTH = 40;
7496
+ function capNoticeText(value) {
7497
+ return Array.from(value).slice(0, NOTICE_LABEL_MAX_LENGTH).join("");
7498
+ }
7499
+ function normalizeNotices(list) {
7500
+ const seen = /* @__PURE__ */ new Set();
7501
+ const perSection = /* @__PURE__ */ new Map();
7502
+ const out = [];
7503
+ list.forEach((raw, index) => {
7504
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
7505
+ const src = raw;
7506
+ const label = typeof src["label"] === "string" ? capNoticeText(src["label"]) : "";
7507
+ const labelTranslations = {};
7508
+ for (const [locale, text] of Object.entries(parseTranslations(src["labelTranslations"]))) {
7509
+ labelTranslations[locale] = capNoticeText(text);
7510
+ }
7511
+ if (label.trim().length === 0 && Object.keys(labelTranslations).length === 0) return;
7512
+ const section = pickEnum(src["section"], NOTICE_SECTIONS, "summary");
7513
+ const used = perSection.get(section) ?? 0;
7514
+ if (used >= NOTICES_PER_SECTION) return;
7515
+ perSection.set(section, used + 1);
7516
+ let id = typeof src["id"] === "string" ? src["id"].replace(/[^A-Za-z0-9_-]/g, "").slice(0, NOTICE_ID_MAX_LENGTH) : "";
7517
+ if (!id || seen.has(id)) id = `notice-${index + 1}`;
7518
+ while (seen.has(id)) id = `${id}x`;
7519
+ seen.add(id);
7520
+ out.push({
7521
+ id,
7522
+ section,
7523
+ icon: isNoticeIcon(src["icon"]) ? src["icon"] : NOTICE_ICON_FALLBACK,
7524
+ label,
7525
+ labelTranslations
7526
+ });
7527
+ });
7528
+ return out;
7529
+ }
7530
+ function decodeNotices(raw) {
7531
+ if (raw === void 0) return null;
7532
+ let parsed;
7533
+ try {
7534
+ parsed = JSON.parse(raw);
7535
+ } catch {
7536
+ return null;
7537
+ }
7538
+ return Array.isArray(parsed) ? normalizeNotices(parsed) : null;
7539
+ }
7540
+ function parseNoticesLoose(raw) {
7541
+ if (typeof raw === "string") return decodeNotices(raw);
7542
+ return Array.isArray(raw) ? normalizeNotices(raw) : null;
7543
+ }
7544
+ function encodeNotices(notices) {
7545
+ return JSON.stringify(
7546
+ notices.map((n) => {
7547
+ const translations = Object.entries(n.labelTranslations).filter(
7548
+ ([, text]) => text.trim().length > 0
7549
+ );
7550
+ return {
7551
+ id: n.id,
7552
+ section: n.section,
7553
+ icon: n.icon,
7554
+ label: n.label,
7555
+ ...translations.length > 0 ? { labelTranslations: Object.fromEntries(translations) } : {}
7556
+ };
7557
+ })
7558
+ );
7559
+ }
7560
+ function noticeText(notice, locale) {
7561
+ return translationFor(notice.labelTranslations, locale) ?? notice.label;
7562
+ }
7563
+ function noticesIn(notices, section) {
7564
+ return notices.filter((n) => n.section === section).slice(0, NOTICES_PER_SECTION);
7565
+ }
7566
+ function shownNotices(notices, section, locale) {
7567
+ return noticesIn(notices, section).map((notice) => ({ notice, text: noticeText(notice, locale).trim() })).filter((entry) => entry.text.length > 0);
7568
+ }
7249
7569
  var CUSTOM_FIELDS_MAX = 20;
7250
7570
  var CUSTOM_FIELD_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,39}$/;
7251
7571
  var ALL_CUSTOM_FIELD_TYPES = [
@@ -7589,7 +7909,10 @@ function customFieldOptionLabel(option, locale) {
7589
7909
  function decodeBranding(source) {
7590
7910
  if (!source) return cloneBranding(DEFAULT_BRANDING);
7591
7911
  const extras = source.sdk_ui_rules?.[BRANDING_GROUP_KEY] ?? {};
7592
- const decodedBadges = decodeBadges(extras["trustBadges"]);
7912
+ const trustRow = resolveNoticesAndBadges(
7913
+ decodeBadges(extras["trustBadges"]),
7914
+ decodeNotices(extras["notices"])
7915
+ );
7593
7916
  const decodedCustomFields = decodeCustomFields(extras["customFields"]);
7594
7917
  const displayName = s(source.merchant_name) || s(source.seller_name);
7595
7918
  const logoUrl = s(source.merchant_logo) || s(source.logo);
@@ -7768,7 +8091,8 @@ function decodeBranding(source) {
7768
8091
  totalLabel: s(extras["totalLabel"]) || DEFAULT_BRANDING.totalLabel,
7769
8092
  showCurrencyCode: parseBool(extras["showCurrencyCode"], DEFAULT_BRANDING.showCurrencyCode),
7770
8093
  showOrderItems: parseBool(extras["showOrderItems"], DEFAULT_BRANDING.showOrderItems),
7771
- trustBadges: decodedBadges ?? DEFAULT_BRANDING.trustBadges.map((b) => ({ ...b })),
8094
+ trustBadges: trustRow.trustBadges,
8095
+ notices: trustRow.notices,
7772
8096
  customFields: decodedCustomFields ?? [],
7773
8097
  methodSectionOrder: decodeMethodSectionOrder(extras[METHOD_SECTION_ORDER_KEY]),
7774
8098
  methodSectionLabels: decodeMethodSectionLabels(extras[METHOD_SECTION_LABELS_KEY]),
@@ -8052,9 +8376,14 @@ function encodeBranding(branding, base) {
8052
8376
  nomupayApplePayButtonType: branding.nomupayApplePayButtonType,
8053
8377
  nomupayGooglePayButtonColor: branding.nomupayGooglePayButtonColor,
8054
8378
  nomupayGooglePayButtonType: branding.nomupayGooglePayButtonType,
8055
- trustBadges: encodeBadges(branding.trustBadges),
8379
+ // Always written, `[]` included — its presence is what stops a later
8380
+ // decode from migrating the shop again (`resolveNoticesAndBadges`).
8381
+ notices: encodeNotices(branding.notices),
8056
8382
  [METHOD_SECTION_ORDER_KEY]: encodeMethodSectionOrder(branding.methodSectionOrder)
8057
8383
  };
8384
+ if (branding.trustBadges.length > 0) {
8385
+ extras["trustBadges"] = encodeBadges(branding.trustBadges);
8386
+ }
8058
8387
  const sectionLabels = encodeMethodSectionLabels(branding.methodSectionLabels);
8059
8388
  if (sectionLabels) extras[METHOD_SECTION_LABELS_KEY] = sectionLabels;
8060
8389
  if (branding.customFields.length > 0) {
@@ -8120,7 +8449,10 @@ function parseImportedBranding(raw) {
8120
8449
  const dflt = DEFAULT_BRANDING;
8121
8450
  const sStr = (v, fallback) => typeof v === "string" ? v : fallback;
8122
8451
  const sHex = (v, fallback) => typeof v === "string" && isHexColor(v) ? v : fallback;
8123
- const trustBadges = parseTrustBadgesLoose(root["trustBadges"]) ?? dflt.trustBadges.map((b) => ({ ...b }));
8452
+ const { trustBadges, notices } = resolveNoticesAndBadges(
8453
+ parseTrustBadgesLoose(root["trustBadges"]),
8454
+ parseNoticesLoose(root["notices"])
8455
+ );
8124
8456
  const customFields = parseCustomFieldsLoose(root["customFields"]) ?? [];
8125
8457
  const labelStyle = (() => {
8126
8458
  const v = root["labelStyle"];
@@ -8263,6 +8595,7 @@ function parseImportedBranding(raw) {
8263
8595
  showCurrencyCode: parseBool(root["showCurrencyCode"], dflt.showCurrencyCode),
8264
8596
  showOrderItems: parseBool(root["showOrderItems"], dflt.showOrderItems),
8265
8597
  trustBadges,
8598
+ notices,
8266
8599
  customFields,
8267
8600
  // An export writes the decoded maps; a hand-written file may carry the
8268
8601
  // JSON strings the bag uses. Both are accepted, and neither is required.
@@ -9459,6 +9792,7 @@ var encodeNativePanes = encodePanes;
9459
9792
  DEFAULT_BADGES_DARK,
9460
9793
  DEFAULT_BRANDING,
9461
9794
  DEFAULT_BRANDING_DARK,
9795
+ DEFAULT_NOTICES,
9462
9796
  Delopay,
9463
9797
  DelopayAuthenticationError,
9464
9798
  DelopayError,
@@ -9480,6 +9814,12 @@ var encodeNativePanes = encodePanes;
9480
9814
  NATIVE_PANE_CATEGORY_KEYS,
9481
9815
  NATIVE_PANE_ICON_KEYS,
9482
9816
  NOMUPAY_FORM_STYLES,
9817
+ NOTICES_PER_SECTION,
9818
+ NOTICE_ICONS,
9819
+ NOTICE_ICON_FALLBACK,
9820
+ NOTICE_ICON_PATHS,
9821
+ NOTICE_LABEL_MAX_LENGTH,
9822
+ NOTICE_SECTIONS,
9483
9823
  OperationLimits,
9484
9824
  PANES_MAX,
9485
9825
  PANE_CATEGORY_KEYS,
@@ -9537,6 +9877,7 @@ var encodeNativePanes = encodePanes;
9537
9877
  cloneBranding,
9538
9878
  cloneCustomField,
9539
9879
  cloneNativePane,
9880
+ cloneNotice,
9540
9881
  clonePane,
9541
9882
  compareSections,
9542
9883
  contrastRatio,
@@ -9552,11 +9893,13 @@ var encodeNativePanes = encodePanes;
9552
9893
  decodeMethodSectionLabels,
9553
9894
  decodeMethodSectionOrder,
9554
9895
  decodeNativePanes,
9896
+ decodeNotices,
9555
9897
  decodePanes,
9556
9898
  defaultBranding,
9557
9899
  defaultCustomFieldVisibility,
9558
9900
  defaultMethodSectionOrder,
9559
9901
  defaultNativePane,
9902
+ defaultNotices,
9560
9903
  defaultOperatorForSource,
9561
9904
  defaultPane,
9562
9905
  emptyPaneCatalog,
@@ -9566,6 +9909,7 @@ var encodeNativePanes = encodePanes;
9566
9909
  encodeMethodSectionLabels,
9567
9910
  encodeMethodSectionOrder,
9568
9911
  encodeNativePanes,
9912
+ encodeNotices,
9569
9913
  encodePanes,
9570
9914
  evaluateCustomFieldCondition,
9571
9915
  evaluateCustomFieldVisibility,
@@ -9576,13 +9920,17 @@ var encodeNativePanes = encodePanes;
9576
9920
  inputPadValue,
9577
9921
  isCheckboxChecked,
9578
9922
  isDarkSurface,
9923
+ isDefaultBadgeSet,
9579
9924
  isHexColor,
9925
+ isNoticeIcon,
9580
9926
  isSubscriptionDisputeWebhook,
9581
9927
  isSubscriptionLifecycleWebhook,
9582
9928
  knownSectionLabelKey,
9583
9929
  leaf,
9584
9930
  logoDimensions,
9585
9931
  nativePaneMethodInfo,
9932
+ noticeText,
9933
+ noticesIn,
9586
9934
  offerablePaneMethods,
9587
9935
  paneCatalogFor,
9588
9936
  paneDisplayDefaults,
@@ -9591,6 +9939,7 @@ var encodeNativePanes = encodePanes;
9591
9939
  paneViewVisibility,
9592
9940
  parseCustomFieldsLoose,
9593
9941
  parseImportedBranding,
9942
+ parseNoticesLoose,
9594
9943
  programToTree,
9595
9944
  radiusValue,
9596
9945
  readableInkOn,
@@ -9598,12 +9947,14 @@ var encodeNativePanes = encodePanes;
9598
9947
  resolveAirwallexLabelStyle,
9599
9948
  resolveApplePayButtonStyle,
9600
9949
  resolveGooglePayButtonColor,
9950
+ resolveNoticesAndBadges,
9601
9951
  resolveStripeTheme,
9602
9952
  ruleMatchToTree,
9603
9953
  sanitizeCustomCss,
9604
9954
  sectionLabel,
9605
9955
  sectionOrderOf,
9606
9956
  shadowFor,
9957
+ shownNotices,
9607
9958
  stripeSpacingValues,
9608
9959
  surchargeFigurePlan,
9609
9960
  surchargeShowsLabel,