wallet-core: use long-polling for P2P kyc

This commit is contained in:
Florian Dold 2023-06-05 13:33:14 +02:00
parent fda5a0ed87
commit e671880b9e
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
16 changed files with 419 additions and 99 deletions

View File

@ -15,6 +15,6 @@
"eslint": "^8.29.0", "eslint": "^8.29.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"nx": "15.0.1", "nx": "15.0.1",
"prettier": "^2.7.1" "prettier": "^2.8.8"
} }
} }

View File

@ -25,7 +25,7 @@
"@types/node": "^18.11.17", "@types/node": "^18.11.17",
"ava": "^4.3.3", "ava": "^4.3.3",
"esm": "^3.2.25", "esm": "^3.2.25",
"prettier": "^2.5.1", "prettier": "^2.8.8",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^4.9.4" "typescript": "^4.9.4"
}, },

View File

@ -33,7 +33,7 @@
"devDependencies": { "devDependencies": {
"@types/node": "^18.11.17", "@types/node": "^18.11.17",
"esbuild": "^0.17.7", "esbuild": "^0.17.7",
"prettier": "^2.5.1", "prettier": "^2.8.8",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^4.9.4" "typescript": "^4.9.4"
}, },

View File

@ -62,7 +62,7 @@
"@types/node": "^18.11.17", "@types/node": "^18.11.17",
"ava": "^4.3.3", "ava": "^4.3.3",
"esbuild": "^0.17.7", "esbuild": "^0.17.7",
"prettier": "^2.5.1", "prettier": "^2.8.8",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typescript": "^4.9.4" "typescript": "^4.9.4"
}, },

View File

@ -345,6 +345,8 @@ export interface TransactionPeerPullCredit extends TransactionCommon {
* URI to send to the other party. * URI to send to the other party.
*/ */
talerUri: string; talerUri: string;
kycUrl: string | undefined;
} }
/** /**
@ -413,6 +415,8 @@ export interface TransactionPeerPushCredit extends TransactionCommon {
* Amount that actually was (or will be) added to the wallet's balance. * Amount that actually was (or will be) added to the wallet's balance.
*/ */
amountEffective: AmountString; amountEffective: AmountString;
kycUrl: string | undefined;
} }
export enum PaymentStatus { export enum PaymentStatus {

View File

@ -32,7 +32,7 @@
], ],
"devDependencies": { "devDependencies": {
"@types/node": "^18.11.17", "@types/node": "^18.11.17",
"prettier": "^2.5.1", "prettier": "^2.8.8",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typedoc": "^0.23.16", "typedoc": "^0.23.16",
"typescript": "^4.9.4" "typescript": "^4.9.4"

View File

@ -64,7 +64,7 @@
"eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-react-hooks": "^4.3.0",
"jed": "^1.1.1", "jed": "^1.1.1",
"po2json": "^0.4.5", "po2json": "^0.4.5",
"prettier": "^2.5.1", "prettier": "^2.8.8",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"typedoc": "^0.23.16", "typedoc": "^0.23.16",
"typescript": "^4.9.4" "typescript": "^4.9.4"

View File

@ -1921,6 +1921,8 @@ export interface PeerPullPaymentInitiationRecord {
kycInfo?: KycPendingInfo; kycInfo?: KycPendingInfo;
kycUrl?: string;
withdrawalGroupId: string | undefined; withdrawalGroupId: string | undefined;
} }
@ -1989,6 +1991,8 @@ export interface PeerPushPaymentIncomingRecord {
currency: string | undefined; currency: string | undefined;
kycInfo?: KycPendingInfo; kycInfo?: KycPendingInfo;
kycUrl?: string;
} }
export enum PeerPullDebitRecordStatus { export enum PeerPullDebitRecordStatus {

View File

@ -36,7 +36,6 @@ import {
TalerErrorDetail, TalerErrorDetail,
TombstoneIdStr, TombstoneIdStr,
TransactionIdStr, TransactionIdStr,
TransactionType,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
WalletStoresV1, WalletStoresV1,

View File

@ -25,19 +25,26 @@ import {
Codec, Codec,
CoinPublicKeyString, CoinPublicKeyString,
CoinStatus, CoinStatus,
HttpStatusCode,
Logger, Logger,
NotificationType,
PayPeerInsufficientBalanceDetails, PayPeerInsufficientBalanceDetails,
TalerError,
TalerErrorCode,
TalerProtocolTimestamp, TalerProtocolTimestamp,
UnblindedSignature, UnblindedSignature,
buildCodecForObject, buildCodecForObject,
codecForAmountString, codecForAmountString,
codecForTimestamp, codecForTimestamp,
codecOptional, codecOptional,
j2s,
strcmp, strcmp,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { SpendCoinDetails } from "../crypto/cryptoImplementation.js"; import { SpendCoinDetails } from "../crypto/cryptoImplementation.js";
import { import {
DenominationRecord, DenominationRecord,
KycPendingInfo,
KycUserType,
PeerPushPaymentCoinSelection, PeerPushPaymentCoinSelection,
ReserveRecord, ReserveRecord,
} from "../db.js"; } from "../db.js";
@ -45,6 +52,7 @@ import { InternalWalletState } from "../internal-wallet-state.js";
import { checkDbInvariant } from "../util/invariants.js"; import { checkDbInvariant } from "../util/invariants.js";
import { getPeerPaymentBalanceDetailsInTx } from "./balance.js"; import { getPeerPaymentBalanceDetailsInTx } from "./balance.js";
import { getTotalRefreshCost } from "./refresh.js"; import { getTotalRefreshCost } from "./refresh.js";
import { OperationAttemptLongpollResult, OperationAttemptResult, OperationAttemptResultType } from "../util/retries.js";
const logger = new Logger("operations/peer-to-peer.ts"); const logger = new Logger("operations/peer-to-peer.ts");

View File

@ -26,6 +26,7 @@ import {
InitiatePeerPullCreditRequest, InitiatePeerPullCreditRequest,
InitiatePeerPullCreditResponse, InitiatePeerPullCreditResponse,
Logger, Logger,
TalerErrorCode,
TalerPreciseTimestamp, TalerPreciseTimestamp,
TransactionAction, TransactionAction,
TransactionMajorState, TransactionMajorState,
@ -33,12 +34,14 @@ import {
TransactionState, TransactionState,
TransactionType, TransactionType,
WalletAccountMergeFlags, WalletAccountMergeFlags,
WalletKycUuid,
codecForAny, codecForAny,
codecForWalletKycUuid, codecForWalletKycUuid,
constructPayPullUri, constructPayPullUri,
encodeCrock, encodeCrock,
getRandomBytes, getRandomBytes,
j2s, j2s,
makeErrorDetail,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
readSuccessResponseJsonOrErrorCode, readSuccessResponseJsonOrErrorCode,
@ -46,6 +49,8 @@ import {
throwUnexpectedRequestError, throwUnexpectedRequestError,
} from "@gnu-taler/taler-util/http"; } from "@gnu-taler/taler-util/http";
import { import {
KycPendingInfo,
KycUserType,
PeerPullPaymentInitiationRecord, PeerPullPaymentInitiationRecord,
PeerPullPaymentInitiationStatus, PeerPullPaymentInitiationStatus,
WithdrawalGroupStatus, WithdrawalGroupStatus,
@ -167,6 +172,75 @@ export async function queryPurseForPeerPullCredit(
}; };
} }
async function longpollKycStatus(
ws: InternalWalletState,
pursePub: string,
exchangeUrl: string,
kycInfo: KycPendingInfo,
userType: KycUserType,
): Promise<OperationAttemptResult> {
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPullCredit,
pursePub,
});
const retryTag = constructTaskIdentifier({
tag: PendingTaskType.PeerPullCredit,
pursePub,
});
runLongpollAsync(ws, retryTag, async (ct) => {
const url = new URL(
`kyc-check/${kycInfo.requirementRow}/${kycInfo.paytoHash}/${userType}`,
exchangeUrl,
);
url.searchParams.set("timeout_ms", "10000");
logger.info(`kyc url ${url.href}`);
const kycStatusRes = await ws.http.fetch(url.href, {
method: "GET",
cancellationToken: ct,
});
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
) {
const transitionInfo = await ws.db
.mktx((x) => [x.peerPullPaymentInitiations])
.runReadWrite(async (tx) => {
const peerIni = await tx.peerPullPaymentInitiations.get(
pursePub,
);
if (!peerIni) {
return;
}
if (
peerIni.status !== PeerPullPaymentInitiationStatus.PendingMergeKycRequired
) {
return;
}
const oldTxState = computePeerPullCreditTransactionState(peerIni);
peerIni.status = PeerPullPaymentInitiationStatus.PendingCreatePurse;
const newTxState = computePeerPullCreditTransactionState(peerIni);
await tx.peerPullPaymentInitiations.put(peerIni);
return { oldTxState, newTxState };
});
notifyTransition(ws, transactionId, transitionInfo);
return { ready: true };
} else if (kycStatusRes.status === HttpStatusCode.Accepted) {
// FIXME: Do we have to update the URL here?
return { ready: false };
} else {
throw Error(
`unexpected response from kyc-check (${kycStatusRes.status})`,
);
}
});
return {
type: OperationAttemptResultType.Longpoll,
};
}
export async function processPeerPullCredit( export async function processPeerPullCredit(
ws: InternalWalletState, ws: InternalWalletState,
pursePub: string, pursePub: string,
@ -233,20 +307,16 @@ export async function processPeerPullCredit(
type: OperationAttemptResultType.Longpoll, type: OperationAttemptResultType.Longpoll,
}; };
case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: { case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: {
const transactionId = constructTransactionIdentifier({ if (!pullIni.kycInfo) {
tag: TransactionType.PeerPullCredit, throw Error("invalid state, kycInfo required");
pursePub: pullIni.pursePub,
});
if (pullIni.kycInfo) {
await checkWithdrawalKycStatus(
ws,
pullIni.exchangeBaseUrl,
transactionId,
pullIni.kycInfo,
"individual",
);
} }
break; return await longpollKycStatus(
ws,
pursePub,
pullIni.exchangeBaseUrl,
pullIni.kycInfo,
"individual",
);
} }
case PeerPullPaymentInitiationStatus.PendingCreatePurse: case PeerPullPaymentInitiationStatus.PendingCreatePurse:
break; break;
@ -325,26 +395,7 @@ export async function processPeerPullCredit(
const respJson = await httpResp.json(); const respJson = await httpResp.json();
const kycPending = codecForWalletKycUuid().decode(respJson); const kycPending = codecForWalletKycUuid().decode(respJson);
logger.info(`kyc uuid response: ${j2s(kycPending)}`); logger.info(`kyc uuid response: ${j2s(kycPending)}`);
return processPeerPullCreditKycRequired(ws, pullIni, 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.PendingMergeKycRequired;
await tx.peerPullPaymentInitiations.put(peerIni);
});
return {
type: OperationAttemptResultType.Pending,
result: undefined,
};
} }
const resp = await readSuccessResponseJsonOrThrow(httpResp, codecForAny()); const resp = await readSuccessResponseJsonOrThrow(httpResp, codecForAny());
@ -368,6 +419,89 @@ export async function processPeerPullCredit(
}; };
} }
async function processPeerPullCreditKycRequired(
ws: InternalWalletState,
peerIni: PeerPullPaymentInitiationRecord,
kycPending: WalletKycUuid,
): Promise<OperationAttemptResult> {
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPullCredit,
pursePub: peerIni.pursePub,
});
const { pursePub } = peerIni;
const userType = "individual";
const url = new URL(
`kyc-check/${kycPending.requirement_row}/${kycPending.h_payto}/${userType}`,
peerIni.exchangeBaseUrl,
);
logger.info(`kyc url ${url.href}`);
const kycStatusRes = await ws.http.fetch(url.href, {
method: "GET",
});
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 {
type: OperationAttemptResultType.Finished,
result: undefined,
};
} else if (kycStatusRes.status === HttpStatusCode.Accepted) {
const kycStatus = await kycStatusRes.json();
logger.info(`kyc status: ${j2s(kycStatus)}`);
const { transitionInfo, result } = await ws.db
.mktx((x) => [x.peerPullPaymentInitiations])
.runReadWrite(async (tx) => {
const peerInc = await tx.peerPullPaymentInitiations.get(
pursePub,
);
if (!peerInc) {
return {
transitionInfo: undefined,
result: OperationAttemptResult.finishedEmpty(),
};
}
const oldTxState = computePeerPullCreditTransactionState(peerInc);
peerInc.kycInfo = {
paytoHash: kycPending.h_payto,
requirementRow: kycPending.requirement_row,
};
peerInc.kycUrl = kycStatus.kyc_url;
peerInc.status = PeerPullPaymentInitiationStatus.PendingMergeKycRequired;
const newTxState = computePeerPullCreditTransactionState(peerInc);
await tx.peerPullPaymentInitiations.put(peerInc);
// We'll remove this eventually! New clients should rely on the
// kycUrl field of the transaction, not the error code.
const res: OperationAttemptResult = {
type: OperationAttemptResultType.Error,
errorDetail: makeErrorDetail(
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED,
{
kycUrl: kycStatus.kyc_url,
},
),
};
return {
transitionInfo: { oldTxState, newTxState },
result: res,
};
});
notifyTransition(ws, transactionId, transitionInfo);
return {
type: OperationAttemptResultType.Pending,
result: undefined,
};
} else {
throw Error(`unexpected response from kyc-check (${kycStatusRes.status})`);
}
}
/** /**
* Check fees and available exchanges for a peer push payment initiation. * Check fees and available exchanges for a peer push payment initiation.
*/ */

View File

@ -44,10 +44,16 @@ import {
TransactionMajorState, TransactionMajorState,
TransactionMinorState, TransactionMinorState,
TransactionState, TransactionState,
TalerError,
TalerErrorCode,
WalletKycUuid,
makeErrorDetail,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http"; import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { import {
InternalWalletState, InternalWalletState,
KycPendingInfo,
KycUserType,
PeerPullDebitRecordStatus, PeerPullDebitRecordStatus,
PeerPushPaymentIncomingRecord, PeerPushPaymentIncomingRecord,
PeerPushPaymentIncomingStatus, PeerPushPaymentIncomingStatus,
@ -62,9 +68,12 @@ import {
queryCoinInfosForSelection, queryCoinInfosForSelection,
talerPaytoFromExchangeReserve, talerPaytoFromExchangeReserve,
} from "./pay-peer-common.js"; } from "./pay-peer-common.js";
import { constructTransactionIdentifier, notifyTransition, stopLongpolling } from "./transactions.js";
import { import {
checkWithdrawalKycStatus, constructTransactionIdentifier,
notifyTransition,
stopLongpolling,
} from "./transactions.js";
import {
getExchangeWithdrawalInfo, getExchangeWithdrawalInfo,
internalCreateWithdrawalGroup, internalCreateWithdrawalGroup,
} from "./withdraw.js"; } from "./withdraw.js";
@ -75,6 +84,7 @@ import {
constructTaskIdentifier, constructTaskIdentifier,
} from "../util/retries.js"; } from "../util/retries.js";
import { assertUnreachable } from "../util/assertUnreachable.js"; import { assertUnreachable } from "../util/assertUnreachable.js";
import { runLongpollAsync } from "./common.js";
const logger = new Logger("pay-peer-push-credit.ts"); const logger = new Logger("pay-peer-push-credit.ts");
@ -215,6 +225,156 @@ export async function preparePeerPushCredit(
}; };
} }
async function longpollKycStatus(
ws: InternalWalletState,
peerPushPaymentIncomingId: string,
exchangeUrl: string,
kycInfo: KycPendingInfo,
userType: KycUserType,
): Promise<OperationAttemptResult> {
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
peerPushPaymentIncomingId,
});
const retryTag = constructTaskIdentifier({
tag: PendingTaskType.PeerPushCredit,
peerPushPaymentIncomingId,
});
runLongpollAsync(ws, retryTag, async (ct) => {
const url = new URL(
`kyc-check/${kycInfo.requirementRow}/${kycInfo.paytoHash}/${userType}`,
exchangeUrl,
);
url.searchParams.set("timeout_ms", "10000");
logger.info(`kyc url ${url.href}`);
const kycStatusRes = await ws.http.fetch(url.href, {
method: "GET",
cancellationToken: ct,
});
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
) {
const transitionInfo = await ws.db
.mktx((x) => [x.peerPushPaymentIncoming])
.runReadWrite(async (tx) => {
const peerInc = await tx.peerPushPaymentIncoming.get(
peerPushPaymentIncomingId,
);
if (!peerInc) {
return;
}
if (
peerInc.status !==
PeerPushPaymentIncomingStatus.PendingMergeKycRequired
) {
return;
}
const oldTxState = computePeerPushCreditTransactionState(peerInc);
peerInc.status = PeerPushPaymentIncomingStatus.PendingMerge;
const newTxState = computePeerPushCreditTransactionState(peerInc);
await tx.peerPushPaymentIncoming.put(peerInc);
return { oldTxState, newTxState };
});
notifyTransition(ws, transactionId, transitionInfo);
return { ready: true };
} else if (kycStatusRes.status === HttpStatusCode.Accepted) {
// FIXME: Do we have to update the URL here?
return { ready: false };
} else {
throw Error(
`unexpected response from kyc-check (${kycStatusRes.status})`,
);
}
});
return {
type: OperationAttemptResultType.Longpoll,
};
}
async function processPeerPushCreditKycRequired(
ws: InternalWalletState,
peerInc: PeerPushPaymentIncomingRecord,
kycPending: WalletKycUuid,
): Promise<OperationAttemptResult> {
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
peerPushPaymentIncomingId: peerInc.peerPushPaymentIncomingId,
});
const { peerPushPaymentIncomingId } = peerInc;
const userType = "individual";
const url = new URL(
`kyc-check/${kycPending.requirement_row}/${kycPending.h_payto}/${userType}`,
peerInc.exchangeBaseUrl,
);
logger.info(`kyc url ${url.href}`);
const kycStatusRes = await ws.http.fetch(url.href, {
method: "GET",
});
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 {
type: OperationAttemptResultType.Finished,
result: undefined,
};
} else if (kycStatusRes.status === HttpStatusCode.Accepted) {
const kycStatus = await kycStatusRes.json();
logger.info(`kyc status: ${j2s(kycStatus)}`);
const { transitionInfo, result } = await ws.db
.mktx((x) => [x.peerPushPaymentIncoming])
.runReadWrite(async (tx) => {
const peerInc = await tx.peerPushPaymentIncoming.get(
peerPushPaymentIncomingId,
);
if (!peerInc) {
return {
transitionInfo: undefined,
result: OperationAttemptResult.finishedEmpty(),
};
}
const oldTxState = computePeerPushCreditTransactionState(peerInc);
peerInc.kycInfo = {
paytoHash: kycPending.h_payto,
requirementRow: kycPending.requirement_row,
};
peerInc.kycUrl = kycStatus.kyc_url;
peerInc.status = PeerPushPaymentIncomingStatus.PendingMergeKycRequired;
const newTxState = computePeerPushCreditTransactionState(peerInc);
await tx.peerPushPaymentIncoming.put(peerInc);
// We'll remove this eventually! New clients should rely on the
// kycUrl field of the transaction, not the error code.
const res: OperationAttemptResult = {
type: OperationAttemptResultType.Error,
errorDetail: makeErrorDetail(
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED,
{
kycUrl: kycStatus.kyc_url,
},
),
};
return {
transitionInfo: { oldTxState, newTxState },
result: res,
};
});
notifyTransition(ws, transactionId, transitionInfo);
return result;
} else {
throw Error(`unexpected response from kyc-check (${kycStatusRes.status})`);
}
}
export async function processPeerPushCredit( export async function processPeerPushCredit(
ws: InternalWalletState, ws: InternalWalletState,
peerPushPaymentIncomingId: string, peerPushPaymentIncomingId: string,
@ -246,17 +406,15 @@ export async function processPeerPushCredit(
const amount = Amounts.parseOrThrow(contractTerms.amount); const amount = Amounts.parseOrThrow(contractTerms.amount);
if ( if (
peerInc.status === PeerPushPaymentIncomingStatus.PendingMergeKycRequired && peerInc.status === PeerPushPaymentIncomingStatus.PendingMergeKycRequired
peerInc.kycInfo
) { ) {
const txId = constructTransactionIdentifier({ if (!peerInc.kycInfo) {
tag: TransactionType.PeerPushCredit, throw Error("invalid state, kycInfo required");
peerPushPaymentIncomingId: peerInc.peerPushPaymentIncomingId, }
}); return await longpollKycStatus(
await checkWithdrawalKycStatus(
ws, ws,
peerPushPaymentIncomingId,
peerInc.exchangeBaseUrl, peerInc.exchangeBaseUrl,
txId,
peerInc.kycInfo, peerInc.kycInfo,
"individual", "individual",
); );
@ -298,33 +456,16 @@ export async function processPeerPushCredit(
reserve_sig: sigRes.accountSig, reserve_sig: sigRes.accountSig,
}; };
const mergeHttpResp = await ws.http.postJson(mergePurseUrl.href, mergeReq); const mergeHttpResp = await ws.http.fetch(mergePurseUrl.href, {
method: "POST",
body: mergeReq,
});
if (mergeHttpResp.status === HttpStatusCode.UnavailableForLegalReasons) { if (mergeHttpResp.status === HttpStatusCode.UnavailableForLegalReasons) {
const respJson = await mergeHttpResp.json(); const respJson = await mergeHttpResp.json();
const kycPending = codecForWalletKycUuid().decode(respJson); const kycPending = codecForWalletKycUuid().decode(respJson);
logger.info(`kyc uuid response: ${j2s(kycPending)}`); logger.info(`kyc uuid response: ${j2s(kycPending)}`);
processPeerPushCreditKycRequired(ws, peerInc, 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.PendingMergeKycRequired;
await tx.peerPushPaymentIncoming.put(peerInc);
});
return {
type: OperationAttemptResultType.Pending,
result: undefined,
};
} }
logger.trace(`merge request: ${j2s(mergeReq)}`); logger.trace(`merge request: ${j2s(mergeReq)}`);
@ -412,7 +553,6 @@ export async function confirmPeerPushCredit(
}; };
} }
export async function processPeerPullDebit( export async function processPeerPullDebit(
ws: InternalWalletState, ws: InternalWalletState,
peerPullPaymentIncomingId: string, peerPullPaymentIncomingId: string,
@ -483,7 +623,6 @@ export async function processPeerPullDebit(
}; };
} }
export async function suspendPeerPushCreditTransaction( export async function suspendPeerPushCreditTransaction(
ws: InternalWalletState, ws: InternalWalletState,
peerPushPaymentIncomingId: string, peerPushPaymentIncomingId: string,
@ -767,4 +906,4 @@ export function computePeerPushCreditTransactionActions(
default: default:
assertUnreachable(pushCreditRecord.status); assertUnreachable(pushCreditRecord.status);
} }
} }

View File

@ -117,10 +117,38 @@ import {
suspendWithdrawalTransaction, suspendWithdrawalTransaction,
computeWithdrawalTransactionActions, computeWithdrawalTransactionActions,
} from "./withdraw.js"; } from "./withdraw.js";
import { computePeerPullCreditTransactionState, computePeerPullCreditTransactionActions, suspendPeerPullCreditTransaction, failPeerPullCreditTransaction, resumePeerPullCreditTransaction, abortPeerPullCreditTransaction } from "./pay-peer-pull-credit.js"; import {
import { computePeerPullDebitTransactionState, computePeerPullDebitTransactionActions, suspendPeerPullDebitTransaction, failPeerPullDebitTransaction, resumePeerPullDebitTransaction, abortPeerPullDebitTransaction } from "./pay-peer-pull-debit.js"; computePeerPullCreditTransactionState,
import { computePeerPushCreditTransactionState, computePeerPushCreditTransactionActions, suspendPeerPushCreditTransaction, failPeerPushCreditTransaction, resumePeerPushCreditTransaction, abortPeerPushCreditTransaction } from "./pay-peer-push-credit.js"; computePeerPullCreditTransactionActions,
import { computePeerPushDebitTransactionState, computePeerPushDebitTransactionActions, suspendPeerPushDebitTransaction, failPeerPushDebitTransaction, resumePeerPushDebitTransaction, abortPeerPushDebitTransaction } from "./pay-peer-push-debit.js"; suspendPeerPullCreditTransaction,
failPeerPullCreditTransaction,
resumePeerPullCreditTransaction,
abortPeerPullCreditTransaction,
} from "./pay-peer-pull-credit.js";
import {
computePeerPullDebitTransactionState,
computePeerPullDebitTransactionActions,
suspendPeerPullDebitTransaction,
failPeerPullDebitTransaction,
resumePeerPullDebitTransaction,
abortPeerPullDebitTransaction,
} from "./pay-peer-pull-debit.js";
import {
computePeerPushCreditTransactionState,
computePeerPushCreditTransactionActions,
suspendPeerPushCreditTransaction,
failPeerPushCreditTransaction,
resumePeerPushCreditTransaction,
abortPeerPushCreditTransaction,
} from "./pay-peer-push-credit.js";
import {
computePeerPushDebitTransactionState,
computePeerPushDebitTransactionActions,
suspendPeerPushDebitTransaction,
failPeerPushDebitTransaction,
resumePeerPushDebitTransaction,
abortPeerPushDebitTransaction,
} from "./pay-peer-push-debit.js";
const logger = new Logger("taler-wallet-core:transactions.ts"); const logger = new Logger("taler-wallet-core:transactions.ts");
@ -510,6 +538,7 @@ function buildTransactionForPeerPullCredit(
tag: TransactionType.PeerPullCredit, tag: TransactionType.PeerPullCredit,
pursePub: pullCredit.pursePub, pursePub: pullCredit.pursePub,
}), }),
kycUrl: pullCredit.kycUrl,
...(wsrOrt?.lastError ...(wsrOrt?.lastError
? { ? {
error: silentWithdrawalErrorForInvoice error: silentWithdrawalErrorForInvoice
@ -541,6 +570,7 @@ function buildTransactionForPeerPullCredit(
tag: TransactionType.PeerPullCredit, tag: TransactionType.PeerPullCredit,
pursePub: pullCredit.pursePub, pursePub: pullCredit.pursePub,
}), }),
kycUrl: pullCredit.kycUrl,
...(pullCreditOrt?.lastError ? { error: pullCreditOrt.lastError } : {}), ...(pullCreditOrt?.lastError ? { error: pullCreditOrt.lastError } : {}),
}; };
} }
@ -573,6 +603,7 @@ function buildTransactionForPeerPushCredit(
tag: TransactionType.PeerPushCredit, tag: TransactionType.PeerPushCredit,
peerPushPaymentIncomingId: pushInc.peerPushPaymentIncomingId, peerPushPaymentIncomingId: pushInc.peerPushPaymentIncomingId,
}), }),
kycUrl: pushInc.kycUrl,
...(wsrOrt?.lastError ? { error: wsrOrt.lastError } : {}), ...(wsrOrt?.lastError ? { error: wsrOrt.lastError } : {}),
}; };
} }
@ -589,6 +620,7 @@ function buildTransactionForPeerPushCredit(
expiration: peerContractTerms.purse_expiration, expiration: peerContractTerms.purse_expiration,
summary: peerContractTerms.summary, summary: peerContractTerms.summary,
}, },
kycUrl: pushInc.kycUrl,
timestamp: pushInc.timestamp, timestamp: pushInc.timestamp,
transactionId: constructTransactionIdentifier({ transactionId: constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit, tag: TransactionType.PeerPushCredit,

View File

@ -29,7 +29,7 @@
"devDependencies": { "devDependencies": {
"@types/node": "^18.11.17", "@types/node": "^18.11.17",
"esbuild": "^0.17.7", "esbuild": "^0.17.7",
"prettier": "^2.5.1", "prettier": "^2.8.8",
"rimraf": "^3.0.2" "rimraf": "^3.0.2"
}, },
"dependencies": { "dependencies": {

View File

@ -53,7 +53,7 @@
"postcss-load-config": "^4.0.1", "postcss-load-config": "^4.0.1",
"preact": "10.11.3", "preact": "10.11.3",
"preact-render-to-string": "^5.2.6", "preact-render-to-string": "^5.2.6",
"prettier": "^2.5.1", "prettier": "^2.8.8",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sass": "1.56.1", "sass": "1.56.1",
"swr": "2.0.3", "swr": "2.0.3",

View File

@ -11,7 +11,7 @@ importers:
eslint: ^8.29.0 eslint: ^8.29.0
eslint-config-prettier: ^8.5.0 eslint-config-prettier: ^8.5.0
nx: 15.0.1 nx: 15.0.1
prettier: ^2.7.1 prettier: ^2.8.8
devDependencies: devDependencies:
'@babel/core': 7.13.16 '@babel/core': 7.13.16
'@linaria/esbuild': 3.0.0-beta.23 '@linaria/esbuild': 3.0.0-beta.23
@ -20,7 +20,7 @@ importers:
eslint: 8.29.0 eslint: 8.29.0
eslint-config-prettier: 8.5.0_eslint@8.29.0 eslint-config-prettier: 8.5.0_eslint@8.29.0
nx: 15.0.1 nx: 15.0.1
prettier: 2.7.1 prettier: 2.8.8
packages/anastasis-core: packages/anastasis-core:
specifiers: specifiers:
@ -209,7 +209,7 @@ importers:
'@types/node': ^18.11.17 '@types/node': ^18.11.17
ava: ^4.3.3 ava: ^4.3.3
esm: ^3.2.25 esm: ^3.2.25
prettier: ^2.5.1 prettier: ^2.8.8
rimraf: ^3.0.2 rimraf: ^3.0.2
tslib: ^2.4.0 tslib: ^2.4.0
typescript: ^4.9.4 typescript: ^4.9.4
@ -219,7 +219,7 @@ importers:
'@types/node': 18.11.17 '@types/node': 18.11.17
ava: 4.3.3 ava: 4.3.3
esm: 3.2.25 esm: 3.2.25
prettier: 2.7.1 prettier: 2.8.8
rimraf: 3.0.2 rimraf: 3.0.2
typescript: 4.9.4 typescript: 4.9.4
@ -391,7 +391,7 @@ importers:
'@types/node': ^18.11.17 '@types/node': ^18.11.17
axios: ^0.27.2 axios: ^0.27.2
esbuild: ^0.17.7 esbuild: ^0.17.7
prettier: ^2.5.1 prettier: ^2.8.8
rimraf: ^3.0.2 rimraf: ^3.0.2
tslib: ^2.4.0 tslib: ^2.4.0
typescript: ^4.9.4 typescript: ^4.9.4
@ -403,7 +403,7 @@ importers:
devDependencies: devDependencies:
'@types/node': 18.11.17 '@types/node': 18.11.17
esbuild: 0.17.7 esbuild: 0.17.7
prettier: 2.7.1 prettier: 2.8.8
rimraf: 3.0.2 rimraf: 3.0.2
typescript: 4.9.4 typescript: 4.9.4
@ -415,7 +415,7 @@ importers:
esbuild: ^0.17.7 esbuild: ^0.17.7
fflate: ^0.7.4 fflate: ^0.7.4
jed: ^1.1.1 jed: ^1.1.1
prettier: ^2.5.1 prettier: ^2.8.8
rimraf: ^3.0.2 rimraf: ^3.0.2
tslib: ^2.4.0 tslib: ^2.4.0
typescript: ^4.9.4 typescript: ^4.9.4
@ -428,7 +428,7 @@ importers:
'@types/node': 18.11.17 '@types/node': 18.11.17
ava: 4.3.3 ava: 4.3.3
esbuild: 0.17.7 esbuild: 0.17.7
prettier: 2.7.1 prettier: 2.8.8
rimraf: 3.0.2 rimraf: 3.0.2
typescript: 4.9.4 typescript: 4.9.4
@ -437,7 +437,7 @@ importers:
'@gnu-taler/taler-util': workspace:* '@gnu-taler/taler-util': workspace:*
'@gnu-taler/taler-wallet-core': workspace:* '@gnu-taler/taler-wallet-core': workspace:*
'@types/node': ^18.11.17 '@types/node': ^18.11.17
prettier: ^2.5.1 prettier: ^2.8.8
rimraf: ^3.0.2 rimraf: ^3.0.2
tslib: ^2.4.0 tslib: ^2.4.0
typedoc: ^0.23.16 typedoc: ^0.23.16
@ -448,7 +448,7 @@ importers:
tslib: 2.4.0 tslib: 2.4.0
devDependencies: devDependencies:
'@types/node': 18.11.17 '@types/node': 18.11.17
prettier: 2.7.1 prettier: 2.8.8
rimraf: 3.0.2 rimraf: 3.0.2
typedoc: 0.23.18_typescript@4.9.4 typedoc: 0.23.18_typescript@4.9.4
typescript: 4.9.4 typescript: 4.9.4
@ -475,7 +475,7 @@ importers:
fflate: ^0.7.4 fflate: ^0.7.4
jed: ^1.1.1 jed: ^1.1.1
po2json: ^0.4.5 po2json: ^0.4.5
prettier: ^2.5.1 prettier: ^2.8.8
rimraf: ^3.0.2 rimraf: ^3.0.2
tslib: ^2.4.0 tslib: ^2.4.0
typedoc: ^0.23.16 typedoc: ^0.23.16
@ -503,7 +503,7 @@ importers:
eslint-plugin-react-hooks: 4.6.0_eslint@8.26.0 eslint-plugin-react-hooks: 4.6.0_eslint@8.26.0
jed: 1.1.1 jed: 1.1.1
po2json: 0.4.5 po2json: 0.4.5
prettier: 2.7.1 prettier: 2.8.8
rimraf: 3.0.2 rimraf: 3.0.2
typedoc: 0.23.18_typescript@4.9.4 typedoc: 0.23.18_typescript@4.9.4
typescript: 4.9.4 typescript: 4.9.4
@ -515,7 +515,7 @@ importers:
'@gnu-taler/taler-wallet-core': workspace:* '@gnu-taler/taler-wallet-core': workspace:*
'@types/node': ^18.11.17 '@types/node': ^18.11.17
esbuild: ^0.17.7 esbuild: ^0.17.7
prettier: ^2.5.1 prettier: ^2.8.8
rimraf: ^3.0.2 rimraf: ^3.0.2
tslib: ^2.4.0 tslib: ^2.4.0
dependencies: dependencies:
@ -526,7 +526,7 @@ importers:
devDependencies: devDependencies:
'@types/node': 18.11.17 '@types/node': 18.11.17
esbuild: 0.17.7 esbuild: 0.17.7
prettier: 2.7.1 prettier: 2.8.8
rimraf: 3.0.2 rimraf: 3.0.2
packages/taler-wallet-webextension: packages/taler-wallet-webextension:
@ -621,7 +621,7 @@ importers:
postcss-load-config: ^4.0.1 postcss-load-config: ^4.0.1
preact: 10.11.3 preact: 10.11.3
preact-render-to-string: ^5.2.6 preact-render-to-string: ^5.2.6
prettier: ^2.5.1 prettier: ^2.8.8
rimraf: ^3.0.2 rimraf: ^3.0.2
sass: 1.56.1 sass: 1.56.1
swr: 2.0.3 swr: 2.0.3
@ -655,7 +655,7 @@ importers:
postcss-load-config: 4.0.1_postcss@8.4.23 postcss-load-config: 4.0.1_postcss@8.4.23
preact: 10.11.3 preact: 10.11.3
preact-render-to-string: 5.2.6_preact@10.11.3 preact-render-to-string: 5.2.6_preact@10.11.3
prettier: 2.7.1 prettier: 2.8.8
rimraf: 3.0.2 rimraf: 3.0.2
sass: 1.56.1 sass: 1.56.1
swr: 2.0.3 swr: 2.0.3
@ -14170,8 +14170,8 @@ packages:
engines: {node: '>=4'} engines: {node: '>=4'}
dev: true dev: true
/prettier/2.7.1: /prettier/2.8.8:
resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==} resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
engines: {node: '>=10.13.0'} engines: {node: '>=10.13.0'}
hasBin: true hasBin: true
dev: true dev: true