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); console.log("payment status", paymentStatus);
const contractUrl = paymentStatus.contract_url; const talerPayUri = paymentStatus.taler_pay_uri;
if (!contractUrl) { if (!talerPayUri) {
throw Error("no contract URL in payment response"); 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(preparePayResult.proposalId, undefined);
const confirmPayResult = await myWallet.confirmPay(proposalId, undefined);
console.log("confirmPayResult", confirmPayResult); console.log("confirmPayResult", confirmPayResult);

View File

@ -85,7 +85,6 @@ import {
import { import {
Badge, Badge,
BenchmarkResult, BenchmarkResult,
CheckPayResult,
CoinSelectionResult, CoinSelectionResult,
CoinWithDenom, CoinWithDenom,
ConfirmPayResult, ConfirmPayResult,
@ -734,8 +733,15 @@ export class Wallet {
return fu.href(); 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) { if (!uriResult) {
return { return {
@ -745,13 +751,11 @@ export class Wallet {
} }
let proposalId: number; let proposalId: number;
let checkResult: CheckPayResult;
try { try {
proposalId = await this.downloadProposal( proposalId = await this.downloadProposal(
uriResult.downloadUrl, uriResult.downloadUrl,
uriResult.sessionId, uriResult.sessionId,
); );
checkResult = await this.checkPay(proposalId);
} catch (e) { } catch (e) {
return { return {
status: "error", status: "error",
@ -765,50 +769,84 @@ export class Wallet {
console.log("proposal", proposal); console.log("proposal", proposal);
if (uriResult.sessionId) { // First check if we already payed for it.
const existingPayment = await this.q().getIndexed( const purchase = await this.q().get(
Stores.purchases.fulfillmentUrlIndex, Stores.purchases,
proposal.contractTerms.fulfillment_url, proposal.contractTermsHash,
); );
if (existingPayment) {
console.log("existing payment", existingPayment); if (!purchase) {
await this.submitPay( const paymentAmount = Amounts.parseOrThrow(proposal.contractTerms.amount);
existingPayment.contractTermsHash, let wireFeeLimit;
uriResult.sessionId, 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 { return {
status: "paid", status: "insufficient-balance",
contractTerms: existingPayment.contractTerms, contractTerms: proposal.contractTerms,
nextUrl: this.getNextUrl(existingPayment.contractTerms), proposalId: proposal.id!,
}; };
} }
}
if (checkResult.status === "paid") { // Only create speculative signature if we don't already have one for this proposal
const nextUrl = this.getNextUrl(proposal.contractTerms); if (
return { !this.speculativePayData ||
status: "paid", (this.speculativePayData &&
contractTerms: proposal.contractTerms, this.speculativePayData.proposalId !== proposalId)
proposalId: proposal.id!, ) {
nextUrl, const { exchangeUrl, cds, totalAmount } = res;
}; const payCoinInfo = await this.cryptoApi.signDeposit(
} proposal.contractTerms,
if (checkResult.status === "insufficient-balance") { cds,
return { totalAmount,
status: "insufficient-balance", );
contractTerms: proposal.contractTerms, this.speculativePayData = {
proposalId: proposal.id!, exchangeUrl,
}; payCoinInfo,
} proposal,
if (checkResult.status === "payment-possible") { proposalId,
};
Wallet.enableTracing &&
console.log("created speculative pay data for payment");
}
return { return {
status: "payment-possible", status: "payment-possible",
contractTerms: proposal.contractTerms, contractTerms: proposal.contractTerms,
proposalId: proposal.id!, 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; 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) { private async sendReserveInfoToBank(reservePub: string) {
const reserve = await this.q().get<ReserveRecord>( const reserve = await this.q().get<ReserveRecord>(

View File

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

View File

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

View File

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

View File

@ -29,13 +29,10 @@ import {
DenominationRecord, DenominationRecord,
ExchangeRecord, ExchangeRecord,
PreCoinRecord, PreCoinRecord,
ProposalDownloadRecord,
PurchaseRecord,
ReserveRecord, ReserveRecord,
} from "../dbTypes"; } from "../dbTypes";
import { import {
BenchmarkResult, BenchmarkResult,
CheckPayResult,
ConfirmPayResult, ConfirmPayResult,
ReserveCreationInfo, ReserveCreationInfo,
SenderWireInfos, SenderWireInfos,
@ -45,10 +42,6 @@ import {
WalletDiagnostics, WalletDiagnostics,
} from "../walletTypes"; } from "../walletTypes";
import {
MerchantRefundPermission,
} from "../talerTypes";
import { MessageMap, MessageType } from "./messages"; import { MessageMap, MessageType } from "./messages";
@ -217,13 +210,6 @@ export function payback(coinPub: string): Promise<void> {
return callBackend("payback-coin", { coinPub }); 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. * Pay for a proposal.
*/ */

View File

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