Skip to content
Merged
97 changes: 97 additions & 0 deletions docs/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -381,14 +381,20 @@
"tables": [
"auto_withdrawal_rule",
"wallet",
"wallet_asset",
"wallet_auto_withdrawal_config",
"wallet_balance",
"wallet_custody_sweep",
"wallet_deposit_address",
"wallet_job_run",
"wallet_provider_vault",
"wallet_reconciliation_finding",
"wallet_transaction"
],
"routes": [
"wallet.approve",
"wallet.create",
"wallet.delete",
"wallet.delete",
"wallet.deposit",
"wallet.get",
Expand All @@ -397,13 +403,22 @@
"wallet.getBalance",
"wallet.getBalances",
"wallet.list",
"wallet.list",
"wallet.list",
"wallet.listAssets",
"wallet.listPlayerTransactions",
"wallet.listTransactions",
"wallet.manualAdjustment",
"wallet.reject",
"wallet.resolve",
"wallet.run",
"wallet.run",
"wallet.set",
"wallet.set",
"wallet.setActiveCurrency",
"wallet.update",
"wallet.webhook",
"wallet.webhookForProvider",
"wallet.withdraw"
]
}
Expand Down Expand Up @@ -824,6 +839,15 @@
"packages/core/src/wallet/plugin.ts"
]
},
{
"category": "wallet-asset-catalog",
"interface": "WalletAsset",
"token": "WALLET_ASSET_CATALOG",
"status": "wired",
"boundIn": [
"packages/core/src/wallet/plugin.ts"
]
},
{
"category": "wallet-commands",
"interface": "WalletDebitArgs",
Expand Down Expand Up @@ -929,6 +953,7 @@
"tag.player.removed",
"tag.rule.upserted",
"wallet.deposit.completed",
"wallet.manual_adjustment.created",
"wallet.withdrawal.approved",
"wallet.withdrawal.completed",
"wallet.withdrawal.failed",
Expand Down Expand Up @@ -1020,6 +1045,10 @@
"name": "AdminUserSortBySchema",
"file": "packages/core/src/admin-console/contract/index.ts"
},
{
"name": "AdminWalletTransactionSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "ApproveWithdrawalInputSchema",
"file": "packages/core/src/wallet/contract/index.ts"
Expand Down Expand Up @@ -1244,6 +1273,10 @@
"name": "createTagSchema",
"file": "packages/core/src/contracts/schemas/tag.ts"
},
{
"name": "CreateWalletAssetInputSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "CurrencyCodeSchema",
"file": "packages/core/src/contracts/schemas/igaming-config.ts"
Expand Down Expand Up @@ -1480,6 +1513,10 @@
"name": "InvitationStatusSchema",
"file": "packages/core/src/contracts/schemas/iam.ts"
},
{
"name": "JobRunResultSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "KycCheckResultSchema",
"file": "packages/core/src/contracts/adapters/kyc.ts"
Expand Down Expand Up @@ -1568,6 +1605,10 @@
"name": "ListFriendRequestsInputSchema",
"file": "packages/core/src/engagement/social/contract/index.ts"
},
{
"name": "ListReconciliationFindingsInputSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "ListRgFlagsInputSchema",
"file": "packages/core/src/compliance/contract/rg.ts"
Expand All @@ -1588,6 +1629,14 @@
"name": "LoginSecurityStateSchema",
"file": "packages/core/src/contracts/schemas/identity.ts"
},
{
"name": "ManualAdjustmentDirectionSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "ManualWalletAdjustmentInputSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "MemberSchema",
"file": "packages/core/src/contracts/schemas/identity.ts"
Expand Down Expand Up @@ -1652,6 +1701,10 @@
"name": "PaymentWebhookOutputSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "PaymentWebhookProviderInputSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "PermissionLevelSchema",
"file": "packages/core/src/contracts/schemas/iam.ts"
Expand Down Expand Up @@ -1788,10 +1841,18 @@
"name": "ProviderSelectionSchema",
"file": "packages/core/src/contracts/schemas/igaming-config.ts"
},
{
"name": "PublicWalletAssetSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "RainCommandMetadataSchema",
"file": "packages/core/src/contracts/schemas/chat-command-metadata.ts"
},
{
"name": "ReconciliationResolutionSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "RegisterInputSchema",
"file": "packages/core/src/contracts/schemas/identity.ts"
Expand Down Expand Up @@ -1844,6 +1905,10 @@
"name": "ResetPasswordInputSchema",
"file": "packages/core/src/contracts/schemas/identity.ts"
},
{
"name": "ResolveReconciliationFindingInputSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "RetentionCohortRowSchema",
"file": "packages/core/src/admin-console/contract/index.ts"
Expand Down Expand Up @@ -2004,6 +2069,10 @@
"name": "updateTagSchema",
"file": "packages/core/src/contracts/schemas/tag.ts"
},
{
"name": "UpdateWalletAssetInputSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "UpsertLimitInputSchema",
"file": "packages/core/src/compliance/contract/limits.ts"
Expand Down Expand Up @@ -2052,6 +2121,14 @@
"name": "VerifyPasswordResetOtpInputSchema",
"file": "packages/core/src/contracts/schemas/identity.ts"
},
{
"name": "WalletAssetKeySchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "WalletAssetSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "WalletAutoWithdrawalConfigSchema",
"file": "packages/core/src/wallet/contract/index.ts"
Expand All @@ -2068,10 +2145,30 @@
"name": "WalletConfigSchema",
"file": "packages/core/src/contracts/schemas/platform-config.ts"
},
{
"name": "WalletCustodySweepStatusSchema",
"file": "packages/core/src/contracts/schemas/wallet-tx.ts"
},
{
"name": "WalletJobRunStatusSchema",
"file": "packages/core/src/contracts/schemas/wallet-tx.ts"
},
{
"name": "WalletRailSchema",
"file": "packages/core/src/contracts/schemas/wallet-tx.ts"
},
{
"name": "WalletReconciliationFindingKindSchema",
"file": "packages/core/src/contracts/schemas/wallet-tx.ts"
},
{
"name": "WalletReconciliationFindingSchema",
"file": "packages/core/src/wallet/contract/index.ts"
},
{
"name": "WalletReconciliationFindingStatusSchema",
"file": "packages/core/src/contracts/schemas/wallet-tx.ts"
},
{
"name": "WalletTransactionSchema",
"file": "packages/core/src/wallet/contract/index.ts"
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/contracts/adapters/audit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export type DirectAuditAction =
| 'wallet.withdrawal.auto_approved'
| 'wallet.auto_withdrawal_rule.set'
| 'wallet.auto_withdrawal_rule.deleted'
| 'wallet.auto_withdrawal_config.set';
| 'wallet.auto_withdrawal_config.set'
| 'wallet.manual_adjustment.created';

/**
* Every value the audit `action` column legitimately holds: a cross-module domain
Expand Down
19 changes: 17 additions & 2 deletions packages/core/src/contracts/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,23 @@ export {
KycCheckResultSchema,
} from './kyc.js';

export type { PaymentAdapter, PaymentWebhookEvent, PaymentWebhookVerifier } from './payment.js';
export { PAYMENT_ADAPTER, PAYMENT_WEBHOOK_VERIFIER } from './payment.js';
export type {
PaymentAdapter,
PaymentWebhookEvent,
PaymentWebhookVerifier,
CustodyBalance,
PaymentProvider,
PaymentProviderRegistry,
} from './payment.js';
export {
PAYMENT_ADAPTER,
PAYMENT_WEBHOOK_VERIFIER,
PAYMENT_PROVIDERS,
DEFAULT_PAYMENT_PROVIDER,
} from './payment.js';

export type { WalletAsset, WalletAssetCatalog } from './wallet-asset-catalog.js';
export { WALLET_ASSET_CATALOG } from './wallet-asset-catalog.js';

export type { GeoIpAdapter } from './geo-ip.js';
export { GEO_IP_ADAPTER } from './geo-ip.js';
Expand Down
105 changes: 105 additions & 0 deletions packages/core/src/contracts/adapters/payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,50 @@ export type PaymentWebhookEvent =
txHash?: string;
};

/**
* A balance sitting in a per-player custody container that is not yet in the pooled
* account withdrawals are paid from. Produced by `PaymentAdapter.listSweepableBalances`
* and handed back to `sweepToPool` unchanged.
*/
export type CustodyBalance = {
userId: string;
currency: string;
network: string;
amount: string;
/** Current network cost to move it, same units as `amount`. */
estimatedFee: string;
};

/**
* Name of the single default `PaymentAdapter`/`PaymentWebhookVerifier` binding, wrapped
* as one `PaymentProviderRegistry` entry so an operator with one vendor changes nothing.
*/
export const DEFAULT_PAYMENT_PROVIDER = 'default';

/** One named vendor binding: the adapter it settles through and the verifier for its webhooks. */
export type PaymentProvider = {
adapter: PaymentAdapter;
webhookVerifier: PaymentWebhookVerifier;
};

/**
* Looks up a named vendor's adapter/verifier pair. `wallet_asset.providerName` is the
* key; a webhook route resolves both the verifier AND the adapter from the SAME entry so
* a request can never be verified against one vendor's key and parsed by another's
* format (signature confusion).
*
* Core only looks a name up here - it never discovers or enumerates vendors itself. The
* operator composes this map in their own plugin because `Container.register` is
* last-wins: two overlays each binding the single `PAYMENT_ADAPTER`/`PAYMENT_WEBHOOK_VERIFIER`
* tokens would clobber each other, so running a fiat PSP and a crypto custodian at once
* needs an operator-owned map from provider name to pair, not a second core-discovered
* binding slot.
*/
export type PaymentProviderRegistry = {
get(providerName: string): PaymentProvider | null;
names(): readonly string[];
};

export type PaymentAdapter = {
processDeposit(
amount: string,
Expand Down Expand Up @@ -80,6 +124,65 @@ export type PaymentAdapter = {
rawBody: string,
headers: Record<string, string | string[] | undefined>,
): PaymentWebhookEvent | null;

/**
* Whether this adapter can actually serve the given asset. The asset catalog is
* operator-editable at runtime, so an admin can name a (currency, network) pair the
* bound vendor has never heard of; the catalog's write path calls this first and
* rejects rather than letting the pair reach a player's deposit screen. Optional -
* an adapter that omits it is assumed to accept anything the operator configures.
*/
supportsAsset?(currency: string, network: string): boolean;

/**
* Per-player balances the vendor holds that are not yet in the pooled account.
* Only meaningful for a custody vendor whose per-player deposit containers are
* distinct from the account withdrawals are paid out of - a synchronous PSP, which
* never holds a per-player balance, omits it.
*/
listSweepableBalances?(): Promise<CustodyBalance[]>;

/**
* Move one player's balance into the pooled account. Implemented alongside
* `listSweepableBalances`; the caller owns the policy (dust floor, fee thresholds)
* and this only performs the transfer it is handed.
*
* `idempotencyKey` exists because a thrown call cannot be distinguished from a lost
* response - the vendor must dedupe the retry on this key rather than double-move
* funds. `poolRef` is an opaque vendor-side identifier for the destination pool,
* recorded so the ledger can evidence that player funds landed in the player pool and
* not an operator account - a regulator asks this.
*/
sweepToPool?(
balance: CustodyBalance,
opts: { idempotencyKey: string },
): Promise<{ externalId: string; poolRef?: string }>;

/**
* Balance currently in the pooled account for this asset. Consulted only when a sweep
* is blocked by the fee ceiling, to decide whether paying a high fee beats running the
* pool dry. Omit it and the ceiling is absolute.
*/
getPoolBalance?(currency: string, network: string): Promise<string>;

/**
* Vendor transactions in a window, normalized into the same events `parseWebhook`
* produces - reconciliation is that same normalization, polled instead of pushed.
* Implemented by a vendor whose ledger can be listed after the fact; a PSP that only
* pushes webhooks omits it.
*/
listTransactions?(range: { since: Date; until: Date }): Promise<PaymentWebhookEvent[]>;

/**
* Targeted status lookup for a single withdrawal, or null when the vendor has no
* record of it. Not redundant with `listTransactions`: a withdrawal stuck in
* `processing` for days falls outside any sane reconciliation window, so finalizing
* it needs a direct lookup by `externalId`.
*/
getWithdrawalStatus?(externalId: string): Promise<{
status: 'processing' | 'completed' | 'failed';
txHash?: string;
} | null>;
};

export const PAYMENT_ADAPTER: Token<PaymentAdapter> = createToken('PAYMENT_ADAPTER');
Expand All @@ -101,3 +204,5 @@ export type PaymentWebhookVerifier = {
export const PAYMENT_WEBHOOK_VERIFIER: Token<PaymentWebhookVerifier> = createToken(
'PAYMENT_WEBHOOK_VERIFIER',
);

export const PAYMENT_PROVIDERS: Token<PaymentProviderRegistry> = createToken('PAYMENT_PROVIDERS');
1 change: 1 addition & 0 deletions packages/core/src/contracts/adapters/rate-limit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const RATE_LIMIT_KEYS = {
EMAIL_VERIFICATION: 'email-verify',
VERIFY_EMAIL: 'verify-email',
WALLET_MUTATION: 'wallet-mutation',
WALLET_WEBHOOK: 'wallet-webhook',
CHAT_ROOM_JOIN: 'chat-room-join',
CHAT_SEND: 'chat-send',
REPORT_ACCESS_DENIED: 'report-access-denied',
Expand Down
Loading
Loading