aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2023-03-29 00:06:24 -0300
committerSebastian <sebasjm@gmail.com>2023-03-29 00:06:24 -0300
commitefbde0e16033542ae104f5365be5cee6e65ef7b0 (patch)
tree65129a0a7e9a9791fcd38343841878a54ad49bf6
parentf414ca39e446563eb10c3c20defe79952fe71e6a (diff)
handle kyc error on invoice and transfer
-rw-r--r--packages/taler-util/src/notifications.ts8
-rw-r--r--packages/taler-wallet-core/src/db.ts6
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer.ts103
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts41
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx21
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.tsx15
6 files changed, 155 insertions, 39 deletions
diff --git a/packages/taler-util/src/notifications.ts b/packages/taler-util/src/notifications.ts
index 51d573a98..0d85c85e9 100644
--- a/packages/taler-util/src/notifications.ts
+++ b/packages/taler-util/src/notifications.ts
@@ -62,7 +62,7 @@ export enum NotificationType {
PendingOperationProcessed = "pending-operation-processed",
ProposalRefused = "proposal-refused",
ReserveRegisteredWithBank = "reserve-registered-with-bank",
- WithdrawalGroupKycRequested = "withdrawal-group-kyc-requested",
+ KycRequested = "kyc-requested",
WithdrawalGroupBankConfirmed = "withdrawal-group-bank-confirmed",
WithdrawalGroupReserveReady = "withdrawal-group-reserve-ready",
PeerPullCreditReady = "peer-pull-credit-ready",
@@ -125,8 +125,8 @@ export interface RefreshMeltedNotification {
type: NotificationType.RefreshMelted;
}
-export interface WithdrawalGroupKycRequested {
- type: NotificationType.WithdrawalGroupKycRequested;
+export interface KycRequestedNotification {
+ type: NotificationType.KycRequested;
transactionId: string;
kycUrl: string;
}
@@ -324,7 +324,7 @@ export type WalletNotification =
| ReserveRegisteredWithBankNotification
| ReserveNotYetFoundNotification
| PayOperationSuccessNotification
- | WithdrawalGroupKycRequested
+ | KycRequestedNotification
| WithdrawalGroupBankConfirmed
| WithdrawalGroupReserveReadyNotification
| PeerPullCreditReadyNotification;
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index a7bdda3ec..cd676b7ca 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -1781,6 +1781,7 @@ export enum PeerPullPaymentInitiationStatus {
* invoice and deposit money into it.
*/
PurseCreated = 11 /* ACTIVE_START + 1 */,
+ KycRequired = 12 /* ACTIVE_START + 2 */,
PurseDeposited = 50 /* DORMANT_START */,
}
@@ -1831,12 +1832,15 @@ export interface PeerPullPaymentInitiationRecord {
*/
status: PeerPullPaymentInitiationStatus;
+ kycInfo?: KycPendingInfo;
+
withdrawalGroupId: string | undefined;
}
export enum PeerPushPaymentIncomingStatus {
Proposed = 30 /* USER_ATTENTION_START */,
Accepted = 10 /* ACTIVE_START */,
+ KycRequired = 11 /* ACTIVE_START + 1 */,
/**
* Merge was successful and withdrawal group has been created, now
* everything is in the hand of the withdrawal group.
@@ -1887,6 +1891,8 @@ export interface PeerPushPaymentIncomingRecord {
* with older (ver_minor<4) DB versions.
*/
currency: string | undefined;
+
+ kycInfo?: KycPendingInfo;
}
export enum PeerPullPaymentIncomingStatus {
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<void> {
- 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})`);
}
}
diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
index 2db2041d4..339e69213 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
@@ -140,12 +140,19 @@ export function View({
});
}
const api = useBackendContext();
+
const fileRef = useRef<HTMLInputElement>(null);
async function onImportDatabase(str: string): Promise<void> {
return api.wallet.call(WalletApiOperation.ImportDb, {
dump: JSON.parse(str),
});
}
+
+ const hook = useAsyncAsHook(() =>
+ api.wallet.call(WalletApiOperation.ListExchanges, {}),
+ );
+ const exchangeList = hook && !hook.hasError ? hook.response.exchanges : [];
+
const currencies: { [ex: string]: string } = {};
const money_by_exchange = coins.reduce(
(prev, cur) => {
@@ -171,7 +178,6 @@ export function View({
[exchange_name: string]: CalculatedCoinfInfo[];
},
);
- const exchanges = Object.keys(money_by_exchange);
const [tagName, setTagName] = useState("");
const [logLevel, setLogLevel] = useState("info");
@@ -324,27 +330,28 @@ export function View({
variant="contained"
onClick={async () => {
const result = await Promise.all(
- exchanges.map(async (ex) => {
+ exchangeList.map(async (exchange) => {
+ const url = exchange.exchangeBaseUrl;
const oldKeys = JSON.stringify(
- await (await fetch(`${ex}keys`)).json(),
+ await (await fetch(`${url}keys`)).json(),
);
const oldWire = JSON.stringify(
- await (await fetch(`${ex}wire`)).json(),
+ await (await fetch(`${url}wire`)).json(),
);
const newKeys = JSON.stringify(
await (
- await fetch(`${ex}keys`, { cache: "no-cache" })
+ await fetch(`${url}keys`, { cache: "no-cache" })
).json(),
);
const newWire = JSON.stringify(
await (
- await fetch(`${ex}wire`, { cache: "no-cache" })
+ await fetch(`${url}wire`, { cache: "no-cache" })
).json(),
);
return oldKeys !== newKeys || newWire !== oldWire;
}),
);
- const ex = exchanges.filter((e, i) => result[i]);
+ const ex = exchangeList.filter((e, i) => result[i]);
if (!ex.length) {
alert("no exchange was outdated");
} else {
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index b9b1aa198..217a77575 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -879,13 +879,14 @@ export function TransactionView({
kind="neutral"
/>
{transaction.extendedStatus ===
- ExtendedStatus.Pending /** pending is not-pay */ && (
- <Part
- title={i18n.str`URI`}
- text={<ShowQrWithCopy text={transaction.talerUri} />}
- kind="neutral"
- />
- )}
+ ExtendedStatus.Pending /** pending is not-pay */ &&
+ !transaction.error && (
+ <Part
+ title={i18n.str`URI`}
+ text={<ShowQrWithCopy text={transaction.talerUri} />}
+ kind="neutral"
+ />
+ )}
<Part
title={i18n.str`Details`}
text={