@magield/node 0.6.1 → 0.8.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.
package/dist/client.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Decision } from '@magield/proto';
2
2
  import { type BreakerOptions } from './breaker.js';
3
3
  import type { DecisionName, EvaluateInput, FeedbackInput, LimitCheckInput, LimitVerdict, TrustDeviceInput, TrustedDevice, Verdict } from './types.js';
4
+ import { type BonusRequest, type MoneyVerdict, type PaymentRequest } from './igaming.js';
4
5
  /**
5
6
  * The backend client.
6
7
  *
@@ -75,6 +76,19 @@ export declare class ShieldClient {
75
76
  * same on the good path and the bad one.
76
77
  */
77
78
  evaluate(input: EvaluateInput): Promise<Verdict>;
79
+ /**
80
+ * A deposit, and what to do with it: take it, ask for 3-D Secure, or
81
+ * decline. A deposit under review is taken; the case is decided before any
82
+ * of it can leave. Never throws, like `evaluate`.
83
+ */
84
+ deposit(request: PaymentRequest): Promise<MoneyVerdict>;
85
+ /**
86
+ * A withdrawal, and what to do with it. `handling === 'hold'` means keep it
87
+ * pending until the case webhook approves or rejects it.
88
+ */
89
+ withdraw(request: PaymentRequest): Promise<MoneyVerdict>;
90
+ /** A bonus, promo or referral claim, and whether to credit it now, later or never. */
91
+ bonusClaim(request: BonusRequest): Promise<MoneyVerdict>;
78
92
  /**
79
93
  * Reports what happened after a decision.
80
94
  *
package/dist/client.js CHANGED
@@ -3,6 +3,7 @@ import { credentials, Metadata, status } from '@grpc/grpc-js';
3
3
  import { CheckMode, CheckRequest, Decision, DevicesClient, EvaluateRequest, EvaluationClient, FeedbackRequest, LimitsClient, TrustRequest, TrustState, } from '@magield/proto';
4
4
  import { Breaker } from './breaker.js';
5
5
  import { decisionName, modeName, outcomeValue } from './decisions.js';
6
+ import { evaluateMoney } from './igaming.js';
6
7
  /**
7
8
  * The backend client.
8
9
  *
@@ -71,6 +72,25 @@ export class ShieldClient {
71
72
  return this.#failOpen(err);
72
73
  }
73
74
  }
75
+ /**
76
+ * A deposit, and what to do with it: take it, ask for 3-D Secure, or
77
+ * decline. A deposit under review is taken; the case is decided before any
78
+ * of it can leave. Never throws, like `evaluate`.
79
+ */
80
+ deposit(request) {
81
+ return evaluateMoney(this, 'deposit', request, this.#config.onError);
82
+ }
83
+ /**
84
+ * A withdrawal, and what to do with it. `handling === 'hold'` means keep it
85
+ * pending until the case webhook approves or rejects it.
86
+ */
87
+ withdraw(request) {
88
+ return evaluateMoney(this, 'withdraw', request, this.#config.onError);
89
+ }
90
+ /** A bonus, promo or referral claim, and whether to credit it now, later or never. */
91
+ bonusClaim(request) {
92
+ return evaluateMoney(this, 'bonus_claim', request, this.#config.onError);
93
+ }
74
94
  /**
75
95
  * Reports what happened after a decision.
76
96
  *
@@ -209,6 +229,7 @@ export class ShieldClient {
209
229
  token: Buffer.from(input.token ?? '', 'utf8'),
210
230
  context: { ...(input.context ?? {}) },
211
231
  ...(input.keys === undefined ? {} : { keys: identifiers(input.keys) }),
232
+ ...(input.attributes === undefined ? {} : { attributes: transactionAttributes(input.attributes) }),
212
233
  ...(input.network === undefined && input.ip === undefined
213
234
  ? {}
214
235
  : { network: networkContext({ ip: input.ip, ...input.network }) }),
@@ -363,9 +384,35 @@ function identifiers(keys) {
363
384
  sessionId: keys.sessionId ?? '',
364
385
  apiKeyId: keys.apiKeyId ?? '',
365
386
  passwordSha1Prefix: keys.passwordSha1Prefix ?? '',
387
+ paymentMethod: keys.paymentMethod ?? '',
388
+ paymentBin: keys.paymentBin ?? '',
389
+ promoCode: keys.promoCode ?? '',
390
+ affiliateId: keys.affiliateId ?? '',
391
+ documentHash: keys.documentHash ?? '',
392
+ addressHash: keys.addressHash ?? '',
393
+ referrerId: keys.referrerId ?? '',
366
394
  custom: { ...(keys.custom ?? {}) },
367
395
  };
368
396
  }
397
+ /** Unset numbers and flags stay unset on the wire, so the gateway can tell "not sent" from zero. */
398
+ function transactionAttributes(attributes) {
399
+ return {
400
+ ...(attributes.amount === undefined ? {} : { amount: attributes.amount }),
401
+ currency: attributes.currency ?? '',
402
+ paymentType: attributes.paymentType ?? '',
403
+ paymentCountry: attributes.paymentCountry ?? '',
404
+ bonusType: attributes.bonusType ?? '',
405
+ gameCategory: attributes.gameCategory ?? '',
406
+ ...(attributes.stake === undefined ? {} : { stake: attributes.stake }),
407
+ ...(attributes.odds === undefined ? {} : { odds: attributes.odds }),
408
+ kycStatus: attributes.kycStatus ?? '',
409
+ ...(attributes.selfExcluded === undefined ? {} : { selfExcluded: attributes.selfExcluded }),
410
+ ...(attributes.depositLimitReached === undefined ? {} : { depositLimitReached: attributes.depositLimitReached }),
411
+ changed: attributes.changed ?? '',
412
+ ...(attributes.age === undefined ? {} : { age: Math.floor(attributes.age) }),
413
+ custom: { ...(attributes.custom ?? {}) },
414
+ };
415
+ }
369
416
  function networkContext(network) {
370
417
  return {
371
418
  ip: network.ip ?? '',
package/dist/decisions.js CHANGED
@@ -18,6 +18,7 @@ import { Decision, Mode, Outcome } from '@magield/proto';
18
18
  const DECISIONS = new Map([
19
19
  [Decision.DECISION_ALLOW, 'allow'],
20
20
  [Decision.DECISION_ALLOW_FLAGGED, 'allow_flagged'],
21
+ [Decision.DECISION_REVIEW, 'review'],
21
22
  [Decision.DECISION_CHALLENGE, 'challenge'],
22
23
  [Decision.DECISION_STEP_UP, 'step_up'],
23
24
  [Decision.DECISION_THROTTLE, 'throttle'],
@@ -0,0 +1,138 @@
1
+ import type { DecisionName, EvaluateInput, Identifiers, NetworkContext, TransactionAttributes, Verdict } from './types.js';
2
+ /**
3
+ * The money actions, typed: deposit, withdraw and bonus claim, each turned into
4
+ * what to do next.
5
+ *
6
+ * A verdict says what the engine decided; an operator's cashier needs to know
7
+ * whether to take the money, hold it, ask for more, or refuse. That mapping is
8
+ * different per action — `review` on a withdrawal means "keep it pending until
9
+ * an analyst decides", on a deposit it means "take it, and look before any of
10
+ * it leaves" — and getting it wrong is how a held withdrawal gets paid out
11
+ * anyway. So the mapping lives here, once, and the helpers return it beside
12
+ * the verdict.
13
+ */
14
+ /**
15
+ * What to do with the transaction.
16
+ *
17
+ * - `proceed`: go ahead.
18
+ * - `hold`: keep it pending. Release it on a `case.approved` webhook, cancel
19
+ * it on `case.rejected` (see `verifyCaseWebhook`).
20
+ * - `challenge`: show `verdict.challenge` and retry with the solution.
21
+ * - `step_up`: ask for more proof — 3-D Secure on a deposit, a second factor
22
+ * on a withdrawal or a bonus.
23
+ * - `retry_later`: after `verdict.retryAfterMs`.
24
+ * - `refuse`: decline it. Say no more than your terms require.
25
+ */
26
+ export type Handling = 'proceed' | 'hold' | 'challenge' | 'step_up' | 'retry_later' | 'refuse';
27
+ export type MoneyAction = 'deposit' | 'withdraw' | 'bonus_claim';
28
+ /** The recommended handling of a decision on a money action. */
29
+ export declare function handlingFor(action: MoneyAction, decision: DecisionName): Handling;
30
+ export interface MoneyVerdict {
31
+ readonly verdict: Verdict;
32
+ readonly handling: Handling;
33
+ /** One sentence, for logs and for whoever reads them at 3am. */
34
+ readonly advice: string;
35
+ }
36
+ interface MoneyRequestBase {
37
+ /** The account: whatever you send as `username` everywhere else. */
38
+ readonly account: string;
39
+ /**
40
+ * Your own id for this transaction. Sent as `requestId`, so a chargeback or
41
+ * an analyst's decision weeks later is reported against the id you already
42
+ * keep — no evaluation id needs storing — and the case webhook names it.
43
+ */
44
+ readonly transactionId: string;
45
+ /** The web SDK's token from the cashier page. */
46
+ readonly token?: string;
47
+ readonly ip?: string;
48
+ readonly network?: NetworkContext;
49
+ readonly sessionId?: string;
50
+ /** Any other identifiers: email, phone, user id, document or address hash. */
51
+ readonly keys?: Identifiers;
52
+ /** Anything else about the transaction: KYC status, deposit limit reached, age… */
53
+ readonly attributes?: TransactionAttributes;
54
+ readonly context?: Readonly<Record<string, string>>;
55
+ }
56
+ export interface PaymentRequest extends MoneyRequestBase {
57
+ /** In your base currency. */
58
+ readonly amount: number;
59
+ readonly currency?: string;
60
+ /** The PSP's card fingerprint or token, or the wallet id — never the card number. */
61
+ readonly paymentMethod?: string;
62
+ readonly paymentBin?: string;
63
+ readonly paymentType?: TransactionAttributes['paymentType'];
64
+ readonly paymentCountry?: string;
65
+ }
66
+ export interface BonusRequest extends MoneyRequestBase {
67
+ readonly bonusType: 'welcome' | 'reload' | 'free_spins' | 'cashback' | 'referral' | (string & {});
68
+ /** What the bonus is worth, in your base currency, for the report's "bonus cost prevented". */
69
+ readonly value?: number;
70
+ readonly promoCode?: string;
71
+ /** The referring account, for referral rewards. */
72
+ readonly referrerId?: string;
73
+ readonly affiliateId?: string;
74
+ }
75
+ /** What the helpers need of the client: `evaluate`, and somewhere to say what was dropped. */
76
+ export interface Evaluator {
77
+ evaluate(input: EvaluateInput): Promise<Verdict>;
78
+ }
79
+ export declare function evaluateMoney(shield: Evaluator, action: MoneyAction, request: PaymentRequest | BonusRequest, onError?: (error: Error) => void): Promise<MoneyVerdict>;
80
+ /** The Evaluate call a money helper makes. Exported for tests and for integrators who build their own. */
81
+ export declare function moneyInput(action: MoneyAction, request: PaymentRequest | BonusRequest, onError?: (error: Error) => void): EvaluateInput;
82
+ /** Thirteen to nineteen digits passing the Luhn check: a PAN, which a PSP token is not. */
83
+ export declare function looksLikeCardNumber(value: string): boolean;
84
+ /**
85
+ * The `documentHash` key: an identity document, hashed on your side so its
86
+ * number never leaves your systems.
87
+ *
88
+ * Normalised first — country upper-cased, spaces, dashes and dots dropped from
89
+ * the number — because "AB 12-34" and "ab1234" are one passport, and two
90
+ * hashes for it would hide the very reuse the KYC rules look for. Pass your
91
+ * own `pepper` (a secret kept beside your database) so the hash cannot be
92
+ * reversed by trying every passport number; use the same one everywhere.
93
+ */
94
+ export declare function documentHash(document: {
95
+ country: string;
96
+ type: string;
97
+ number: string;
98
+ }, pepper?: string): string;
99
+ /**
100
+ * The `addressHash` key: a postal address, normalised (case, punctuation and
101
+ * spacing folded; the postcode without spaces) and hashed on your side.
102
+ */
103
+ export declare function addressHash(address: {
104
+ line1: string;
105
+ line2?: string;
106
+ postcode: string;
107
+ country: string;
108
+ }, pepper?: string): string;
109
+ export interface CaseWebhook {
110
+ readonly type: 'case.approved' | 'case.rejected' | 'case.closed';
111
+ readonly caseId: string;
112
+ readonly tenant: string;
113
+ readonly queue: 'review' | 'rg';
114
+ /** Your transaction ids (`requestId`) and our evaluation ids, so you can find what you held. */
115
+ readonly requests: readonly {
116
+ requestId: string;
117
+ evaluationId: string;
118
+ action: string;
119
+ amount: number | null;
120
+ }[];
121
+ readonly reasons: readonly string[];
122
+ readonly note: string;
123
+ readonly decidedAt: string;
124
+ }
125
+ /**
126
+ * Checks the `x-mshield-signature` header on a case webhook and returns the
127
+ * event, or undefined when the signature is wrong or older than the tolerance.
128
+ *
129
+ * Pass the raw body exactly as received — parsing and re-serialising it
130
+ * changes the bytes the signature covers. Answer a valid delivery with 2xx
131
+ * quickly; release or cancel the held transaction in your own queue.
132
+ */
133
+ export declare function verifyCaseWebhook(secret: string, header: string | undefined, rawBody: string | Buffer, options?: {
134
+ toleranceSeconds?: number;
135
+ nowMs?: number;
136
+ }): CaseWebhook | undefined;
137
+ export {};
138
+ //# sourceMappingURL=igaming.d.ts.map
@@ -0,0 +1,173 @@
1
+ import { createHash, createHmac, timingSafeEqual } from 'node:crypto';
2
+ const COMMON = {
3
+ allow: 'proceed',
4
+ allow_flagged: 'proceed',
5
+ review: 'hold',
6
+ challenge: 'challenge',
7
+ step_up: 'step_up',
8
+ throttle: 'retry_later',
9
+ deny: 'refuse',
10
+ ban: 'refuse',
11
+ };
12
+ /**
13
+ * Per action, where it differs from the common mapping. A deposit under review
14
+ * is taken: holding a player's incoming money helps nobody, and the case is
15
+ * decided before any of it can be withdrawn.
16
+ */
17
+ const BY_ACTION = {
18
+ deposit: { review: 'proceed' },
19
+ withdraw: {},
20
+ bonus_claim: {},
21
+ };
22
+ const ADVICE = {
23
+ deposit: {
24
+ proceed: 'Take the deposit.',
25
+ hold: 'Hold the deposit until the case is decided.',
26
+ challenge: 'Show the challenge, then retry the deposit with the solution.',
27
+ step_up: 'Ask for 3-D Secure or your own stronger check before taking it.',
28
+ retry_later: 'Too many attempts: ask the player to try again shortly.',
29
+ refuse: 'Decline the deposit.',
30
+ },
31
+ withdraw: {
32
+ proceed: 'Pay out.',
33
+ hold: 'Keep the withdrawal pending. Release it on case.approved; cancel it and return the funds on case.rejected.',
34
+ challenge: 'Show the challenge, then retry the withdrawal with the solution.',
35
+ step_up: 'Ask for a second factor before paying out.',
36
+ retry_later: 'Too many attempts: ask the player to try again shortly.',
37
+ refuse: 'Decline the withdrawal; the funds stay in the account.',
38
+ },
39
+ bonus_claim: {
40
+ proceed: 'Credit the bonus.',
41
+ hold: 'Do not credit the bonus yet. Credit it on case.approved; withdraw the offer on case.rejected.',
42
+ challenge: 'Show the challenge, then retry the claim with the solution.',
43
+ step_up: 'Ask for a second factor or verification before crediting it.',
44
+ retry_later: 'Too many attempts: ask the player to try again shortly.',
45
+ refuse: 'Do not credit the bonus.',
46
+ },
47
+ };
48
+ /** The recommended handling of a decision on a money action. */
49
+ export function handlingFor(action, decision) {
50
+ return BY_ACTION[action][decision] ?? COMMON[decision];
51
+ }
52
+ export async function evaluateMoney(shield, action, request, onError) {
53
+ const verdict = await shield.evaluate(moneyInput(action, request, onError));
54
+ const handling = handlingFor(action, verdict.decision);
55
+ return { verdict, handling, advice: ADVICE[action][handling] };
56
+ }
57
+ /** The Evaluate call a money helper makes. Exported for tests and for integrators who build their own. */
58
+ export function moneyInput(action, request, onError) {
59
+ const payment = 'amount' in request ? request : undefined;
60
+ const bonus = 'bonusType' in request ? request : undefined;
61
+ let paymentMethod = payment?.paymentMethod;
62
+ if (paymentMethod !== undefined && looksLikeCardNumber(paymentMethod)) {
63
+ // The gateway refuses a request carrying a card number outright, and the
64
+ // client would then fail open with no decision at all. Dropping the key
65
+ // keeps the decision; the error says why the card was not counted.
66
+ onError?.(new Error(`${action}: paymentMethod looks like a card number and was not sent; send the PSP's card fingerprint or token`));
67
+ paymentMethod = undefined;
68
+ }
69
+ const keys = {
70
+ ...request.keys,
71
+ username: request.account,
72
+ ...(request.sessionId === undefined ? {} : { sessionId: request.sessionId }),
73
+ ...(paymentMethod === undefined ? {} : { paymentMethod }),
74
+ ...(payment?.paymentBin === undefined ? {} : { paymentBin: payment.paymentBin }),
75
+ ...(bonus?.promoCode === undefined ? {} : { promoCode: bonus.promoCode }),
76
+ ...(bonus?.referrerId === undefined ? {} : { referrerId: bonus.referrerId }),
77
+ ...(bonus?.affiliateId === undefined ? {} : { affiliateId: bonus.affiliateId }),
78
+ };
79
+ const amount = payment?.amount ?? bonus?.value;
80
+ const attributes = {
81
+ ...request.attributes,
82
+ ...(amount === undefined ? {} : { amount }),
83
+ ...(payment?.currency === undefined ? {} : { currency: payment.currency }),
84
+ ...(payment?.paymentType === undefined ? {} : { paymentType: payment.paymentType }),
85
+ ...(payment?.paymentCountry === undefined ? {} : { paymentCountry: payment.paymentCountry.toUpperCase() }),
86
+ ...(bonus === undefined ? {} : { bonusType: bonus.bonusType }),
87
+ };
88
+ return {
89
+ action,
90
+ requestId: request.transactionId,
91
+ keys,
92
+ attributes,
93
+ ...(request.token === undefined ? {} : { token: request.token }),
94
+ ...(request.ip === undefined ? {} : { ip: request.ip }),
95
+ ...(request.network === undefined ? {} : { network: request.network }),
96
+ ...(request.context === undefined ? {} : { context: request.context }),
97
+ };
98
+ }
99
+ /** Thirteen to nineteen digits passing the Luhn check: a PAN, which a PSP token is not. */
100
+ export function looksLikeCardNumber(value) {
101
+ const digits = value.replace(/[\s-]/g, '');
102
+ if (!/^\d{13,19}$/.test(digits))
103
+ return false;
104
+ let sum = 0;
105
+ for (let i = 0; i < digits.length; i += 1) {
106
+ let d = Number(digits[digits.length - 1 - i]);
107
+ if (i % 2 === 1) {
108
+ d *= 2;
109
+ if (d > 9)
110
+ d -= 9;
111
+ }
112
+ sum += d;
113
+ }
114
+ return sum % 10 === 0;
115
+ }
116
+ /* ── Hashing what Magic Shield must not see ─────────────────────────────── */
117
+ /**
118
+ * The `documentHash` key: an identity document, hashed on your side so its
119
+ * number never leaves your systems.
120
+ *
121
+ * Normalised first — country upper-cased, spaces, dashes and dots dropped from
122
+ * the number — because "AB 12-34" and "ab1234" are one passport, and two
123
+ * hashes for it would hide the very reuse the KYC rules look for. Pass your
124
+ * own `pepper` (a secret kept beside your database) so the hash cannot be
125
+ * reversed by trying every passport number; use the same one everywhere.
126
+ */
127
+ export function documentHash(document, pepper = '') {
128
+ const canonical = [document.country.trim().toUpperCase(), document.type.trim().toLowerCase(), document.number.replace(/[\s.\-/]/g, '').toUpperCase()].join(':');
129
+ return keyed(pepper, `document:${canonical}`);
130
+ }
131
+ /**
132
+ * The `addressHash` key: a postal address, normalised (case, punctuation and
133
+ * spacing folded; the postcode without spaces) and hashed on your side.
134
+ */
135
+ export function addressHash(address, pepper = '') {
136
+ const fold = (s) => s.toLowerCase().replace(/[.,#'"]/g, ' ').replace(/\s+/g, ' ').trim();
137
+ const canonical = [fold(address.line1), fold(address.line2 ?? ''), address.postcode.replace(/\s+/g, '').toUpperCase(), address.country.trim().toUpperCase()].join('|');
138
+ return keyed(pepper, `address:${canonical}`);
139
+ }
140
+ function keyed(pepper, value) {
141
+ return pepper === '' ? createHash('sha256').update(value).digest('hex') : createHmac('sha256', pepper).update(value).digest('hex');
142
+ }
143
+ /**
144
+ * Checks the `x-mshield-signature` header on a case webhook and returns the
145
+ * event, or undefined when the signature is wrong or older than the tolerance.
146
+ *
147
+ * Pass the raw body exactly as received — parsing and re-serialising it
148
+ * changes the bytes the signature covers. Answer a valid delivery with 2xx
149
+ * quickly; release or cancel the held transaction in your own queue.
150
+ */
151
+ export function verifyCaseWebhook(secret, header, rawBody, options = {}) {
152
+ if (header === undefined || secret === '')
153
+ return undefined;
154
+ const parts = new Map(header.split(',').map((part) => part.trim().split('=', 2)));
155
+ const t = Number(parts.get('t'));
156
+ const v1 = parts.get('v1') ?? '';
157
+ if (!Number.isInteger(t) || !/^[0-9a-f]{64}$/.test(v1))
158
+ return undefined;
159
+ const now = Math.floor((options.nowMs ?? Date.now()) / 1000);
160
+ if (Math.abs(now - t) > (options.toleranceSeconds ?? 300))
161
+ return undefined;
162
+ const body = typeof rawBody === 'string' ? rawBody : rawBody.toString('utf8');
163
+ const expected = createHmac('sha256', secret).update(`${t}.${body}`).digest();
164
+ if (!timingSafeEqual(expected, Buffer.from(v1, 'hex')))
165
+ return undefined;
166
+ try {
167
+ return JSON.parse(body);
168
+ }
169
+ catch {
170
+ return undefined;
171
+ }
172
+ }
173
+ //# sourceMappingURL=igaming.js.map
package/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ export { createClient, ShieldClient, API_KEY_METADATA } from './client.js';
2
2
  export type { ShieldConfig, ShieldDefaults } from './client.js';
3
3
  export { passwordPrefix, isPasswordPrefix } from './password.js';
4
4
  export { Breaker } from './breaker.js';
5
+ export { addressHash, documentHash, handlingFor, looksLikeCardNumber, moneyInput, verifyCaseWebhook } from './igaming.js';
6
+ export type { BonusRequest, CaseWebhook, Handling, MoneyAction, MoneyVerdict, PaymentRequest } from './igaming.js';
5
7
  export type { BreakerOptions } from './breaker.js';
6
- export type { ChallengeOffer, ChallengeSolution, DecisionName, EvaluateInput, FeedbackInput, Identifiers, LimitCheckInput, LimitState, LimitVerdict, NetworkContext, OutcomeDetails, OutcomeName, Reason, TrustDeviceInput, TrustedDevice, Verdict, WouldHave, } from './types.js';
8
+ export type { ChallengeOffer, ChallengeSolution, DecisionName, EvaluateInput, FeedbackInput, Identifiers, LimitCheckInput, LimitState, LimitVerdict, NetworkContext, OutcomeDetails, OutcomeName, Reason, TransactionAttributes, TrustDeviceInput, TrustedDevice, Verdict, WouldHave, } from './types.js';
7
9
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { createClient, ShieldClient, API_KEY_METADATA } from './client.js';
2
2
  export { passwordPrefix, isPasswordPrefix } from './password.js';
3
3
  export { Breaker } from './breaker.js';
4
+ export { addressHash, documentHash, handlingFor, looksLikeCardNumber, moneyInput, verifyCaseWebhook } from './igaming.js';
4
5
  //# sourceMappingURL=index.js.map
package/dist/types.d.ts CHANGED
@@ -20,7 +20,13 @@
20
20
  * mapping to the wire enum is in `decisions.ts` and is the only place either
21
21
  * spelling appears.
22
22
  */
23
- export type DecisionName = 'allow' | 'allow_flagged' | 'challenge' | 'step_up' | 'throttle' | 'deny' | 'ban';
23
+ export type DecisionName = 'allow' | 'allow_flagged'
24
+ /**
25
+ * Let it proceed or hold it — your choice per action — while an analyst
26
+ * looks. For a withdrawal: hold it, and release or cancel it when the case
27
+ * webhook arrives.
28
+ */
29
+ | 'review' | 'challenge' | 'step_up' | 'throttle' | 'deny' | 'ban';
24
30
  /**
25
31
  * The eleven outcomes, in the short form `docs/api.md` §5 publishes.
26
32
  *
@@ -56,6 +62,47 @@ export interface Identifiers {
56
62
  * identifies a spraying campaign and identifies no individual.
57
63
  */
58
64
  readonly passwordSha1Prefix?: string;
65
+ /**
66
+ * The card fingerprint or token your PSP gives you, or a wallet id — never
67
+ * the card number. A value that looks like one is refused by the gateway.
68
+ */
69
+ readonly paymentMethod?: string;
70
+ /** The first six or eight digits of the card: the issuing bank, not the person. */
71
+ readonly paymentBin?: string;
72
+ readonly promoCode?: string;
73
+ readonly affiliateId?: string;
74
+ /** A hash of the identity document number, computed on your side. */
75
+ readonly documentHash?: string;
76
+ /** A hash of the normalised postal address, computed on your side. */
77
+ readonly addressHash?: string;
78
+ /** The account that referred this one. */
79
+ readonly referrerId?: string;
80
+ readonly custom?: Readonly<Record<string, string>>;
81
+ }
82
+ /**
83
+ * What the transaction is, for the money and account actions. Every field is
84
+ * optional; a rule that reads one you did not send abstains rather than
85
+ * matching.
86
+ */
87
+ export interface TransactionAttributes {
88
+ /** In your base currency, so one rule compares deposits in any currency. */
89
+ readonly amount?: number;
90
+ /** What the player paid in, for the record. */
91
+ readonly currency?: string;
92
+ readonly paymentType?: 'card' | 'prepaid' | 'e_wallet' | 'crypto' | 'bank' | (string & {});
93
+ /** ISO 3166 alpha-2 of the payment instrument. */
94
+ readonly paymentCountry?: string;
95
+ readonly bonusType?: string;
96
+ readonly gameCategory?: string;
97
+ readonly stake?: number;
98
+ readonly odds?: number;
99
+ readonly kycStatus?: 'none' | 'pending' | 'verified' | 'rejected' | (string & {});
100
+ readonly selfExcluded?: boolean;
101
+ readonly depositLimitReached?: boolean;
102
+ /** For `profile_change`: email, phone, password or address. */
103
+ readonly changed?: string;
104
+ /** The player's age in whole years, from the date of birth you hold — never the date itself. */
105
+ readonly age?: number;
59
106
  readonly custom?: Readonly<Record<string, string>>;
60
107
  }
61
108
  /** What the socket revealed, when something in front of you observed it. */
@@ -69,7 +116,11 @@ export interface NetworkContext {
69
116
  readonly headers?: Readonly<Record<string, string>>;
70
117
  }
71
118
  export interface EvaluateInput {
72
- /** `'login'`, `'signup'`, `'checkout'` — yours to name. */
119
+ /**
120
+ * `'login'`, `'signup'`, `'deposit'`, `'withdraw'`, `'bonus_claim'`… The
121
+ * standard names are what the shipped rules match; your own names work too,
122
+ * and can be mapped to the standard ones in the console.
123
+ */
73
124
  readonly action: string;
74
125
  /**
75
126
  * The sealed string the web SDK returned.
@@ -102,6 +153,8 @@ export interface EvaluateInput {
102
153
  readonly verboseReasons?: boolean;
103
154
  /** Free-form context the rules can read. `origin` is the one that matters. */
104
155
  readonly context?: Readonly<Record<string, string>>;
156
+ /** Amount, payment type, KYC status… for deposit, withdraw, bonus_claim and the like. */
157
+ readonly attributes?: TransactionAttributes;
105
158
  }
106
159
  export interface ChallengeSolution {
107
160
  readonly blob: string;
@@ -202,7 +255,7 @@ export type Verdict = (VerdictBase & {
202
255
  readonly decision: 'throttle';
203
256
  readonly retryAfterMs: number;
204
257
  }) | (VerdictBase & {
205
- readonly decision: 'allow' | 'allow_flagged' | 'step_up' | 'deny' | 'ban';
258
+ readonly decision: 'allow' | 'allow_flagged' | 'review' | 'step_up' | 'deny' | 'ban';
206
259
  });
207
260
  export interface FeedbackInput {
208
261
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magield/node",
3
- "version": "0.6.1",
3
+ "version": "0.8.2",
4
4
  "description": "Magic Shield backend SDK",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "files": [
@@ -23,14 +23,14 @@
23
23
  "types": "./dist/index.d.ts",
24
24
  "dependencies": {
25
25
  "@grpc/grpc-js": "^1.12.4",
26
- "@magield/proto": "0.6.1"
26
+ "@magield/proto": "0.8.2"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^22.10.2",
30
30
  "typescript": "^5.7.2",
31
31
  "vitest": "^2.1.8",
32
- "@magield/gateway": "0.6.1",
33
- "@magield/storage": "0.6.1"
32
+ "@magield/gateway": "0.8.2",
33
+ "@magield/storage": "0.8.2"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "tsc -p tsconfig.json",