re-use the same kyc function from withdrawal for deposits
This commit is contained in:
parent
eeea3e62a0
commit
2c14a180c1
@ -1361,7 +1361,8 @@ export type WgInfo =
|
|||||||
| WgInfoBankPeerPush
|
| WgInfoBankPeerPush
|
||||||
| WgInfoBankRecoup;
|
| WgInfoBankRecoup;
|
||||||
|
|
||||||
export interface WithdrawalKycPendingInfo {
|
export type KycUserType = "individual" | "business";
|
||||||
|
export interface KycPendingInfo {
|
||||||
paytoHash: string;
|
paytoHash: string;
|
||||||
requirementRow: number;
|
requirementRow: number;
|
||||||
}
|
}
|
||||||
@ -1380,7 +1381,7 @@ export interface WithdrawalGroupRecord {
|
|||||||
|
|
||||||
wgInfo: WgInfo;
|
wgInfo: WgInfo;
|
||||||
|
|
||||||
kycPending?: WithdrawalKycPendingInfo;
|
kycPending?: KycPendingInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Secret seed used to derive planchets.
|
* Secret seed used to derive planchets.
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
AbsoluteTime,
|
AbsoluteTime,
|
||||||
AmountJson,
|
AmountJson,
|
||||||
Amounts,
|
Amounts,
|
||||||
|
bytesToString,
|
||||||
CancellationToken,
|
CancellationToken,
|
||||||
canonicalJson,
|
canonicalJson,
|
||||||
codecForDepositSuccess,
|
codecForDepositSuccess,
|
||||||
@ -35,6 +36,7 @@ import {
|
|||||||
ExchangeDepositRequest,
|
ExchangeDepositRequest,
|
||||||
GetFeeForDepositRequest,
|
GetFeeForDepositRequest,
|
||||||
getRandomBytes,
|
getRandomBytes,
|
||||||
|
hashTruncate32,
|
||||||
hashWire,
|
hashWire,
|
||||||
HttpStatusCode,
|
HttpStatusCode,
|
||||||
Logger,
|
Logger,
|
||||||
@ -44,6 +46,7 @@ import {
|
|||||||
PrepareDepositRequest,
|
PrepareDepositRequest,
|
||||||
PrepareDepositResponse,
|
PrepareDepositResponse,
|
||||||
RefreshReason,
|
RefreshReason,
|
||||||
|
stringToBytes,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
TalerProtocolTimestamp,
|
TalerProtocolTimestamp,
|
||||||
TrackDepositGroupRequest,
|
TrackDepositGroupRequest,
|
||||||
@ -59,6 +62,7 @@ import {
|
|||||||
TransactionStatus,
|
TransactionStatus,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { TalerError } from "../errors.js";
|
import { TalerError } from "../errors.js";
|
||||||
|
import { checkKycStatus } from "../index.js";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
|
import { readSuccessResponseJsonOrThrow } from "../util/http.js";
|
||||||
import { OperationAttemptResult } from "../util/retries.js";
|
import { OperationAttemptResult } from "../util/retries.js";
|
||||||
@ -151,7 +155,28 @@ export async function processDepositGroup(
|
|||||||
|
|
||||||
if (depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired) {
|
if (depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired) {
|
||||||
const track = await trackDepositPermission(ws, depositGroup, perm);
|
const track = await trackDepositPermission(ws, depositGroup, perm);
|
||||||
updatedTxStatus = txStatusFromTrack(track);
|
|
||||||
|
if (track.type === "accepted") {
|
||||||
|
if (!track.kyc_ok && track.requirement_row !== undefined) {
|
||||||
|
updatedTxStatus = TransactionStatus.KycRequired;
|
||||||
|
const { requirement_row: requirementRow } = track;
|
||||||
|
const paytoHash = encodeCrock(
|
||||||
|
hashTruncate32(stringToBytes(depositGroup.wire.payto_uri + "\0")),
|
||||||
|
);
|
||||||
|
await checkKycStatus(
|
||||||
|
ws,
|
||||||
|
perm.exchange_url,
|
||||||
|
{ paytoHash, requirementRow },
|
||||||
|
"individual",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
updatedTxStatus = TransactionStatus.Accepted;
|
||||||
|
}
|
||||||
|
} else if (track.type === "wired") {
|
||||||
|
updatedTxStatus = TransactionStatus.Wired;
|
||||||
|
} else {
|
||||||
|
updatedTxStatus = TransactionStatus.Unknown;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updatedTxStatus !== undefined || updatedDeposit !== undefined) {
|
if (updatedTxStatus !== undefined || updatedDeposit !== undefined) {
|
||||||
@ -199,19 +224,6 @@ export async function processDepositGroup(
|
|||||||
return OperationAttemptResult.finishedEmpty();
|
return OperationAttemptResult.finishedEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
function txStatusFromTrack(t: TrackTransaction): TransactionStatus {
|
|
||||||
if (t.type === "accepted") {
|
|
||||||
if (!t.kyc_ok && t.requirement_row !== undefined) {
|
|
||||||
return TransactionStatus.KycRequired;
|
|
||||||
}
|
|
||||||
return TransactionStatus.Accepted;
|
|
||||||
}
|
|
||||||
if (t.type === "wired") {
|
|
||||||
return TransactionStatus.Wired;
|
|
||||||
}
|
|
||||||
return TransactionStatus.Unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function trackDepositGroup(
|
export async function trackDepositGroup(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
req: TrackDepositGroupRequest,
|
req: TrackDepositGroupRequest,
|
||||||
|
@ -26,7 +26,6 @@ import {
|
|||||||
AmountJson,
|
AmountJson,
|
||||||
AmountLike,
|
AmountLike,
|
||||||
Amounts,
|
Amounts,
|
||||||
AmountString,
|
|
||||||
BankWithdrawDetails,
|
BankWithdrawDetails,
|
||||||
CancellationToken,
|
CancellationToken,
|
||||||
canonicalizeBaseUrl,
|
canonicalizeBaseUrl,
|
||||||
@ -70,13 +69,14 @@ import {
|
|||||||
CoinSourceType,
|
CoinSourceType,
|
||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
DenominationVerificationStatus,
|
DenominationVerificationStatus,
|
||||||
|
KycPendingInfo,
|
||||||
|
KycUserType,
|
||||||
PlanchetRecord,
|
PlanchetRecord,
|
||||||
PlanchetStatus,
|
PlanchetStatus,
|
||||||
WalletStoresV1,
|
WalletStoresV1,
|
||||||
WgInfo,
|
WgInfo,
|
||||||
WithdrawalGroupRecord,
|
WithdrawalGroupRecord,
|
||||||
WithdrawalGroupStatus,
|
WithdrawalGroupStatus,
|
||||||
WithdrawalKycPendingInfo,
|
|
||||||
WithdrawalRecordType,
|
WithdrawalRecordType,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import {
|
import {
|
||||||
@ -86,7 +86,6 @@ import {
|
|||||||
} from "../errors.js";
|
} from "../errors.js";
|
||||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||||
import {
|
import {
|
||||||
getExchangeTosStatus,
|
|
||||||
makeCoinAvailable,
|
makeCoinAvailable,
|
||||||
makeExchangeListItem,
|
makeExchangeListItem,
|
||||||
runOperationWithErrorReporting,
|
runOperationWithErrorReporting,
|
||||||
@ -927,7 +926,7 @@ async function queryReserve(
|
|||||||
);
|
);
|
||||||
reserveUrl.searchParams.set("timeout_ms", "30000");
|
reserveUrl.searchParams.set("timeout_ms", "30000");
|
||||||
|
|
||||||
logger.info(`querying reserve status via ${reserveUrl}`);
|
logger.info(`querying reserve status via ${reserveUrl.href}`);
|
||||||
|
|
||||||
const resp = await ws.http.get(reserveUrl.href, {
|
const resp = await ws.http.get(reserveUrl.href, {
|
||||||
timeout: getReserveRequestTimeout(withdrawalGroup),
|
timeout: getReserveRequestTimeout(withdrawalGroup),
|
||||||
@ -1165,9 +1164,9 @@ export async function processWithdrawalGroup(
|
|||||||
let numFinished = 0;
|
let numFinished = 0;
|
||||||
let numKycRequired = 0;
|
let numKycRequired = 0;
|
||||||
let finishedForFirstTime = false;
|
let finishedForFirstTime = false;
|
||||||
let errorsPerCoin: Record<number, TalerErrorDetail> = {};
|
const errorsPerCoin: Record<number, TalerErrorDetail> = {};
|
||||||
|
|
||||||
let res = await ws.db
|
const res = await ws.db
|
||||||
.mktx((x) => [x.coins, x.withdrawalGroups, x.planchets])
|
.mktx((x) => [x.coins, x.withdrawalGroups, x.planchets])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
|
const wg = await tx.withdrawalGroups.get(withdrawalGroupId);
|
||||||
@ -1210,39 +1209,22 @@ export async function processWithdrawalGroup(
|
|||||||
|
|
||||||
if (numKycRequired > 0) {
|
if (numKycRequired > 0) {
|
||||||
if (kycInfo) {
|
if (kycInfo) {
|
||||||
const url = new URL(
|
await checkKycStatus(
|
||||||
`kyc-check/${kycInfo.requirementRow}/${kycInfo.paytoHash}/individual`,
|
ws,
|
||||||
withdrawalGroup.exchangeBaseUrl,
|
withdrawalGroup.exchangeBaseUrl,
|
||||||
|
kycInfo,
|
||||||
|
"individual",
|
||||||
);
|
);
|
||||||
logger.info(`kyc url ${url.href}`);
|
return {
|
||||||
const kycStatusReq = await ws.http.fetch(url.href, {
|
type: OperationAttemptResultType.Pending,
|
||||||
method: "GET",
|
result: undefined,
|
||||||
});
|
};
|
||||||
logger.warn("kyc requested, but already fulfilled");
|
|
||||||
if (kycStatusReq.status === HttpStatusCode.Ok) {
|
|
||||||
return {
|
|
||||||
type: OperationAttemptResultType.Pending,
|
|
||||||
result: undefined,
|
|
||||||
};
|
|
||||||
} else if (kycStatusReq.status === HttpStatusCode.Accepted) {
|
|
||||||
const kycStatus = await kycStatusReq.json();
|
|
||||||
logger.info(`kyc status: ${j2s(kycStatus)}`);
|
|
||||||
throw TalerError.fromDetail(
|
|
||||||
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED,
|
|
||||||
{
|
|
||||||
kycUrl: kycStatus.kyc_url,
|
|
||||||
},
|
|
||||||
`KYC check required for withdrawal`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw Error(
|
|
||||||
`unexpected response from kyc-check (${kycStatusReq.status})`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw TalerError.fromDetail(
|
throw TalerError.fromDetail(
|
||||||
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED,
|
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED,
|
||||||
{},
|
{
|
||||||
|
//FIXME we can't rise KYC error here since we don't have the url
|
||||||
|
} as any,
|
||||||
`KYC check required for withdrawal (not yet implemented in wallet-core)`,
|
`KYC check required for withdrawal (not yet implemented in wallet-core)`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1270,6 +1252,38 @@ export async function processWithdrawalGroup(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function checkKycStatus(
|
||||||
|
ws: InternalWalletState,
|
||||||
|
exchangeUrl: string,
|
||||||
|
kycInfo: KycPendingInfo,
|
||||||
|
userType: KycUserType,
|
||||||
|
): Promise<void> {
|
||||||
|
const url = new URL(
|
||||||
|
`kyc-check/${kycInfo.requirementRow}/${kycInfo.paytoHash}/${userType}`,
|
||||||
|
exchangeUrl,
|
||||||
|
);
|
||||||
|
logger.info(`kyc url ${url.href}`);
|
||||||
|
const kycStatusReq = await ws.http.fetch(url.href, {
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
logger.warn("kyc requested, but already fulfilled");
|
||||||
|
if (kycStatusReq.status === HttpStatusCode.Ok) {
|
||||||
|
return;
|
||||||
|
} else if (kycStatusReq.status === HttpStatusCode.Accepted) {
|
||||||
|
const kycStatus = await kycStatusReq.json();
|
||||||
|
logger.info(`kyc status: ${j2s(kycStatus)}`);
|
||||||
|
throw TalerError.fromDetail(
|
||||||
|
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED,
|
||||||
|
{
|
||||||
|
kycUrl: kycStatus.kyc_url,
|
||||||
|
},
|
||||||
|
`KYC check required for withdrawal`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw Error(`unexpected response from kyc-check (${kycStatusReq.status})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const AGE_MASK_GROUPS = "8:10:12:14:16:18"
|
const AGE_MASK_GROUPS = "8:10:12:14:16:18"
|
||||||
.split(":")
|
.split(":")
|
||||||
.map((n) => parseInt(n, 10));
|
.map((n) => parseInt(n, 10));
|
||||||
|
Loading…
Reference in New Issue
Block a user