aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2020-07-28 15:18:01 +0530
committerFlorian Dold <florian.dold@gmail.com>2020-07-28 15:18:01 +0530
commit43655adff0f8f1db082e1f0c18f1271a29ab8905 (patch)
treeaf106a1e45f85eb12399d8e25ae6f3a413d4fe60 /src
parent86fd5f24406d05fe473c0ffe6c22666fe1b3e343 (diff)
updated preparePay API according to spec
Diffstat (limited to 'src')
-rw-r--r--src/TalerErrorCode.ts23
-rw-r--r--src/headless/taler-wallet-cli.ts32
-rw-r--r--src/operations/pay.ts55
-rw-r--r--src/types/walletTypes.ts26
-rw-r--r--src/webex/pages/pay.tsx8
5 files changed, 91 insertions, 53 deletions
diff --git a/src/TalerErrorCode.ts b/src/TalerErrorCode.ts
index f338f1ad4..56c28ee20 100644
--- a/src/TalerErrorCode.ts
+++ b/src/TalerErrorCode.ts
@@ -550,7 +550,7 @@ export enum TalerErrorCode {
DEPOSIT_INVALID_WIRE_FORMAT_JSON = 1210,
/**
- * The hash of the given wire address does not match the hash specified in the proposal data.
+ * The hash of the given wire address does not match the wire hash specified in the proposal data.
* Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
* (A value of 0 indicates that the error is generated client-side).
*/
@@ -1404,6 +1404,13 @@ export enum TalerErrorCode {
PAY_EXCHANGE_FAILED = 2135,
/**
+ * The merchant backend couldn't verify the order payment because of a database failure.
+ * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR (500).
+ * (A value of 0 indicates that the error is generated client-side).
+ */
+ PAID_DB_ERROR = 2146,
+
+ /**
* The order is not known.
* Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
* (A value of 0 indicates that the error is generated client-side).
@@ -2678,6 +2685,13 @@ export enum TalerErrorCode {
MERCHANT_ORDER_GET_REPLY_MALFORMED = 2922,
/**
+ * The token used to authenticate the client is invalid for this order.
+ * Returned with an HTTP status code of #MHD_HTTP_FORBIDDEN (403).
+ * (A value of 0 indicates that the error is generated client-side).
+ */
+ MERCHANT_GET_ORDER_INVALID_TOKEN = 2923,
+
+ /**
* The signature from the exchange on the deposit confirmation is invalid. Returned with a "400 Bad Request" status code.
* Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
* (A value of 0 indicates that the error is generated client-side).
@@ -3063,6 +3077,13 @@ export enum TalerErrorCode {
WALLET_CORE_API_OPERATION_UNKNOWN = 7007,
/**
+ * The given taler://pay URI is invalid.
+ * Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
+ * (A value of 0 indicates that the error is generated client-side).
+ */
+ WALLET_INVALID_TALER_PAY_URI = 7008,
+
+ /**
* The exchange does not know about the reserve (yet), and thus withdrawal can't progress.
* Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
* (A value of 0 indicates that the error is generated client-side).
diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts
index 4bae298a0..ca0f0f5d1 100644
--- a/src/headless/taler-wallet-cli.ts
+++ b/src/headless/taler-wallet-cli.ts
@@ -43,6 +43,7 @@ import { NodeHttpLib } from "./NodeHttpLib";
import * as nacl from "../crypto/primitives/nacl-fast";
import { addPaytoQueryParams } from "../util/payto";
import { handleCoreApiRequest } from "../walletCoreApiHandler";
+import { PreparePayResultType } from "../types/walletTypes";
const logger = new Logger("taler-wallet-cli.ts");
@@ -58,19 +59,19 @@ async function doPay(
options: { alwaysYes: boolean } = { alwaysYes: true },
): Promise<void> {
const result = await wallet.preparePayForUri(payUrl);
- if (result.status === "error") {
- console.error("Could not pay:", result.error);
- process.exit(1);
- return;
- }
- if (result.status === "insufficient-balance") {
+ if (result.status === PreparePayResultType.InsufficientBalance) {
console.log("contract", result.contractTermsRaw);
console.error("insufficient balance");
process.exit(1);
return;
}
- if (result.status === "paid") {
- console.log("already paid!");
+ if (result.status === PreparePayResultType.AlreadyConfirmed) {
+ if (result.paid) {
+ console.log("already paid!");
+ } else {
+ console.log("payment already in progress");
+ }
+
process.exit(0);
return;
}
@@ -502,16 +503,17 @@ advancedCli
await withWallet(args, async (wallet) => {
const res = await wallet.preparePayForUri(args.payPrepare.url);
switch (res.status) {
- case "error":
- console.log("error:", res.error);
- break;
- case "insufficient-balance":
+ case PreparePayResultType.InsufficientBalance:
console.log("insufficient balance");
break;
- case "paid":
- console.log("already paid");
+ case PreparePayResultType.AlreadyConfirmed:
+ if (res.paid) {
+ console.log("already paid!");
+ } else {
+ console.log("payment in progress");
+ }
break;
- case "payment-possible":
+ case PreparePayResultType.PaymentPossible:
console.log("payment possible");
break;
default:
diff --git a/src/operations/pay.ts b/src/operations/pay.ts
index 58911b4bf..08bbe43a1 100644
--- a/src/operations/pay.ts
+++ b/src/operations/pay.ts
@@ -48,19 +48,19 @@ import {
OperationErrorDetails,
PreparePayResult,
RefreshReason,
+ PreparePayResultType,
} from "../types/walletTypes";
import * as Amounts from "../util/amounts";
import { AmountJson } from "../util/amounts";
import { Logger } from "../util/logging";
import { parsePayUri } from "../util/taleruri";
-import { guardOperationException } from "./errors";
+import { guardOperationException, OperationFailedError } from "./errors";
import { createRefreshGroup, getTotalRefreshCost } from "./refresh";
import { InternalWalletState } from "./state";
import { getTimestampNow, timestampAddDuration } from "../util/time";
import { strcmp, canonicalJson } from "../util/helpers";
-import {
- readSuccessResponseJsonOrThrow,
-} from "../util/http";
+import { readSuccessResponseJsonOrThrow } from "../util/http";
+import { TalerErrorCode } from "../TalerErrorCode";
/**
* Logger.
@@ -783,7 +783,7 @@ export async function submitPay(
coins: purchase.coinDepositPermissions,
session_id: purchase.lastSessionId,
};
-
+
logger.trace("making pay request", JSON.stringify(reqBody, undefined, 2));
const resp = await ws.http.postJson(payUrl, reqBody);
@@ -860,10 +860,13 @@ export async function preparePayForUri(
const uriResult = parsePayUri(talerPayUri);
if (!uriResult) {
- return {
- status: "error",
- error: "URI not supported",
- };
+ throw OperationFailedError.fromCode(
+ TalerErrorCode.WALLET_INVALID_TALER_PAY_URI,
+ `invalid taler://pay URI (${talerPayUri})`,
+ {
+ talerPayUri,
+ }
+ );
}
let proposalId = await startDownloadProposal(
@@ -911,7 +914,7 @@ export async function preparePayForUri(
if (!res) {
console.log("not confirming payment, insufficient coins");
return {
- status: "insufficient-balance",
+ status: PreparePayResultType.InsufficientBalance,
contractTermsRaw: d.contractTermsRaw,
proposalId: proposal.proposalId,
};
@@ -923,14 +926,14 @@ export async function preparePayForUri(
const totalFees = Amounts.sub(costInfo.totalCost, res.paymentAmount).amount;
return {
- status: "payment-possible",
+ status: PreparePayResultType.PaymentPossible,
contractTermsRaw: d.contractTermsRaw,
proposalId: proposal.proposalId,
totalFees,
};
}
- if (uriResult.sessionId && purchase.lastSessionId !== uriResult.sessionId) {
+ if (purchase.lastSessionId !== uriResult.sessionId) {
console.log(
"automatically re-submitting payment with different session ID",
);
@@ -942,14 +945,28 @@ export async function preparePayForUri(
p.lastSessionId = uriResult.sessionId;
await tx.put(Stores.purchases, p);
});
- await submitPay(ws, proposalId);
+ const r = await submitPay(ws, proposalId);
+ return {
+ status: PreparePayResultType.AlreadyConfirmed,
+ contractTermsRaw: purchase.contractTermsRaw,
+ paid: true,
+ nextUrl: r.nextUrl,
+ };
+ } else if (!purchase.timestampFirstSuccessfulPay) {
+ return {
+ status: PreparePayResultType.AlreadyConfirmed,
+ contractTermsRaw: purchase.contractTermsRaw,
+ paid: false,
+ };
+ } else if (purchase.paymentSubmitPending) {
+ return {
+ status: PreparePayResultType.AlreadyConfirmed,
+ contractTermsRaw: purchase.contractTermsRaw,
+ paid: false,
+ };
}
-
- return {
- status: "paid",
- contractTermsRaw: purchase.contractTermsRaw,
- nextUrl: getNextUrl(purchase.contractData),
- };
+ // FIXME: we don't handle aborted payments correctly here.
+ throw Error("BUG: invariant violation (purchase status)");
}
/**
diff --git a/src/types/walletTypes.ts b/src/types/walletTypes.ts
index 2ca95bf48..d68f41564 100644
--- a/src/types/walletTypes.ts
+++ b/src/types/walletTypes.ts
@@ -337,34 +337,36 @@ export interface NextUrlResult {
lastSessionId: string | undefined;
}
+export const enum PreparePayResultType {
+ PaymentPossible = "payment-possible",
+ InsufficientBalance = "insufficient-balance",
+ AlreadyConfirmed = "already-confirmed",
+}
+
export type PreparePayResult =
- | PreparePayResultError
| PreparePayResultInsufficientBalance
- | PreparePayResultPaid
+ | PreparePayResultAlreadyConfirmed
| PreparePayResultPaymentPossible;
export interface PreparePayResultPaymentPossible {
- status: "payment-possible";
+ status: PreparePayResultType.PaymentPossible;
proposalId: string;
contractTermsRaw: string;
totalFees: AmountJson;
}
export interface PreparePayResultInsufficientBalance {
- status: "insufficient-balance";
+ status: PreparePayResultType.InsufficientBalance;
proposalId: string;
contractTermsRaw: any;
}
-export interface PreparePayResultError {
- status: "error";
- error: string;
-}
-
-export interface PreparePayResultPaid {
- status: "paid";
+export interface PreparePayResultAlreadyConfirmed {
+ status: PreparePayResultType.AlreadyConfirmed;
contractTermsRaw: any;
- nextUrl: string;
+ paid: boolean;
+ // Only specified if paid.
+ nextUrl?: string;
}
export interface BankWithdrawDetails {
diff --git a/src/webex/pages/pay.tsx b/src/webex/pages/pay.tsx
index a69b6b766..87f3c83d3 100644
--- a/src/webex/pages/pay.tsx
+++ b/src/webex/pages/pay.tsx
@@ -24,7 +24,7 @@
*/
import * as i18n from "../i18n";
-import { PreparePayResult } from "../../types/walletTypes";
+import { PreparePayResult, PreparePayResultType } from "../../types/walletTypes";
import { renderAmount, ProgressButton } from "../renderHtml";
import * as wxApi from "../wxApi";
@@ -58,15 +58,11 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
insufficientBalance = true;
}
- if (payStatus.status === "error") {
- return <span>Error: {payStatus.error}</span>;
- }
-
if (payStatus.status === "payment-possible") {
totalFees = payStatus.totalFees;
}
- if (payStatus.status === "paid" && numTries === 0) {
+ if (payStatus.status === PreparePayResultType.AlreadyConfirmed && numTries === 0) {
return (
<span>
You have already paid for this article. Click{" "}