updated preparePay API according to spec

This commit is contained in:
Florian Dold 2020-07-28 15:18:01 +05:30
parent 86fd5f2440
commit 43655adff0
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
5 changed files with 91 additions and 53 deletions

View File

@ -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).
*/
@ -1403,6 +1403,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).
@ -2677,6 +2684,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).
@ -3062,6 +3076,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).

View File

@ -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:

View File

@ -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)");
}
/**

View File

@ -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 {

View File

@ -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{" "}