fold checkPay into preparePay

This commit is contained in:
Florian Dold 2019-09-06 09:48:00 +02:00
parent 51aa6d8146
commit 9297bbc825
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
7 changed files with 94 additions and 169 deletions

View File

@ -58,20 +58,20 @@ export async function runIntegrationTest(args: {
console.log("payment status", paymentStatus);
const contractUrl = paymentStatus.contract_url;
if (!contractUrl) {
throw Error("no contract URL in payment response");
const talerPayUri = paymentStatus.taler_pay_uri;
if (!talerPayUri) {
throw Error("no taler://pay/ URI in payment response");
}
const proposalId = await myWallet.downloadProposal(contractUrl);
const preparePayResult = await myWallet.preparePay(talerPayUri);
console.log("proposal id", proposalId);
console.log("prepare pay result", preparePayResult);
const checkPayResult = await myWallet.checkPay(proposalId);
if (preparePayResult.status != "payment-possible") {
throw Error("payment not possible");
}
console.log("check pay result", checkPayResult);
const confirmPayResult = await myWallet.confirmPay(proposalId, undefined);
const confirmPayResult = await myWallet.confirmPay(preparePayResult.proposalId, undefined);
console.log("confirmPayResult", confirmPayResult);

View File

@ -85,7 +85,6 @@ import {
import {
Badge,
BenchmarkResult,
CheckPayResult,
CoinSelectionResult,
CoinWithDenom,
ConfirmPayResult,
@ -734,8 +733,15 @@ export class Wallet {
return fu.href();
}
async preparePay(url: string): Promise<PreparePayResult> {
const uriResult = parsePayUri(url);
/**
* Check if a payment for the given taler://pay/ URI is possible.
*
* If the payment is possible, the signature are already generated but not
* yet send to the merchant.
*/
async preparePay(talerPayUri: string): Promise<PreparePayResult> {
const uriResult = parsePayUri(talerPayUri);
if (!uriResult) {
return {
@ -745,13 +751,11 @@ export class Wallet {
}
let proposalId: number;
let checkResult: CheckPayResult;
try {
proposalId = await this.downloadProposal(
uriResult.downloadUrl,
uriResult.sessionId,
);
checkResult = await this.checkPay(proposalId);
} catch (e) {
return {
status: "error",
@ -765,50 +769,84 @@ export class Wallet {
console.log("proposal", proposal);
if (uriResult.sessionId) {
const existingPayment = await this.q().getIndexed(
Stores.purchases.fulfillmentUrlIndex,
proposal.contractTerms.fulfillment_url,
// First check if we already payed for it.
const purchase = await this.q().get(
Stores.purchases,
proposal.contractTermsHash,
);
if (existingPayment) {
console.log("existing payment", existingPayment);
await this.submitPay(
existingPayment.contractTermsHash,
uriResult.sessionId,
);
return {
status: "paid",
contractTerms: existingPayment.contractTerms,
nextUrl: this.getNextUrl(existingPayment.contractTerms),
};
}
}
if (checkResult.status === "paid") {
const nextUrl = this.getNextUrl(proposal.contractTerms);
return {
status: "paid",
contractTerms: proposal.contractTerms,
proposalId: proposal.id!,
nextUrl,
};
if (!purchase) {
const paymentAmount = Amounts.parseOrThrow(proposal.contractTerms.amount);
let wireFeeLimit;
if (proposal.contractTerms.max_wire_fee) {
wireFeeLimit = Amounts.parseOrThrow(proposal.contractTerms.max_wire_fee);
} else {
wireFeeLimit = Amounts.getZero(paymentAmount.currency);
}
if (checkResult.status === "insufficient-balance") {
// If not already payed, check if we could pay for it.
const res = await this.getCoinsForPayment({
allowedAuditors: proposal.contractTerms.auditors,
allowedExchanges: proposal.contractTerms.exchanges,
depositFeeLimit: Amounts.parseOrThrow(proposal.contractTerms.max_fee),
paymentAmount,
wireFeeAmortization: proposal.contractTerms.wire_fee_amortization || 1,
wireFeeLimit,
wireFeeTime: getTalerStampSec(proposal.contractTerms.timestamp) || 0,
wireMethod: proposal.contractTerms.wire_method,
});
if (!res) {
console.log("not confirming payment, insufficient coins");
return {
status: "insufficient-balance",
contractTerms: proposal.contractTerms,
proposalId: proposal.id!,
};
}
if (checkResult.status === "payment-possible") {
// Only create speculative signature if we don't already have one for this proposal
if (
!this.speculativePayData ||
(this.speculativePayData &&
this.speculativePayData.proposalId !== proposalId)
) {
const { exchangeUrl, cds, totalAmount } = res;
const payCoinInfo = await this.cryptoApi.signDeposit(
proposal.contractTerms,
cds,
totalAmount,
);
this.speculativePayData = {
exchangeUrl,
payCoinInfo,
proposal,
proposalId,
};
Wallet.enableTracing &&
console.log("created speculative pay data for payment");
}
return {
status: "payment-possible",
contractTerms: proposal.contractTerms,
proposalId: proposal.id!,
totalFees: checkResult.coinSelection!.totalFees,
totalFees: res.totalFees,
};
}
throw Error("not reached");
if (uriResult.sessionId) {
await this.submitPay(
purchase.contractTermsHash,
uriResult.sessionId,
);
}
return {
status: "paid",
contractTerms: proposal.contractTerms,
proposalId: proposal.id!,
nextUrl: this.getNextUrl(purchase.contractTerms),
};
}
/**
@ -1088,85 +1126,6 @@ export class Wallet {
return sp;
}
/**
* Check if payment for an offer is possible, or if the offer has already
* been payed for.
*
* Also speculatively computes the signature for the payment to make the payment
* look faster to the user.
*/
async checkPay(proposalId: number): Promise<CheckPayResult> {
const proposal = await this.q().get(Stores.proposals, proposalId);
if (!proposal) {
throw Error(`proposal with id ${proposalId} not found`);
}
// First check if we already payed for it.
const purchase = await this.q().get(
Stores.purchases,
proposal.contractTermsHash,
);
if (purchase) {
Wallet.enableTracing && console.log("got purchase", purchase);
return { status: "paid" };
}
const paymentAmount = Amounts.parseOrThrow(proposal.contractTerms.amount);
Wallet.enableTracing &&
console.log(
`checking if payment of ${JSON.stringify(paymentAmount)} is possible`,
);
let wireFeeLimit;
if (proposal.contractTerms.max_wire_fee) {
wireFeeLimit = Amounts.parseOrThrow(proposal.contractTerms.max_wire_fee);
} else {
wireFeeLimit = Amounts.getZero(paymentAmount.currency);
}
// If not already payed, check if we could pay for it.
const res = await this.getCoinsForPayment({
allowedAuditors: proposal.contractTerms.auditors,
allowedExchanges: proposal.contractTerms.exchanges,
depositFeeLimit: Amounts.parseOrThrow(proposal.contractTerms.max_fee),
paymentAmount,
wireFeeAmortization: proposal.contractTerms.wire_fee_amortization || 1,
wireFeeLimit,
wireFeeTime: getTalerStampSec(proposal.contractTerms.timestamp) || 0,
wireMethod: proposal.contractTerms.wire_method,
});
if (!res) {
console.log("not confirming payment, insufficient coins");
return { status: "insufficient-balance" };
}
// Only create speculative signature if we don't already have one for this proposal
if (
!this.speculativePayData ||
(this.speculativePayData &&
this.speculativePayData.proposalId !== proposalId)
) {
const { exchangeUrl, cds, totalAmount } = res;
const payCoinInfo = await this.cryptoApi.signDeposit(
proposal.contractTerms,
cds,
totalAmount,
);
this.speculativePayData = {
exchangeUrl,
payCoinInfo,
proposal,
proposalId,
};
Wallet.enableTracing &&
console.log("created speculative pay data for payment");
}
return { status: "payment-possible", coinSelection: res };
}
private async sendReserveInfoToBank(reservePub: string) {
const reserve = await this.q().get<ReserveRecord>(

View File

@ -220,14 +220,6 @@ export function mkAmount(
return { value, fraction, currency };
}
/**
* Possible results for checkPay.
*/
export interface CheckPayResult {
status: "paid" | "payment-possible" | "insufficient-balance";
coinSelection?: CoinSelectionResult;
}
/**
* Result for confirmPay
*/
@ -463,16 +455,15 @@ export type PreparePayResult =
export interface PreparePayResultPaymentPossible {
status: "payment-possible";
proposalId?: number;
contractTerms?: ContractTerms;
totalFees?: AmountJson;
proposalId: number;
contractTerms: ContractTerms;
totalFees: AmountJson;
}
export interface PreparePayResultInsufficientBalance {
status: "insufficient-balance";
proposalId?: number;
contractTerms?: ContractTerms;
totalFees?: AmountJson;
proposalId: number;
contractTerms: ContractTerms;
}
export interface PreparePayResultError {
@ -482,8 +473,8 @@ export interface PreparePayResultError {
export interface PreparePayResultPaid {
status: "paid";
proposalId?: number;
contractTerms?: ContractTerms;
proposalId: number;
contractTerms: ContractTerms;
nextUrl: string;
}

View File

@ -69,10 +69,6 @@ export interface MessageMap {
request: { proposalId: number; sessionId?: string };
response: walletTypes.ConfirmPayResult;
};
"check-pay": {
request: { proposalId: number };
response: walletTypes.CheckPayResult;
};
"exchange-info": {
request: { baseUrl: string };
response: dbTypes.ExchangeRecord;
@ -205,7 +201,6 @@ export interface MessageMap {
request: { talerPayUri: string };
response: walletTypes.PreparePayResult;
};
"get-diagnostics": {
request: { };
response: walletTypes.WalletDiagnostics;

View File

@ -24,7 +24,7 @@
*/
import * as i18n from "../../i18n";
import { CheckPayResult, PreparePayResult } from "../../walletTypes";
import { PreparePayResult } from "../../walletTypes";
import { renderAmount, ProgressButton, registerMountPage } from "../renderHtml";
import * as wxApi from "../wxApi";

View File

@ -29,13 +29,10 @@ import {
DenominationRecord,
ExchangeRecord,
PreCoinRecord,
ProposalDownloadRecord,
PurchaseRecord,
ReserveRecord,
} from "../dbTypes";
import {
BenchmarkResult,
CheckPayResult,
ConfirmPayResult,
ReserveCreationInfo,
SenderWireInfos,
@ -45,10 +42,6 @@ import {
WalletDiagnostics,
} from "../walletTypes";
import {
MerchantRefundPermission,
} from "../talerTypes";
import { MessageMap, MessageType } from "./messages";
@ -217,13 +210,6 @@ export function payback(coinPub: string): Promise<void> {
return callBackend("payback-coin", { coinPub });
}
/**
* Check if payment is possible or already done.
*/
export function checkPay(proposalId: number): Promise<CheckPayResult> {
return callBackend("check-pay", { proposalId });
}
/**
* Pay for a proposal.
*/

View File

@ -132,12 +132,6 @@ async function handleMessage(
detail.sessionId,
);
}
case "check-pay": {
if (typeof detail.proposalId !== "number") {
throw Error("proposalId must be number");
}
return needsWallet().checkPay(detail.proposalId);
}
case "exchange-info": {
if (!detail.baseUrl) {
return Promise.resolve({ error: "bad url" });