@vesant-sdk/fraud 0.1.1-next.f221d37 → 0.1.2-dev.254dc8c

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