@vesant-sdk/fraud 0.1.1-next.f2f4e87 → 0.1.2-dev.ab7ff29

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.js CHANGED
@@ -121,8 +121,321 @@ var TransactionStatus = {
121
121
  Failure: "$failure",
122
122
  Pending: "$pending"
123
123
  };
124
+ var SiftPaymentTypeValues = [
125
+ "$cash",
126
+ "$check",
127
+ "$credit_card",
128
+ "$crypto_currency",
129
+ "$debit_card",
130
+ "$digital_wallet",
131
+ "$electronic_fund_transfer",
132
+ "$financing",
133
+ "$gift_card",
134
+ "$invoice",
135
+ "$in_app_purchase",
136
+ "$money_order",
137
+ "$points",
138
+ "$prepaid_card",
139
+ "$store_credit",
140
+ "$third_party_processor",
141
+ "$voucher",
142
+ "$sepa_credit",
143
+ "$sepa_instant_credit",
144
+ "$sepa_direct_debit",
145
+ "$ach_credit",
146
+ "$ach_debit",
147
+ "$wire_credit",
148
+ "$wire_debit"
149
+ ];
150
+ var PaymentType = {
151
+ Cash: "$cash",
152
+ Check: "$check",
153
+ CreditCard: "$credit_card",
154
+ CryptoCurrency: "$crypto_currency",
155
+ DebitCard: "$debit_card",
156
+ DigitalWallet: "$digital_wallet",
157
+ ElectronicFundTransfer: "$electronic_fund_transfer",
158
+ Financing: "$financing",
159
+ GiftCard: "$gift_card",
160
+ Invoice: "$invoice",
161
+ InAppPurchase: "$in_app_purchase",
162
+ MoneyOrder: "$money_order",
163
+ Points: "$points",
164
+ PrepaidCard: "$prepaid_card",
165
+ StoreCredit: "$store_credit",
166
+ ThirdPartyProcessor: "$third_party_processor",
167
+ Voucher: "$voucher",
168
+ SepaCredit: "$sepa_credit",
169
+ SepaInstantCredit: "$sepa_instant_credit",
170
+ SepaDirectDebit: "$sepa_direct_debit",
171
+ AchCredit: "$ach_credit",
172
+ AchDebit: "$ach_debit",
173
+ WireCredit: "$wire_credit",
174
+ WireDebit: "$wire_debit"
175
+ };
176
+ var SiftWalletTypeValues = ["$crypto", "$digital", "$fiat"];
177
+ var WalletType = {
178
+ Crypto: "$crypto",
179
+ Digital: "$digital",
180
+ Fiat: "$fiat"
181
+ };
182
+
183
+ // src/types/rule-signals.ts
184
+ var LOGIN_RULE_SIGNAL_KEYS = ["ip_country_mismatch"];
185
+ var UPDATE_ACCOUNT_RULE_SIGNAL_KEYS = [
186
+ "two_fa_enabled",
187
+ "two_fa_disabled",
188
+ "two_fa_method_changed"
189
+ ];
190
+ var TRANSACTION_RULE_SIGNAL_KEYS = [
191
+ "payment_method",
192
+ "is_first_deposit",
193
+ "cumulative_deposit_amount",
194
+ "deposit_count",
195
+ "failed_deposit_count",
196
+ "is_first_withdrawal",
197
+ "withdrawal_method_differs",
198
+ "cumulative_withdrawal_amount",
199
+ "withdrawal_count",
200
+ "hours_since_last_deposit",
201
+ "days_since_last_activity",
202
+ "hours_since_account_creation"
203
+ ];
204
+
205
+ // src/normalization/payment-methods.ts
206
+ var PAYMENT_TYPE_ALIASES = {
207
+ bank_account: "$electronic_fund_transfer",
208
+ electronic_fund_transfer: "$electronic_fund_transfer",
209
+ credit_card: "$credit_card",
210
+ crypto_currency: "$crypto_currency",
211
+ debit_card: "$debit_card",
212
+ digital_wallet: "$digital_wallet",
213
+ cash: "$cash",
214
+ check: "$check",
215
+ financing: "$financing",
216
+ gift_card: "$gift_card",
217
+ invoice: "$invoice",
218
+ in_app_purchase: "$in_app_purchase",
219
+ money_order: "$money_order",
220
+ points: "$points",
221
+ prepaid_card: "$prepaid_card",
222
+ store_credit: "$store_credit",
223
+ third_party_processor: "$third_party_processor",
224
+ voucher: "$voucher",
225
+ sepa_credit: "$sepa_credit",
226
+ sepa_instant_credit: "$sepa_instant_credit",
227
+ sepa_direct_debit: "$sepa_direct_debit",
228
+ ach_credit: "$ach_credit",
229
+ ach_debit: "$ach_debit",
230
+ wire_credit: "$wire_credit",
231
+ wire_debit: "$wire_debit"
232
+ };
233
+ var SIFT_PAYMENT_METHOD_WHITELIST = /* @__PURE__ */ new Set([
234
+ "payment_type",
235
+ "payment_gateway",
236
+ "card_bin",
237
+ "card_last4",
238
+ "avs_result_code",
239
+ "cvv_result_code",
240
+ "verification_status",
241
+ "routing_number",
242
+ "account_holder_name",
243
+ "account_number_last5",
244
+ "bank_name",
245
+ "bank_country",
246
+ "wallet_address",
247
+ "wallet_type",
248
+ "paypal_payer_id",
249
+ "paypal_payer_email"
250
+ ]);
251
+ var DEPRECATED_PAYMENT_METHOD_KEYS = [
252
+ "account_number_last4",
253
+ "account_id",
254
+ "wallet_address_last4",
255
+ "crypto_currency",
256
+ "network"
257
+ ];
258
+ var METADATA_STRIP_KEYS = /* @__PURE__ */ new Set([
259
+ "account_id",
260
+ "wallet_address_last4",
261
+ "crypto_currency",
262
+ "network"
263
+ ]);
264
+ function isDevEnvironment() {
265
+ return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
266
+ }
267
+ function warnDeprecatedKey(key) {
268
+ if (!isDevEnvironment()) return;
269
+ const messages = {
270
+ account_number_last4: "[vesant-sdk/fraud] account_number_last4 is not a Sift field; use account_number_last5",
271
+ account_id: "[vesant-sdk/fraud] account_id is not a Sift field; moved to metadata",
272
+ wallet_address_last4: "[vesant-sdk/fraud] wallet_address_last4 is not a Sift field; moved to metadata",
273
+ crypto_currency: "[vesant-sdk/fraud] crypto_currency is not a Sift field on payment_method; moved to metadata",
274
+ network: "[vesant-sdk/fraud] network is not a Sift field on payment_method; moved to metadata"
275
+ };
276
+ const message = messages[key];
277
+ if (message) {
278
+ console.warn(message);
279
+ }
280
+ }
281
+ function normalizeSiftPaymentType(value) {
282
+ if (value === void 0 || value === null) return void 0;
283
+ const trimmed = String(value).trim();
284
+ if (trimmed === "") return void 0;
285
+ const normalized = trimmed.startsWith("$") ? trimmed.slice(1) : trimmed;
286
+ const alias = PAYMENT_TYPE_ALIASES[normalized.toLowerCase()];
287
+ if (alias) return alias;
288
+ const withPrefix = `$${normalized}`;
289
+ if (SiftPaymentTypeValues.includes(withPrefix)) {
290
+ return withPrefix;
291
+ }
292
+ return void 0;
293
+ }
294
+ function toSiftAccountNumberLast5(value) {
295
+ if (value === void 0 || value === null) return void 0;
296
+ const digits = String(value).replace(/\D/g, "");
297
+ if (digits.length === 0) return void 0;
298
+ if (digits.length >= 5) return digits.slice(-5);
299
+ return digits.padStart(5, "0");
300
+ }
301
+ function collectMetadataExtra(metadataExtras, key, value) {
302
+ if (value === void 0) return;
303
+ metadataExtras[key] = value;
304
+ }
305
+ function normalizeSiftPaymentMethod(raw) {
306
+ const metadataExtras = {};
307
+ const method = {};
308
+ const rawPaymentType = raw.payment_type !== void 0 ? String(raw.payment_type) : raw.$payment_type !== void 0 ? String(raw.$payment_type) : void 0;
309
+ const normalizedType = normalizeSiftPaymentType(rawPaymentType);
310
+ if (rawPaymentType !== void 0 && normalizedType === void 0) {
311
+ return null;
312
+ }
313
+ if (normalizedType !== void 0) {
314
+ method.payment_type = normalizedType;
315
+ }
316
+ for (const key of DEPRECATED_PAYMENT_METHOD_KEYS) {
317
+ if (key in raw) warnDeprecatedKey(key);
318
+ }
319
+ const accountLast4 = raw.account_number_last4 ?? raw.$account_number_last4;
320
+ const accountLast5 = raw.account_number_last5 ?? raw.$account_number_last5;
321
+ const resolvedLast5 = toSiftAccountNumberLast5(accountLast5 ?? accountLast4);
322
+ if (resolvedLast5 !== void 0) {
323
+ method.account_number_last5 = resolvedLast5;
324
+ }
325
+ for (const [key, value] of Object.entries(raw)) {
326
+ if (key === "payment_type" || key === "$payment_type") continue;
327
+ if (key === "account_number_last4" || key === "$account_number_last4") continue;
328
+ if (key === "account_number_last5" || key === "$account_number_last5") continue;
329
+ const plainKey = key.startsWith("$") ? key.slice(1) : key;
330
+ if (METADATA_STRIP_KEYS.has(plainKey)) {
331
+ if (plainKey === "account_id") {
332
+ collectMetadataExtra(metadataExtras, "linkmoney_account_id", String(value));
333
+ } else {
334
+ collectMetadataExtra(metadataExtras, plainKey, value);
335
+ }
336
+ continue;
337
+ }
338
+ if (!SIFT_PAYMENT_METHOD_WHITELIST.has(plainKey)) {
339
+ collectMetadataExtra(metadataExtras, `payment_method_${plainKey}`, value);
340
+ continue;
341
+ }
342
+ if (plainKey === "payment_type") {
343
+ continue;
344
+ }
345
+ method[plainKey] = value;
346
+ }
347
+ const hasMetadata = Object.keys(metadataExtras).length > 0;
348
+ return {
349
+ method,
350
+ ...hasMetadata ? { metadataExtras } : {}
351
+ };
352
+ }
353
+ function mergeAccountIds(target, accountId) {
354
+ const existing = target.linkmoney_account_ids;
355
+ if (Array.isArray(existing)) {
356
+ existing.push(accountId);
357
+ return;
358
+ }
359
+ if (typeof target.linkmoney_account_id === "string") {
360
+ target.linkmoney_account_ids = [target.linkmoney_account_id, accountId];
361
+ delete target.linkmoney_account_id;
362
+ return;
363
+ }
364
+ target.linkmoney_account_id = accountId;
365
+ }
366
+ function normalizeSiftPaymentMethods(methods) {
367
+ if (methods === void 0) return {};
368
+ const normalized = [];
369
+ const metadata = {};
370
+ for (const raw of methods) {
371
+ const result = normalizeSiftPaymentMethod(raw);
372
+ if (result === null) continue;
373
+ normalized.push(result.method);
374
+ if (result.metadataExtras) {
375
+ for (const [key, value] of Object.entries(result.metadataExtras)) {
376
+ if (key === "linkmoney_account_id" && typeof value === "string") {
377
+ mergeAccountIds(metadata, value);
378
+ } else if (key in metadata && Array.isArray(metadata[key]) && Array.isArray(value)) {
379
+ metadata[key] = [...metadata[key], ...value];
380
+ } else if (!(key in metadata)) {
381
+ metadata[key] = value;
382
+ }
383
+ }
384
+ }
385
+ }
386
+ const hasMetadata = Object.keys(metadata).length > 0;
387
+ return {
388
+ payment_methods: normalized,
389
+ ...hasMetadata ? { metadata } : {}
390
+ };
391
+ }
392
+
393
+ // src/normalization/sift-keys.ts
394
+ function siftKey(key) {
395
+ return key.startsWith("$") ? key : `$${key}`;
396
+ }
397
+ function toSiftKeyedObject(value) {
398
+ if (value === null || value === void 0) {
399
+ return value;
400
+ }
401
+ if (Array.isArray(value)) {
402
+ return value.map((item) => toSiftKeyedObject(item));
403
+ }
404
+ if (typeof value === "object") {
405
+ const out = {};
406
+ for (const [key, nested] of Object.entries(value)) {
407
+ out[siftKey(key)] = toSiftKeyedObject(nested);
408
+ }
409
+ return out;
410
+ }
411
+ return value;
412
+ }
124
413
 
125
414
  // src/normalization/normalize.ts
415
+ var BASE_SIFT_FIELD_KEYS = [
416
+ "session_id",
417
+ "user_email",
418
+ "name",
419
+ "phone",
420
+ "sift_payment_method",
421
+ "payment_methods",
422
+ "billing_address",
423
+ "shipping_address",
424
+ "app",
425
+ "browser",
426
+ "verification_phone_number",
427
+ "order_id",
428
+ "exchange_rate",
429
+ "expedited_shipping",
430
+ "shipping_method",
431
+ "shipping_carrier",
432
+ "shipping_tracking_numbers",
433
+ "brand_name",
434
+ "site_country",
435
+ "site_domain",
436
+ "ip",
437
+ "skip_payment_method_normalization"
438
+ ];
126
439
  function mergeMetadata(base, extras) {
127
440
  return { ...base, ...extras };
128
441
  }
@@ -145,17 +458,152 @@ function applySiftMetadata(body, metadata, siftMetadata, onlyWhenNonEmpty = fals
145
458
  }
146
459
  return body;
147
460
  }
461
+ function collectRuleSignals(request, keys) {
462
+ const signals = {};
463
+ for (const key of keys) {
464
+ const value = request[key];
465
+ if (value !== void 0) {
466
+ signals[key] = value;
467
+ }
468
+ }
469
+ return signals;
470
+ }
471
+ function buildBaseSiftMetadata(body) {
472
+ const {
473
+ event_type,
474
+ session_id,
475
+ user_email,
476
+ name,
477
+ phone,
478
+ sift_payment_method,
479
+ payment_methods,
480
+ billing_address,
481
+ shipping_address,
482
+ app,
483
+ browser,
484
+ verification_phone_number,
485
+ order_id,
486
+ amount,
487
+ currency_code,
488
+ exchange_rate,
489
+ expedited_shipping,
490
+ shipping_method,
491
+ shipping_carrier,
492
+ shipping_tracking_numbers,
493
+ brand_name,
494
+ site_country,
495
+ site_domain,
496
+ ip,
497
+ metadata,
498
+ skip_payment_method_normalization
499
+ } = body;
500
+ const siftMetadata = {
501
+ ...metadata ?? {}
502
+ };
503
+ if (session_id !== void 0) siftMetadata.$session_id = session_id;
504
+ if (user_email !== void 0) siftMetadata.$user_email = user_email;
505
+ if (name !== void 0) siftMetadata.$name = name;
506
+ if (phone !== void 0) siftMetadata.$phone = phone;
507
+ if (sift_payment_method !== void 0) {
508
+ if (skip_payment_method_normalization) {
509
+ siftMetadata.$payment_method = toSiftKeyedObject(sift_payment_method);
510
+ } else {
511
+ const result = normalizeSiftPaymentMethod(
512
+ sift_payment_method
513
+ );
514
+ if (result !== null) {
515
+ siftMetadata.$payment_method = toSiftKeyedObject(result.method);
516
+ if (result.metadataExtras) {
517
+ Object.assign(siftMetadata, result.metadataExtras);
518
+ }
519
+ }
520
+ }
521
+ }
522
+ if (payment_methods !== void 0) {
523
+ if (skip_payment_method_normalization) {
524
+ siftMetadata.$payment_methods = toSiftKeyedObject(payment_methods);
525
+ } else {
526
+ const { payment_methods: normalized, metadata: pmMetadata } = normalizeSiftPaymentMethods(
527
+ payment_methods
528
+ );
529
+ if (normalized !== void 0) {
530
+ siftMetadata.$payment_methods = toSiftKeyedObject(normalized);
531
+ }
532
+ if (pmMetadata) {
533
+ Object.assign(siftMetadata, pmMetadata);
534
+ }
535
+ }
536
+ }
537
+ if (billing_address !== void 0) {
538
+ siftMetadata.$billing_address = toSiftKeyedObject(billing_address);
539
+ }
540
+ if (shipping_address !== void 0) {
541
+ siftMetadata.$shipping_address = toSiftKeyedObject(shipping_address);
542
+ }
543
+ if (app !== void 0) siftMetadata.$app = toSiftKeyedObject(app);
544
+ if (browser !== void 0) {
545
+ siftMetadata.$browser = toSiftKeyedObject(browser);
546
+ }
547
+ if (event_type === "$create_order" || event_type === "$update_account") {
548
+ if (verification_phone_number !== void 0) {
549
+ siftMetadata.$verification_phone_number = verification_phone_number;
550
+ }
551
+ }
552
+ if (event_type === "$create_order") {
553
+ if (order_id !== void 0) siftMetadata.$order_id = order_id;
554
+ if (amount !== void 0) siftMetadata.$amount = amount;
555
+ if (currency_code !== void 0) siftMetadata.$currency_code = currency_code;
556
+ if (exchange_rate !== void 0) {
557
+ siftMetadata.$exchange_rate = toSiftKeyedObject(exchange_rate);
558
+ }
559
+ if (expedited_shipping !== void 0) siftMetadata.$expedited_shipping = expedited_shipping;
560
+ if (shipping_method !== void 0) siftMetadata.$shipping_method = shipping_method;
561
+ if (shipping_carrier !== void 0) siftMetadata.$shipping_carrier = shipping_carrier;
562
+ if (shipping_tracking_numbers !== void 0) {
563
+ siftMetadata.$shipping_tracking_numbers = shipping_tracking_numbers;
564
+ }
565
+ if (brand_name !== void 0) siftMetadata.$brand_name = brand_name;
566
+ if (site_country !== void 0) siftMetadata.$site_country = site_country;
567
+ if (site_domain !== void 0) siftMetadata.$site_domain = site_domain;
568
+ if (ip !== void 0) siftMetadata.$ip = ip;
569
+ }
570
+ return siftMetadata;
571
+ }
572
+ function normalizeBaseSiftFields(body) {
573
+ const siftMetadata = buildBaseSiftMetadata(body);
574
+ const stripped = stripSdkOnlyFields(body, [...BASE_SIFT_FIELD_KEYS]);
575
+ if (Object.keys(siftMetadata).length > 0) {
576
+ stripped.metadata = siftMetadata;
577
+ }
578
+ return stripped;
579
+ }
580
+ function applyRuleSignals(body, request, keys, stripKeys) {
581
+ const ruleSignals = collectRuleSignals(request, keys);
582
+ const stripped = stripSdkOnlyFields(body, [...stripKeys]);
583
+ if (Object.keys(ruleSignals).length > 0) {
584
+ stripped.metadata = mergeMetadata(
585
+ stripped.metadata,
586
+ ruleSignals
587
+ );
588
+ }
589
+ return stripped;
590
+ }
148
591
  function normalizeUpdatePassword(request) {
149
592
  const { password_reason, password_status, metadata, ...rest } = request;
150
- const body = createStrippedBody(rest, metadata, []);
151
- const siftMetadata = {};
152
- if (password_reason) siftMetadata.$reason = password_reason;
153
- if (password_status) siftMetadata.$status = password_status;
154
- return applySiftMetadata(body, metadata, siftMetadata);
593
+ let body = createStrippedBody(rest, metadata, [
594
+ "password_reason",
595
+ "password_status"
596
+ ]);
597
+ const siftMetadata = {
598
+ $reason: password_reason ?? PasswordUpdateReason.UserUpdate,
599
+ $status: password_status ?? PasswordUpdateStatus.Success
600
+ };
601
+ body = applySiftMetadata(body, metadata, siftMetadata);
602
+ return normalizeBaseSiftFields(body);
155
603
  }
156
604
  function normalizeWager(request) {
157
605
  const { wager_type, wager_status, wager_event_type, wager_event_name, wager_event_id, metadata, ...rest } = request;
158
- const body = createStrippedBody(rest, metadata, [
606
+ let body = createStrippedBody(rest, metadata, [
159
607
  "wager_type",
160
608
  "wager_status",
161
609
  "wager_event_type",
@@ -166,17 +614,19 @@ function normalizeWager(request) {
166
614
  $wager_type: wager_type,
167
615
  $wager_status: wager_status
168
616
  };
169
- if (wager_event_type) siftMetadata.wager_event_type = wager_event_type;
170
- if (wager_event_name) siftMetadata.wager_event_name = wager_event_name;
171
- if (wager_event_id) siftMetadata.wager_event_id = wager_event_id;
172
- return applySiftMetadata(body, metadata, siftMetadata);
617
+ if (wager_event_type) siftMetadata.$wager_event_type = wager_event_type;
618
+ if (wager_event_name) siftMetadata.$wager_event_name = wager_event_name;
619
+ if (wager_event_id) siftMetadata.$wager_event_id = wager_event_id;
620
+ body = applySiftMetadata(body, metadata, siftMetadata);
621
+ return normalizeBaseSiftFields(body);
173
622
  }
174
623
  function normalizeLogin(request) {
175
624
  const { login_status, login_failure_reason, metadata, ...rest } = request;
176
625
  const effectiveStatus = login_status ?? LoginStatus.Success;
177
- const body = createStrippedBody(rest, metadata, [
626
+ let body = createStrippedBody(rest, metadata, [
178
627
  "login_status",
179
- "login_failure_reason"
628
+ "login_failure_reason",
629
+ ...LOGIN_RULE_SIGNAL_KEYS
180
630
  ]);
181
631
  const siftMetadata = {
182
632
  $login_status: effectiveStatus
@@ -184,14 +634,22 @@ function normalizeLogin(request) {
184
634
  if (effectiveStatus === LoginStatus.Failure && login_failure_reason) {
185
635
  siftMetadata.$failure_reason = login_failure_reason;
186
636
  }
187
- return applySiftMetadata(body, metadata, siftMetadata);
637
+ body = applySiftMetadata(body, metadata, siftMetadata);
638
+ body = applyRuleSignals(
639
+ body,
640
+ request,
641
+ LOGIN_RULE_SIGNAL_KEYS,
642
+ []
643
+ );
644
+ return normalizeBaseSiftFields(body);
188
645
  }
189
646
  function normalizeTransaction(request) {
190
647
  const { transaction_status, decline_category, transfer_recipient_user_id, metadata, ...rest } = request;
191
- const body = createStrippedBody(rest, metadata, [
648
+ let body = createStrippedBody(rest, metadata, [
192
649
  "transaction_status",
193
650
  "decline_category",
194
- "transfer_recipient_user_id"
651
+ "transfer_recipient_user_id",
652
+ ...TRANSACTION_RULE_SIGNAL_KEYS
195
653
  ]);
196
654
  const siftMetadata = {};
197
655
  if (transaction_status) siftMetadata.$transaction_status = transaction_status;
@@ -199,7 +657,43 @@ function normalizeTransaction(request) {
199
657
  if (transfer_recipient_user_id) {
200
658
  siftMetadata.$transfer_recipient_user_id = transfer_recipient_user_id;
201
659
  }
202
- return applySiftMetadata(body, metadata, siftMetadata, true);
660
+ body = applySiftMetadata(body, metadata, siftMetadata, true);
661
+ body = applyRuleSignals(
662
+ body,
663
+ request,
664
+ TRANSACTION_RULE_SIGNAL_KEYS,
665
+ []
666
+ );
667
+ return normalizeBaseSiftFields(body);
668
+ }
669
+ function normalizeUpdateAccount(request) {
670
+ const { metadata, ...rest } = request;
671
+ let body = createStrippedBody(rest, metadata, [
672
+ ...UPDATE_ACCOUNT_RULE_SIGNAL_KEYS
673
+ ]);
674
+ body = applyRuleSignals(
675
+ body,
676
+ request,
677
+ UPDATE_ACCOUNT_RULE_SIGNAL_KEYS,
678
+ []
679
+ );
680
+ return normalizeBaseSiftFields(body);
681
+ }
682
+ function normalizeCreateOrder(request) {
683
+ const { amount, currency_code, metadata, ...rest } = request;
684
+ let body = createStrippedBody(rest, metadata, [
685
+ "amount",
686
+ "currency_code"
687
+ ]);
688
+ const siftMetadata = {
689
+ $amount: amount,
690
+ $currency_code: currency_code
691
+ };
692
+ body = applySiftMetadata(body, metadata, siftMetadata);
693
+ return normalizeBaseSiftFields(body);
694
+ }
695
+ function normalizeDefault(request) {
696
+ return normalizeBaseSiftFields({ ...request });
203
697
  }
204
698
  function normalizeScoreRequestForApi(request) {
205
699
  switch (request.event_type) {
@@ -211,8 +705,12 @@ function normalizeScoreRequestForApi(request) {
211
705
  return normalizeLogin(request);
212
706
  case "$transaction":
213
707
  return normalizeTransaction(request);
708
+ case "$update_account":
709
+ return normalizeUpdateAccount(request);
710
+ case "$create_order":
711
+ return normalizeCreateOrder(request);
214
712
  default:
215
- return { ...request };
713
+ return normalizeDefault(request);
216
714
  }
217
715
  }
218
716
 
@@ -224,6 +722,7 @@ function buildScoreRequestPreview(request) {
224
722
  // src/types/events.ts
225
723
  var SCORED_FRAUD_EVENT_TYPES = [
226
724
  "$create_account",
725
+ "$create_order",
227
726
  "$login",
228
727
  "$logout",
229
728
  "$update_account",
@@ -240,7 +739,6 @@ var PLANNED_FRAUD_EVENT_TYPES = [
240
739
  "$verification",
241
740
  "$link_session_to_user",
242
741
  "$security_notification",
243
- "$create_order",
244
742
  "$update_order",
245
743
  "$order_status",
246
744
  "$add_promotion"
@@ -253,10 +751,33 @@ function isSupportedFraudEventType(eventType) {
253
751
  }
254
752
 
255
753
  // src/types/metadata.ts
256
- function hasReservedMetadataKey(metadata) {
257
- for (const key of Object.keys(metadata)) {
258
- if (key.startsWith("$")) {
259
- return key;
754
+ var TYPED_SIFT_METADATA_KEYS = {
755
+ session_id: "$session_id",
756
+ user_email: "$user_email",
757
+ name: "$name",
758
+ phone: "$phone",
759
+ sift_payment_method: "$payment_method",
760
+ billing_address: "$billing_address",
761
+ payment_methods: "$payment_methods",
762
+ app: "$app",
763
+ verification_phone_number: "$verification_phone_number",
764
+ order_id: "$order_id",
765
+ exchange_rate: "$exchange_rate",
766
+ shipping_address: "$shipping_address",
767
+ expedited_shipping: "$expedited_shipping",
768
+ shipping_method: "$shipping_method",
769
+ shipping_carrier: "$shipping_carrier",
770
+ shipping_tracking_numbers: "$shipping_tracking_numbers",
771
+ browser: "$browser",
772
+ brand_name: "$brand_name",
773
+ site_country: "$site_country",
774
+ site_domain: "$site_domain",
775
+ ip: "$ip"
776
+ };
777
+ function findMetadataConflictWithTypedFields(metadata, request) {
778
+ for (const [field, metaKey] of Object.entries(TYPED_SIFT_METADATA_KEYS)) {
779
+ if (request[field] !== void 0 && metadata[metaKey] !== void 0) {
780
+ return metaKey;
260
781
  }
261
782
  }
262
783
  return void 0;
@@ -282,22 +803,63 @@ function assertPositiveAmount(amount, field, errors, prefix) {
282
803
  errors.push(`${prefix}${field} must be greater than 0`);
283
804
  }
284
805
  }
285
- function validateMetadata(metadata, errors, prefix) {
806
+ function validateMetadata(metadata, request, errors, prefix) {
286
807
  if (!metadata) return;
287
- const reservedKey = hasReservedMetadataKey(metadata);
288
- if (reservedKey) {
808
+ const conflictKey = findMetadataConflictWithTypedFields(
809
+ metadata,
810
+ request
811
+ );
812
+ if (conflictKey) {
289
813
  errors.push(
290
- `${prefix}metadata keys must not start with '$' (found '${reservedKey}'). Use typed request fields instead.`
814
+ `${prefix}metadata.${conflictKey} conflicts with a typed request field that maps to the same key; omit metadata.${conflictKey} or remove the typed field`
291
815
  );
292
816
  }
293
817
  }
294
818
 
295
819
  // src/validation/rules.ts
820
+ var E164_PHONE_PATTERN = /^\+[1-9]\d{7,14}$/;
821
+ function validatePaymentMethods(request, errors, prefix) {
822
+ const { payment_methods, skip_payment_method_normalization: skipNormalization } = request;
823
+ if (payment_methods === void 0) return;
824
+ if (payment_methods.length === 0) {
825
+ errors.push(`${prefix}payment_methods must include at least one item when provided`);
826
+ return;
827
+ }
828
+ payment_methods.forEach((method, index) => {
829
+ const raw = method;
830
+ if (skipNormalization) {
831
+ for (const key of Object.keys(raw)) {
832
+ const plainKey = key.startsWith("$") ? key.slice(1) : key;
833
+ if (!SIFT_PAYMENT_METHOD_WHITELIST.has(plainKey)) {
834
+ errors.push(
835
+ `${prefix}payment_methods[${index}].${plainKey} is not a valid Sift payment method field`
836
+ );
837
+ }
838
+ }
839
+ const rawType2 = raw.payment_type !== void 0 ? String(raw.payment_type) : void 0;
840
+ if (rawType2 !== void 0 && normalizeSiftPaymentType(rawType2) === void 0) {
841
+ errors.push(
842
+ `${prefix}payment_methods[${index}].payment_type is not a valid Sift payment type`
843
+ );
844
+ }
845
+ return;
846
+ }
847
+ const rawType = raw.payment_type !== void 0 ? String(raw.payment_type) : raw.$payment_type !== void 0 ? String(raw.$payment_type) : void 0;
848
+ if (rawType !== void 0) {
849
+ const result = normalizeSiftPaymentMethod(raw);
850
+ if (result === null) {
851
+ errors.push(
852
+ `${prefix}payment_methods[${index}].payment_type is not a valid Sift payment type`
853
+ );
854
+ }
855
+ }
856
+ });
857
+ }
296
858
  function validateBase(request, errors, prefix) {
297
859
  assertNonEmpty(request.customer_id, "customer_id", errors, prefix);
298
860
  assertNonEmpty(request.sift_user_id, "sift_user_id", errors, prefix);
299
861
  assertNonEmpty(request.event_type, "event_type", errors, prefix);
300
- validateMetadata(request.metadata, errors, prefix);
862
+ validateMetadata(request.metadata, request, errors, prefix);
301
863
  }
302
864
  function validateLogin(request, errors, prefix) {
303
865
  assertEnum(request.login_status, SiftLoginStatusValues, "login_status", errors, prefix);
@@ -323,9 +885,6 @@ function validateUpdatePassword(request, errors, prefix) {
323
885
  errors,
324
886
  prefix
325
887
  );
326
- if (!request.password_reason) {
327
- errors.push(`${prefix}password_reason is required`);
328
- }
329
888
  assertEnum(
330
889
  request.password_status,
331
890
  SiftPasswordUpdateStatusValues,
@@ -333,9 +892,6 @@ function validateUpdatePassword(request, errors, prefix) {
333
892
  errors,
334
893
  prefix
335
894
  );
336
- if (!request.password_status) {
337
- errors.push(`${prefix}password_status is required`);
338
- }
339
895
  }
340
896
  function validateTransaction(request, errors, prefix) {
341
897
  assertPositiveAmount(request.amount, "amount", errors, prefix);
@@ -369,6 +925,28 @@ function validateWager(request, errors, prefix) {
369
925
  assertPositiveAmount(request.amount, "amount", errors, prefix);
370
926
  assertNonEmpty(request.currency, "currency", errors, prefix);
371
927
  }
928
+ function validateCreateAccount(request, errors, prefix) {
929
+ validatePaymentMethods(request, errors, prefix);
930
+ }
931
+ function validateUpdateAccount(request, errors, prefix) {
932
+ validatePaymentMethods(request, errors, prefix);
933
+ if (request.verification_phone_number !== void 0) {
934
+ if (!E164_PHONE_PATTERN.test(request.verification_phone_number.trim())) {
935
+ errors.push(`${prefix}verification_phone_number must be E.164 (e.g. +14155550123)`);
936
+ }
937
+ }
938
+ }
939
+ function validateCreateOrder(request, errors, prefix) {
940
+ assertPositiveAmount(request.amount, "amount", errors, prefix);
941
+ assertNonEmpty(request.currency_code, "currency_code", errors, prefix);
942
+ validatePaymentMethods(request, errors, prefix);
943
+ if (request.shipping_tracking_numbers !== void 0) {
944
+ const hasEmpty = request.shipping_tracking_numbers.some((n) => n.trim() === "");
945
+ if (hasEmpty) {
946
+ errors.push(`${prefix}shipping_tracking_numbers cannot contain empty values`);
947
+ }
948
+ }
949
+ }
372
950
  function validateChargeback(request, errors, prefix) {
373
951
  const hasTransactionId = !!request.transaction_id?.trim();
374
952
  const hasOrderId = !!request.order_id?.trim();
@@ -403,9 +981,20 @@ function validateByEventType(request, errors, prefix) {
403
981
  case "$wager":
404
982
  validateWager(request, errors, prefix);
405
983
  break;
984
+ case "$create_order":
985
+ validateCreateOrder(request, errors, prefix);
986
+ break;
406
987
  case "$chargeback":
407
988
  validateChargeback(request, errors, prefix);
408
989
  break;
990
+ case "$create_account":
991
+ validateCreateAccount(request, errors, prefix);
992
+ break;
993
+ case "$logout":
994
+ break;
995
+ case "$update_account":
996
+ validateUpdateAccount(request, errors, prefix);
997
+ break;
409
998
  }
410
999
  }
411
1000
  function validateScoreRequest(request, index) {
@@ -472,10 +1061,12 @@ exports.CHARGEBACK_FRAUD_EVENT_TYPE = CHARGEBACK_FRAUD_EVENT_TYPE;
472
1061
  exports.ChargebackReason = ChargebackReason;
473
1062
  exports.ChargebackState = ChargebackState;
474
1063
  exports.FraudClient = FraudClient;
1064
+ exports.LOGIN_RULE_SIGNAL_KEYS = LOGIN_RULE_SIGNAL_KEYS;
475
1065
  exports.LoginStatus = LoginStatus;
476
1066
  exports.PLANNED_FRAUD_EVENT_TYPES = PLANNED_FRAUD_EVENT_TYPES;
477
1067
  exports.PasswordUpdateReason = PasswordUpdateReason;
478
1068
  exports.PasswordUpdateStatus = PasswordUpdateStatus;
1069
+ exports.PaymentType = PaymentType;
479
1070
  exports.SCORED_FRAUD_EVENT_TYPES = SCORED_FRAUD_EVENT_TYPES;
480
1071
  exports.SUPPORTED_FRAUD_EVENT_TYPES = SUPPORTED_FRAUD_EVENT_TYPES;
481
1072
  exports.SiftChargebackReasonValues = SiftChargebackReasonValues;
@@ -484,17 +1075,27 @@ exports.SiftLoginFailureReasonValues = SiftLoginFailureReasonValues;
484
1075
  exports.SiftLoginStatusValues = SiftLoginStatusValues;
485
1076
  exports.SiftPasswordUpdateReasonValues = SiftPasswordUpdateReasonValues;
486
1077
  exports.SiftPasswordUpdateStatusValues = SiftPasswordUpdateStatusValues;
1078
+ exports.SiftPaymentTypeValues = SiftPaymentTypeValues;
487
1079
  exports.SiftTransactionStatusValues = SiftTransactionStatusValues;
488
1080
  exports.SiftTransactionTypeValues = SiftTransactionTypeValues;
489
1081
  exports.SiftWagerStatusValues = SiftWagerStatusValues;
1082
+ exports.SiftWalletTypeValues = SiftWalletTypeValues;
1083
+ exports.TRANSACTION_RULE_SIGNAL_KEYS = TRANSACTION_RULE_SIGNAL_KEYS;
1084
+ exports.TYPED_SIFT_METADATA_KEYS = TYPED_SIFT_METADATA_KEYS;
490
1085
  exports.TransactionStatus = TransactionStatus;
491
1086
  exports.TransactionType = TransactionType;
1087
+ exports.UPDATE_ACCOUNT_RULE_SIGNAL_KEYS = UPDATE_ACCOUNT_RULE_SIGNAL_KEYS;
492
1088
  exports.WagerStatus = WagerStatus;
1089
+ exports.WalletType = WalletType;
493
1090
  exports.buildScoreRequestPreview = buildScoreRequestPreview;
494
- exports.hasReservedMetadataKey = hasReservedMetadataKey;
1091
+ exports.findMetadataConflictWithTypedFields = findMetadataConflictWithTypedFields;
495
1092
  exports.isPlannedFraudEventType = isPlannedFraudEventType;
496
1093
  exports.isSupportedFraudEventType = isSupportedFraudEventType;
497
1094
  exports.normalizeScoreRequestForApi = normalizeScoreRequestForApi;
1095
+ exports.normalizeSiftPaymentMethod = normalizeSiftPaymentMethod;
1096
+ exports.normalizeSiftPaymentMethods = normalizeSiftPaymentMethods;
1097
+ exports.normalizeSiftPaymentType = normalizeSiftPaymentType;
1098
+ exports.toSiftAccountNumberLast5 = toSiftAccountNumberLast5;
498
1099
  exports.validateScoreRequest = validateScoreRequest;
499
1100
  //# sourceMappingURL=index.js.map
500
1101
  //# sourceMappingURL=index.js.map