From efbde0e16033542ae104f5365be5cee6e65ef7b0 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 29 Mar 2023 00:06:24 -0300 Subject: handle kyc error on invoice and transfer --- .../taler-wallet-core/src/operations/pay-peer.ts | 103 ++++++++++++++++++++- .../taler-wallet-core/src/operations/withdraw.ts | 41 ++++---- 2 files changed, 123 insertions(+), 21 deletions(-) (limited to 'packages/taler-wallet-core/src/operations') diff --git a/packages/taler-wallet-core/src/operations/pay-peer.ts b/packages/taler-wallet-core/src/operations/pay-peer.ts index 73dd3bf2c..ff0e15c00 100644 --- a/packages/taler-wallet-core/src/operations/pay-peer.ts +++ b/packages/taler-wallet-core/src/operations/pay-peer.ts @@ -73,10 +73,15 @@ import { codecForTimestamp, CancellationToken, NotificationType, + HttpStatusCode, + codecForWalletKycUuid, + WalletKycUuid, } from "@gnu-taler/taler-util"; import { SpendCoinDetails } from "../crypto/cryptoImplementation.js"; import { DenominationRecord, + KycPendingInfo, + KycUserType, OperationStatus, PeerPullPaymentIncomingStatus, PeerPullPaymentInitiationRecord, @@ -115,6 +120,7 @@ import { getPeerPaymentBalanceDetailsInTx } from "./balance.js"; import { updateExchangeFromUrl } from "./exchanges.js"; import { getTotalRefreshCost } from "./refresh.js"; import { + checkWithdrawalKycStatus, getExchangeWithdrawalInfo, internalCreateWithdrawalGroup, processWithdrawalGroup, @@ -866,6 +872,23 @@ export async function processPeerPushCredit( const amount = Amounts.parseOrThrow(contractTerms.amount); + if ( + peerInc.status === PeerPushPaymentIncomingStatus.KycRequired && + peerInc.kycInfo + ) { + const txId = makeTransactionId( + TransactionType.PeerPushCredit, + peerInc.peerPushPaymentIncomingId, + ); + await checkWithdrawalKycStatus( + ws, + peerInc.exchangeBaseUrl, + txId, + peerInc.kycInfo, + "individual", + ); + } + const mergeReserveInfo = await getMergeReserveInfo(ws, { exchangeBaseUrl: peerInc.exchangeBaseUrl, }); @@ -902,10 +925,40 @@ export async function processPeerPushCredit( reserve_sig: sigRes.accountSig, }; - const mergeHttpReq = await ws.http.postJson(mergePurseUrl.href, mergeReq); + const mergeHttpResp = await ws.http.postJson(mergePurseUrl.href, mergeReq); + + if (mergeHttpResp.status === HttpStatusCode.UnavailableForLegalReasons) { + const respJson = await mergeHttpResp.json(); + const kycPending = codecForWalletKycUuid().decode(respJson); + logger.info(`kyc uuid response: ${j2s(kycPending)}`); + + await ws.db + .mktx((x) => [x.peerPushPaymentIncoming]) + .runReadWrite(async (tx) => { + const peerInc = await tx.peerPushPaymentIncoming.get( + peerPushPaymentIncomingId, + ); + if (!peerInc) { + return; + } + peerInc.kycInfo = { + paytoHash: kycPending.h_payto, + requirementRow: kycPending.requirement_row, + }; + peerInc.status = PeerPushPaymentIncomingStatus.KycRequired; + await tx.peerPushPaymentIncoming.put(peerInc); + }); + return { + type: OperationAttemptResultType.Pending, + result: undefined, + }; + } logger.trace(`merge request: ${j2s(mergeReq)}`); - const res = await readSuccessResponseJsonOrThrow(mergeHttpReq, codecForAny()); + const res = await readSuccessResponseJsonOrThrow( + mergeHttpResp, + codecForAny(), + ); logger.trace(`merge response: ${j2s(res)}`); await internalCreateWithdrawalGroup(ws, { @@ -932,7 +985,10 @@ export async function processPeerPushCredit( if (!peerInc) { return; } - if (peerInc.status === PeerPushPaymentIncomingStatus.Accepted) { + if ( + peerInc.status === PeerPushPaymentIncomingStatus.Accepted || + peerInc.status === PeerPushPaymentIncomingStatus.KycRequired + ) { peerInc.status = PeerPushPaymentIncomingStatus.WithdrawalCreated; } await tx.peerPushPaymentIncoming.put(peerInc); @@ -1423,6 +1479,22 @@ export async function processPeerPullCredit( return { type: OperationAttemptResultType.Longpoll, }; + case PeerPullPaymentInitiationStatus.KycRequired: { + if (pullIni.kycInfo) { + const txId = makeTransactionId( + TransactionType.PeerPullCredit, + pullIni.pursePub, + ); + await checkWithdrawalKycStatus( + ws, + pullIni.exchangeBaseUrl, + txId, + pullIni.kycInfo, + "individual", + ); + } + break; + } case PeerPullPaymentInitiationStatus.Initial: break; default: @@ -1496,6 +1568,31 @@ export async function processPeerPullCredit( reservePurseReqBody, ); + if (httpResp.status === HttpStatusCode.UnavailableForLegalReasons) { + const respJson = await httpResp.json(); + const kycPending = codecForWalletKycUuid().decode(respJson); + logger.info(`kyc uuid response: ${j2s(kycPending)}`); + + await ws.db + .mktx((x) => [x.peerPullPaymentInitiations]) + .runReadWrite(async (tx) => { + const peerIni = await tx.peerPullPaymentInitiations.get(pursePub); + if (!peerIni) { + return; + } + peerIni.kycInfo = { + paytoHash: kycPending.h_payto, + requirementRow: kycPending.requirement_row, + }; + peerIni.status = PeerPullPaymentInitiationStatus.KycRequired; + await tx.peerPullPaymentInitiations.put(peerIni); + }); + return { + type: OperationAttemptResultType.Pending, + result: undefined, + }; + } + const resp = await readSuccessResponseJsonOrThrow(httpResp, codecForAny()); logger.info(`reserve merge response: ${j2s(resp)}`); diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 3c3878792..2c91d4184 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -116,9 +116,7 @@ import { WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, WALLET_EXCHANGE_PROTOCOL_VERSION, } from "../versions.js"; -import { - makeTransactionId, -} from "./common.js"; +import { makeTransactionId } from "./common.js"; import { getExchangeDetails, getExchangePaytoUri, @@ -1226,9 +1224,14 @@ export async function processWithdrawalGroup( if (numKycRequired > 0) { if (kycInfo) { + const txId = makeTransactionId( + TransactionType.Withdrawal, + withdrawalGroup.withdrawalGroupId, + ); await checkWithdrawalKycStatus( ws, - withdrawalGroup, + withdrawalGroup.exchangeBaseUrl, + txId, kycInfo, "individual", ); @@ -1271,42 +1274,44 @@ export async function processWithdrawalGroup( export async function checkWithdrawalKycStatus( ws: InternalWalletState, - wg: WithdrawalGroupRecord, + exchangeUrl: string, + txId: string, kycInfo: KycPendingInfo, userType: KycUserType, ): Promise { - const exchangeUrl = wg.exchangeBaseUrl; 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, { + const kycStatusRes = await ws.http.fetch(url.href, { method: "GET", }); - if (kycStatusReq.status === HttpStatusCode.Ok) { + if ( + kycStatusRes.status === HttpStatusCode.Ok || + //FIXME: NoContent is not expected https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-merge + // remove after the exchange is fixed or clarified + kycStatusRes.status === HttpStatusCode.NoContent + ) { logger.warn("kyc requested, but already fulfilled"); return; - } else if (kycStatusReq.status === HttpStatusCode.Accepted) { - const kycStatus = await kycStatusReq.json(); + } else if (kycStatusRes.status === HttpStatusCode.Accepted) { + const kycStatus = await kycStatusRes.json(); logger.info(`kyc status: ${j2s(kycStatus)}`); ws.notify({ - type: NotificationType.WithdrawalGroupKycRequested, + type: NotificationType.KycRequested, kycUrl: kycStatus.kyc_url, - transactionId: makeTransactionId( - TransactionType.Withdrawal, - wg.withdrawalGroupId, - ), + transactionId: txId, }); throw TalerError.fromDetail( - TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED, + TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED, //FIXME: another error code or rename for merge { kycUrl: kycStatus.kyc_url, }, - `KYC check required for withdrawal`, + `KYC check required for transfer`, ); } else { - throw Error(`unexpected response from kyc-check (${kycStatusReq.status})`); + throw Error(`unexpected response from kyc-check (${kycStatusRes.status})`); } } -- cgit v1.2.3