wallet-core: use long-polling for P2P kyc
This commit is contained in:
parent
fda5a0ed87
commit
e671880b9e
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
},
|
},
|
||||||
|
@ -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"
|
||||||
},
|
},
|
||||||
|
@ -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"
|
||||||
},
|
},
|
||||||
|
@ -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 {
|
||||||
|
@ -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"
|
||||||
|
@ -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"
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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");
|
||||||
|
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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": {
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user