updated preparePay API according to spec
This commit is contained in:
parent
86fd5f2440
commit
43655adff0
@ -550,7 +550,7 @@ export enum TalerErrorCode {
|
|||||||
DEPOSIT_INVALID_WIRE_FORMAT_JSON = 1210,
|
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).
|
* Returned with an HTTP status code of #MHD_HTTP_BAD_REQUEST (400).
|
||||||
* (A value of 0 indicates that the error is generated client-side).
|
* (A value of 0 indicates that the error is generated client-side).
|
||||||
*/
|
*/
|
||||||
@ -1403,6 +1403,13 @@ export enum TalerErrorCode {
|
|||||||
*/
|
*/
|
||||||
PAY_EXCHANGE_FAILED = 2135,
|
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.
|
* The order is not known.
|
||||||
* Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
|
* 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,
|
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.
|
* 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).
|
* Returned with an HTTP status code of #MHD_HTTP_UNINITIALIZED (0).
|
||||||
@ -3062,6 +3076,13 @@ export enum TalerErrorCode {
|
|||||||
*/
|
*/
|
||||||
WALLET_CORE_API_OPERATION_UNKNOWN = 7007,
|
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.
|
* 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).
|
* Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
|
||||||
|
@ -43,6 +43,7 @@ import { NodeHttpLib } from "./NodeHttpLib";
|
|||||||
import * as nacl from "../crypto/primitives/nacl-fast";
|
import * as nacl from "../crypto/primitives/nacl-fast";
|
||||||
import { addPaytoQueryParams } from "../util/payto";
|
import { addPaytoQueryParams } from "../util/payto";
|
||||||
import { handleCoreApiRequest } from "../walletCoreApiHandler";
|
import { handleCoreApiRequest } from "../walletCoreApiHandler";
|
||||||
|
import { PreparePayResultType } from "../types/walletTypes";
|
||||||
|
|
||||||
const logger = new Logger("taler-wallet-cli.ts");
|
const logger = new Logger("taler-wallet-cli.ts");
|
||||||
|
|
||||||
@ -58,19 +59,19 @@ async function doPay(
|
|||||||
options: { alwaysYes: boolean } = { alwaysYes: true },
|
options: { alwaysYes: boolean } = { alwaysYes: true },
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const result = await wallet.preparePayForUri(payUrl);
|
const result = await wallet.preparePayForUri(payUrl);
|
||||||
if (result.status === "error") {
|
if (result.status === PreparePayResultType.InsufficientBalance) {
|
||||||
console.error("Could not pay:", result.error);
|
|
||||||
process.exit(1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (result.status === "insufficient-balance") {
|
|
||||||
console.log("contract", result.contractTermsRaw);
|
console.log("contract", result.contractTermsRaw);
|
||||||
console.error("insufficient balance");
|
console.error("insufficient balance");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (result.status === "paid") {
|
if (result.status === PreparePayResultType.AlreadyConfirmed) {
|
||||||
|
if (result.paid) {
|
||||||
console.log("already paid!");
|
console.log("already paid!");
|
||||||
|
} else {
|
||||||
|
console.log("payment already in progress");
|
||||||
|
}
|
||||||
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -502,16 +503,17 @@ advancedCli
|
|||||||
await withWallet(args, async (wallet) => {
|
await withWallet(args, async (wallet) => {
|
||||||
const res = await wallet.preparePayForUri(args.payPrepare.url);
|
const res = await wallet.preparePayForUri(args.payPrepare.url);
|
||||||
switch (res.status) {
|
switch (res.status) {
|
||||||
case "error":
|
case PreparePayResultType.InsufficientBalance:
|
||||||
console.log("error:", res.error);
|
|
||||||
break;
|
|
||||||
case "insufficient-balance":
|
|
||||||
console.log("insufficient balance");
|
console.log("insufficient balance");
|
||||||
break;
|
break;
|
||||||
case "paid":
|
case PreparePayResultType.AlreadyConfirmed:
|
||||||
console.log("already paid");
|
if (res.paid) {
|
||||||
|
console.log("already paid!");
|
||||||
|
} else {
|
||||||
|
console.log("payment in progress");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "payment-possible":
|
case PreparePayResultType.PaymentPossible:
|
||||||
console.log("payment possible");
|
console.log("payment possible");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -48,19 +48,19 @@ import {
|
|||||||
OperationErrorDetails,
|
OperationErrorDetails,
|
||||||
PreparePayResult,
|
PreparePayResult,
|
||||||
RefreshReason,
|
RefreshReason,
|
||||||
|
PreparePayResultType,
|
||||||
} from "../types/walletTypes";
|
} from "../types/walletTypes";
|
||||||
import * as Amounts from "../util/amounts";
|
import * as Amounts from "../util/amounts";
|
||||||
import { AmountJson } from "../util/amounts";
|
import { AmountJson } from "../util/amounts";
|
||||||
import { Logger } from "../util/logging";
|
import { Logger } from "../util/logging";
|
||||||
import { parsePayUri } from "../util/taleruri";
|
import { parsePayUri } from "../util/taleruri";
|
||||||
import { guardOperationException } from "./errors";
|
import { guardOperationException, OperationFailedError } from "./errors";
|
||||||
import { createRefreshGroup, getTotalRefreshCost } from "./refresh";
|
import { createRefreshGroup, getTotalRefreshCost } from "./refresh";
|
||||||
import { InternalWalletState } from "./state";
|
import { InternalWalletState } from "./state";
|
||||||
import { getTimestampNow, timestampAddDuration } from "../util/time";
|
import { getTimestampNow, timestampAddDuration } from "../util/time";
|
||||||
import { strcmp, canonicalJson } from "../util/helpers";
|
import { strcmp, canonicalJson } from "../util/helpers";
|
||||||
import {
|
import { readSuccessResponseJsonOrThrow } from "../util/http";
|
||||||
readSuccessResponseJsonOrThrow,
|
import { TalerErrorCode } from "../TalerErrorCode";
|
||||||
} from "../util/http";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logger.
|
* Logger.
|
||||||
@ -860,10 +860,13 @@ export async function preparePayForUri(
|
|||||||
const uriResult = parsePayUri(talerPayUri);
|
const uriResult = parsePayUri(talerPayUri);
|
||||||
|
|
||||||
if (!uriResult) {
|
if (!uriResult) {
|
||||||
return {
|
throw OperationFailedError.fromCode(
|
||||||
status: "error",
|
TalerErrorCode.WALLET_INVALID_TALER_PAY_URI,
|
||||||
error: "URI not supported",
|
`invalid taler://pay URI (${talerPayUri})`,
|
||||||
};
|
{
|
||||||
|
talerPayUri,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let proposalId = await startDownloadProposal(
|
let proposalId = await startDownloadProposal(
|
||||||
@ -911,7 +914,7 @@ export async function preparePayForUri(
|
|||||||
if (!res) {
|
if (!res) {
|
||||||
console.log("not confirming payment, insufficient coins");
|
console.log("not confirming payment, insufficient coins");
|
||||||
return {
|
return {
|
||||||
status: "insufficient-balance",
|
status: PreparePayResultType.InsufficientBalance,
|
||||||
contractTermsRaw: d.contractTermsRaw,
|
contractTermsRaw: d.contractTermsRaw,
|
||||||
proposalId: proposal.proposalId,
|
proposalId: proposal.proposalId,
|
||||||
};
|
};
|
||||||
@ -923,14 +926,14 @@ export async function preparePayForUri(
|
|||||||
const totalFees = Amounts.sub(costInfo.totalCost, res.paymentAmount).amount;
|
const totalFees = Amounts.sub(costInfo.totalCost, res.paymentAmount).amount;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: "payment-possible",
|
status: PreparePayResultType.PaymentPossible,
|
||||||
contractTermsRaw: d.contractTermsRaw,
|
contractTermsRaw: d.contractTermsRaw,
|
||||||
proposalId: proposal.proposalId,
|
proposalId: proposal.proposalId,
|
||||||
totalFees,
|
totalFees,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uriResult.sessionId && purchase.lastSessionId !== uriResult.sessionId) {
|
if (purchase.lastSessionId !== uriResult.sessionId) {
|
||||||
console.log(
|
console.log(
|
||||||
"automatically re-submitting payment with different session ID",
|
"automatically re-submitting payment with different session ID",
|
||||||
);
|
);
|
||||||
@ -942,14 +945,28 @@ export async function preparePayForUri(
|
|||||||
p.lastSessionId = uriResult.sessionId;
|
p.lastSessionId = uriResult.sessionId;
|
||||||
await tx.put(Stores.purchases, p);
|
await tx.put(Stores.purchases, p);
|
||||||
});
|
});
|
||||||
await submitPay(ws, proposalId);
|
const r = await submitPay(ws, proposalId);
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
status: "paid",
|
status: PreparePayResultType.AlreadyConfirmed,
|
||||||
contractTermsRaw: purchase.contractTermsRaw,
|
contractTermsRaw: purchase.contractTermsRaw,
|
||||||
nextUrl: getNextUrl(purchase.contractData),
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// FIXME: we don't handle aborted payments correctly here.
|
||||||
|
throw Error("BUG: invariant violation (purchase status)");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -337,34 +337,36 @@ export interface NextUrlResult {
|
|||||||
lastSessionId: string | undefined;
|
lastSessionId: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum PreparePayResultType {
|
||||||
|
PaymentPossible = "payment-possible",
|
||||||
|
InsufficientBalance = "insufficient-balance",
|
||||||
|
AlreadyConfirmed = "already-confirmed",
|
||||||
|
}
|
||||||
|
|
||||||
export type PreparePayResult =
|
export type PreparePayResult =
|
||||||
| PreparePayResultError
|
|
||||||
| PreparePayResultInsufficientBalance
|
| PreparePayResultInsufficientBalance
|
||||||
| PreparePayResultPaid
|
| PreparePayResultAlreadyConfirmed
|
||||||
| PreparePayResultPaymentPossible;
|
| PreparePayResultPaymentPossible;
|
||||||
|
|
||||||
export interface PreparePayResultPaymentPossible {
|
export interface PreparePayResultPaymentPossible {
|
||||||
status: "payment-possible";
|
status: PreparePayResultType.PaymentPossible;
|
||||||
proposalId: string;
|
proposalId: string;
|
||||||
contractTermsRaw: string;
|
contractTermsRaw: string;
|
||||||
totalFees: AmountJson;
|
totalFees: AmountJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PreparePayResultInsufficientBalance {
|
export interface PreparePayResultInsufficientBalance {
|
||||||
status: "insufficient-balance";
|
status: PreparePayResultType.InsufficientBalance;
|
||||||
proposalId: string;
|
proposalId: string;
|
||||||
contractTermsRaw: any;
|
contractTermsRaw: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PreparePayResultError {
|
export interface PreparePayResultAlreadyConfirmed {
|
||||||
status: "error";
|
status: PreparePayResultType.AlreadyConfirmed;
|
||||||
error: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PreparePayResultPaid {
|
|
||||||
status: "paid";
|
|
||||||
contractTermsRaw: any;
|
contractTermsRaw: any;
|
||||||
nextUrl: string;
|
paid: boolean;
|
||||||
|
// Only specified if paid.
|
||||||
|
nextUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BankWithdrawDetails {
|
export interface BankWithdrawDetails {
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
import * as i18n from "../i18n";
|
import * as i18n from "../i18n";
|
||||||
|
|
||||||
import { PreparePayResult } from "../../types/walletTypes";
|
import { PreparePayResult, PreparePayResultType } from "../../types/walletTypes";
|
||||||
|
|
||||||
import { renderAmount, ProgressButton } from "../renderHtml";
|
import { renderAmount, ProgressButton } from "../renderHtml";
|
||||||
import * as wxApi from "../wxApi";
|
import * as wxApi from "../wxApi";
|
||||||
@ -58,15 +58,11 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }): JSX.Element {
|
|||||||
insufficientBalance = true;
|
insufficientBalance = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payStatus.status === "error") {
|
|
||||||
return <span>Error: {payStatus.error}</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payStatus.status === "payment-possible") {
|
if (payStatus.status === "payment-possible") {
|
||||||
totalFees = payStatus.totalFees;
|
totalFees = payStatus.totalFees;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payStatus.status === "paid" && numTries === 0) {
|
if (payStatus.status === PreparePayResultType.AlreadyConfirmed && numTries === 0) {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
You have already paid for this article. Click{" "}
|
You have already paid for this article. Click{" "}
|
||||||
|
Loading…
Reference in New Issue
Block a user