re-use the same kyc function from withdrawal for deposits

This commit is contained in:
Sebastian 2023-01-17 15:59:30 -03:00
parent eeea3e62a0
commit 2c14a180c1
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
3 changed files with 77 additions and 50 deletions

View File

@ -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.

View File

@ -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,

View File

@ -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));