gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated (5a8931d9 -> f4a8702b)


From: gnunet
Subject: [taler-wallet-core] branch master updated (5a8931d9 -> f4a8702b)
Date: Tue, 21 Jul 2020 08:53:54 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a change to branch master
in repository wallet-core.

    from 5a8931d9 add new fields to signatures
     new dd2efc3d nicer HTTP helper in preparation for better error handling
     new f4a8702b towards consuming new merchant API

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 integrationtests/common.sh                 |  24 +++++-
 src/crypto/workers/cryptoImplementation.ts |   4 +-
 src/headless/integrationtest.ts            |   7 +-
 src/headless/merchant.ts                   |  11 ++-
 src/headless/taler-wallet-cli.ts           |   3 +-
 src/operations/balance.ts                  |   2 +-
 src/operations/errors.ts                   |  60 ---------------
 src/operations/history.ts                  |   6 +-
 src/operations/pay.ts                      |  83 ++++++++++-----------
 src/operations/recoup.ts                   |  26 ++++---
 src/operations/withdraw.ts                 |  12 ++-
 src/types/dbTypes.ts                       |   5 +-
 src/types/notifications.ts                 |   1 +
 src/types/talerTypes.ts                    |  36 +++------
 src/types/walletTypes.ts                   |   2 +-
 src/util/http.ts                           | 115 +++++++++++++++++++++++++++++
 src/util/taleruri.ts                       |  11 +--
 17 files changed, 230 insertions(+), 178 deletions(-)

diff --git a/integrationtests/common.sh b/integrationtests/common.sh
index d228d1ea..d3f34767 100644
--- a/integrationtests/common.sh
+++ b/integrationtests/common.sh
@@ -1,8 +1,8 @@
 #!/bin/bash
 
-function setup_config() {
-    set -eu
+set -eu
 
+function setup_config() {
     echo -n "Testing for taler-bank-manage"
     taler-bank-manage -h >/dev/null </dev/null || exit_skip " MISSING"
     echo " FOUND"
@@ -135,11 +135,31 @@ function wait_for_services() {
     echo " DONE"
 }
 
+# Configure merchant instances
+function configure_merchant() {
+  json='
+    {
+      "id": "default",
+      "name": "GNU Taler Merchant",
+      "payto_uris": ["payto://x-taler-bank/test_merchant"],
+      "address": {},
+      "jurisdiction": {},
+      "default_max_wire_fee": "TESTKUDOS:1",
+      "default_wire_fee_amortization": 3,
+      "default_max_deposit_fee": "TESTKUDOS:1",
+      "default_wire_transfer_delay": {"d_ms": "forever"},
+      "default_pay_delay": {"d_ms": "forever"}
+    }
+  '
+  curl -v -XPOST --data "$json" "${MERCHANT_URL}private/instances"
+}
+
 function normal_start_and_wait() {
     setup_config "$1"
     setup_services
     launch_services
     wait_for_services
+    configure_merchant
 }
 
 # provide the service URL as first parameter
diff --git a/src/crypto/workers/cryptoImplementation.ts 
b/src/crypto/workers/cryptoImplementation.ts
index 43def7b6..979fe41d 100644
--- a/src/crypto/workers/cryptoImplementation.ts
+++ b/src/crypto/workers/cryptoImplementation.ts
@@ -336,7 +336,7 @@ export class CryptoImplementation {
     const d = buildSigPS(SignaturePurpose.WALLET_COIN_DEPOSIT)
       .put(decodeCrock(depositInfo.contractTermsHash))
       .put(decodeCrock(depositInfo.wireInfoHash))
-      .put(hash(decodeCrock(depositInfo.denomPub)))
+      .put(decodeCrock(depositInfo.denomPubHash))
       .put(timestampRoundedToBuffer(depositInfo.timestamp))
       .put(timestampRoundedToBuffer(depositInfo.refundDeadline))
       .put(amountToBuffer(depositInfo.spendAmount))
@@ -350,7 +350,7 @@ export class CryptoImplementation {
       coin_pub: depositInfo.coinPub,
       coin_sig: encodeCrock(coinSig),
       contribution: Amounts.stringify(depositInfo.spendAmount),
-      denom_pub: depositInfo.denomPub,
+      h_denom: depositInfo.denomPubHash,
       exchange_url: depositInfo.exchangeBaseUrl,
       ub_sig: depositInfo.denomSig,
     };
diff --git a/src/headless/integrationtest.ts b/src/headless/integrationtest.ts
index db96d57c..786907a0 100644
--- a/src/headless/integrationtest.ts
+++ b/src/headless/integrationtest.ts
@@ -25,6 +25,7 @@ import { NodeHttpLib } from "./NodeHttpLib";
 import { Wallet } from "../wallet";
 import { Configuration } from "../util/talerconfig";
 import { Amounts, AmountJson } from "../util/amounts";
+import { OperationFailedAndReportedError, OperationFailedError } from 
"../operations/errors";
 
 const logger = new Logger("integrationtest.ts");
 
@@ -69,9 +70,9 @@ async function makePayment(
   }
 
   const confirmPayResult = await wallet.confirmPay(
-    preparePayResult.proposalId,
-    undefined,
-  );
+      preparePayResult.proposalId,
+      undefined,
+    );
 
   console.log("confirmPayResult", confirmPayResult);
 
diff --git a/src/headless/merchant.ts b/src/headless/merchant.ts
index b479c5a8..3324924f 100644
--- a/src/headless/merchant.ts
+++ b/src/headless/merchant.ts
@@ -37,9 +37,8 @@ export class MerchantBackendConnection {
     reason: string,
     refundAmount: string,
   ): Promise<string> {
-    const reqUrl = new URL("refund", this.merchantBaseUrl);
+    const reqUrl = new URL(`private/orders/${orderId}/refund`, 
this.merchantBaseUrl);
     const refundReq = {
-      order_id: orderId,
       reason,
       refund: refundAmount,
     };
@@ -66,10 +65,11 @@ export class MerchantBackendConnection {
   constructor(public merchantBaseUrl: string, public apiKey: string) {}
 
   async authorizeTip(amount: string, justification: string): Promise<string> {
-    const reqUrl = new URL("tip-authorize", this.merchantBaseUrl).href;
+    const reqUrl = new URL("private/tips", this.merchantBaseUrl).href;
     const tipReq = {
       amount,
       justification,
+      next_url: "about:blank",
     };
     const resp = await axios({
       method: "post",
@@ -93,7 +93,7 @@ export class MerchantBackendConnection {
     fulfillmentUrl: string,
   ): Promise<{ orderId: string }> {
     const t = Math.floor(new Date().getTime() / 1000) + 15 * 60;
-    const reqUrl = new URL("order", this.merchantBaseUrl).href;
+    const reqUrl = new URL("private/orders", this.merchantBaseUrl).href;
     const orderReq = {
       order: {
         amount,
@@ -123,11 +123,10 @@ export class MerchantBackendConnection {
   }
 
   async checkPayment(orderId: string): Promise<CheckPaymentResponse> {
-    const reqUrl = new URL("check-payment", this.merchantBaseUrl).href;
+    const reqUrl = new URL(`private/orders/${orderId}`, 
this.merchantBaseUrl).href;
     const resp = await axios({
       method: "get",
       url: reqUrl,
-      params: { order_id: orderId },
       responseType: "json",
       headers: {
         Authorization: `ApiKey ${this.apiKey}`,
diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts
index 3f116670..fc13d77f 100644
--- a/src/headless/taler-wallet-cli.ts
+++ b/src/headless/taler-wallet-cli.ts
@@ -30,7 +30,7 @@ import {
   setupRefreshPlanchet,
   encodeCrock,
 } from "../crypto/talerCrypto";
-import { OperationFailedAndReportedError } from "../operations/errors";
+import { OperationFailedAndReportedError, OperationFailedError } from 
"../operations/errors";
 import { Bank } from "./bank";
 import { classifyTalerUri, TalerUriType } from "../util/taleruri";
 import { Configuration } from "../util/talerconfig";
@@ -97,7 +97,6 @@ async function doPay(
 
   if (pay) {
     await wallet.confirmPay(result.proposalId, undefined);
-    console.log("paid!");
   } else {
     console.log("not paying");
   }
diff --git a/src/operations/balance.ts b/src/operations/balance.ts
index 36971c39..81b4da6e 100644
--- a/src/operations/balance.ts
+++ b/src/operations/balance.ts
@@ -130,7 +130,7 @@ export async function getBalancesInsideTransaction(
     if (t.timestampFirstSuccessfulPay) {
       return;
     }
-    for (const c of t.payReq.coins) {
+    for (const c of t.coinDepositPermissions) {
       addTo(
         balanceStore,
         "pendingPayment",
diff --git a/src/operations/errors.ts b/src/operations/errors.ts
index c8885c9e..01a8283c 100644
--- a/src/operations/errors.ts
+++ b/src/operations/errors.ts
@@ -24,8 +24,6 @@
  * Imports.
  */
 import { OperationError } from "../types/walletTypes";
-import { HttpResponse } from "../util/http";
-import { Codec } from "../util/codec";
 
 /**
  * This exception is there to let the caller know that an error happened,
@@ -53,64 +51,6 @@ export class OperationFailedError extends Error {
   }
 }
 
-/**
- * Process an HTTP response that we expect to contain Taler-specific JSON.
- *
- * Depending on the status code, we throw an exception.  This function
- * will try to extract Taler-specific error information from the HTTP response
- * if possible.
- */
-export async function scrutinizeTalerJsonResponse<T>(
-  resp: HttpResponse,
-  codec: Codec<T>,
-): Promise<T> {
-  // FIXME: We should distinguish between different types of error status
-  // to react differently (throttle, report permanent failure)
-
-  // FIXME: Make sure that when we receive an error message,
-  // it looks like a Taler error message
-
-  if (resp.status !== 200) {
-    let exc: OperationFailedError | undefined = undefined;
-    try {
-      const errorJson = await resp.json();
-      const m = `received error response (status ${resp.status})`;
-      exc = new OperationFailedError({
-        type: "protocol",
-        message: m,
-        details: {
-          httpStatusCode: resp.status,
-          errorResponse: errorJson,
-        },
-      });
-    } catch (e) {
-      const m = "could not parse response JSON";
-      exc = new OperationFailedError({
-        type: "network",
-        message: m,
-        details: {
-          status: resp.status,
-        },
-      });
-    }
-    throw exc;
-  }
-  let json: any;
-  try {
-    json = await resp.json();
-  } catch (e) {
-    const m = "could not parse response JSON";
-    throw new OperationFailedError({
-      type: "network",
-      message: m,
-      details: {
-        status: resp.status,
-      },
-    });
-  }
-  return codec.decode(json);
-}
-
 /**
  * Run an operation and call the onOpError callback
  * when there was an exception or operation error that must be reported.
diff --git a/src/operations/history.ts b/src/operations/history.ts
index f04dad11..9cbbd516 100644
--- a/src/operations/history.ts
+++ b/src/operations/history.ts
@@ -246,7 +246,7 @@ export async function getHistory(
             contribution: string;
             denomPub: string;
           }[] = [];
-          for (const x of purchase.payReq.coins) {
+          for (const x of purchase.coinDepositPermissions) {
             const c = await tx.get(Stores.coins, x.coin_pub);
             if (!c) {
               // FIXME: what to do here??
@@ -269,7 +269,7 @@ export async function getHistory(
           verboseDetails = { coins };
         }
         const amountPaidWithFees = Amounts.sum(
-          purchase.payReq.coins.map((x) =>
+          purchase.coinDepositPermissions.map((x) =>
             Amounts.parseOrThrow(x.contribution),
           ),
         ).amount;
@@ -280,7 +280,7 @@ export async function getHistory(
           replay: pe.isReplay,
           sessionId: pe.sessionId,
           timestamp: pe.timestamp,
-          numCoins: purchase.payReq.coins.length,
+          numCoins: purchase.coinDepositPermissions.length,
           amountPaidWithFees: Amounts.stringify(amountPaidWithFees),
           verboseDetails,
         });
diff --git a/src/operations/pay.ts b/src/operations/pay.ts
index a1619074..74bfcc70 100644
--- a/src/operations/pay.ts
+++ b/src/operations/pay.ts
@@ -42,6 +42,7 @@ import {
   codecForProposal,
   codecForContractTerms,
   CoinDepositPermission,
+  codecForMerchantPayResponse,
 } from "../types/talerTypes";
 import {
   ConfirmPayResult,
@@ -52,12 +53,13 @@ import {
 import * as Amounts from "../util/amounts";
 import { AmountJson } from "../util/amounts";
 import { Logger } from "../util/logging";
-import { getOrderDownloadUrl, parsePayUri } from "../util/taleruri";
+import { parsePayUri } from "../util/taleruri";
 import { guardOperationException } from "./errors";
 import { createRefreshGroup, getTotalRefreshCost } from "./refresh";
 import { InternalWalletState } from "./state";
 import { getTimestampNow, timestampAddDuration } from "../util/time";
 import { strcmp, canonicalJson } from "../util/helpers";
+import { httpPostTalerJson } from "../util/http";
 
 /**
  * Logger.
@@ -430,12 +432,6 @@ async function recordConfirmPay(
     sessionId = proposal.downloadSessionId;
   }
   logger.trace(`recording payment with session ID ${sessionId}`);
-  const payReq: PayReq = {
-    coins: coinDepositPermissions,
-    merchant_pub: d.contractData.merchantPub,
-    mode: "pay",
-    order_id: d.contractData.orderId,
-  };
   const payCostInfo = await getTotalPaymentCost(ws, coinSelection);
   const t: PurchaseRecord = {
     abortDone: false,
@@ -444,8 +440,8 @@ async function recordConfirmPay(
     contractData: d.contractData,
     lastSessionId: sessionId,
     payCoinSelection: coinSelection,
-    payReq,
     payCostInfo,
+    coinDepositPermissions,
     timestampAccept: getTimestampNow(),
     timestampLastRefundStatus: undefined,
     proposalId: proposal.proposalId,
@@ -534,7 +530,9 @@ async function incrementProposalRetry(
     pr.lastError = err;
     await tx.put(Stores.proposals, pr);
   });
-  ws.notify({ type: NotificationType.ProposalOperationError });
+  if (err) {
+    ws.notify({ type: NotificationType.ProposalOperationError, error: err });
+  }
 }
 
 async function incrementPurchasePayRetry(
@@ -600,25 +598,24 @@ async function processDownloadProposalImpl(
     return;
   }
 
-  const parsedUrl = new URL(
-    getOrderDownloadUrl(proposal.merchantBaseUrl, proposal.orderId),
-  );
-  parsedUrl.searchParams.set("nonce", proposal.noncePub);
-  const urlWithNonce = parsedUrl.href;
-  console.log("downloading contract from '" + urlWithNonce + "'");
-  let resp;
-  try {
-    resp = await ws.http.get(urlWithNonce);
-  } catch (e) {
-    console.log("contract download failed", e);
-    throw e;
-  }
+  const orderClaimUrl = new URL(
+    `orders/${proposal.orderId}/claim`,
+    proposal.merchantBaseUrl,
+  ).href;
+  logger.trace("downloading contract from '" + orderClaimUrl + "'");
 
-  if (resp.status !== 200) {
-    throw Error(`contract download failed with status ${resp.status}`);
-  }
+  const proposalResp = await httpPostTalerJson({
+    url: orderClaimUrl,
+    body: {
+      nonce: proposal.noncePub,
+    },
+    codec: codecForProposal(),
+    http: ws.http,
+  });
 
-  const proposalResp = codecForProposal().decode(await resp.json());
+  // The proposalResp contains the contract terms as raw JSON,
+  // as the coded to parse them doesn't necessarily round-trip.
+  // We need this raw JSON to compute the contract terms hash.
 
   const contractTermsHash = await ws.cryptoApi.hashString(
     canonicalJson(proposalResp.contract_terms),
@@ -774,26 +771,24 @@ export async function submitPay(
     throw Error("not submitting payment for aborted purchase");
   }
   const sessionId = purchase.lastSessionId;
-  let resp;
-  const payReq = { ...purchase.payReq, session_id: sessionId };
 
   console.log("paying with session ID", sessionId);
 
-  const payUrl = new URL("pay", purchase.contractData.merchantBaseUrl).href;
+  const payUrl = new URL(
+    `orders/${purchase.contractData.orderId}/pay`,
+    purchase.contractData.merchantBaseUrl,
+  ).href;
+
+  const merchantResp = await httpPostTalerJson({
+    url: payUrl,
+    body: {
+      coins: purchase.coinDepositPermissions,
+      session_id: purchase.lastSessionId,
+    },
+    codec: codecForMerchantPayResponse(),
+    http: ws.http,
+  });
 
-  try {
-    console.log("pay req", payReq);
-    resp = await ws.http.postJson(payUrl, payReq);
-  } catch (e) {
-    // Gives the user the option to retry / abort and refresh
-    console.log("payment failed", e);
-    throw e;
-  }
-  if (resp.status !== 200) {
-    console.log(await resp.json());
-    throw Error(`unexpected status (${resp.status}) for /pay`);
-  }
-  const merchantResp = await resp.json();
   console.log("got success from pay URL", merchantResp);
 
   const now = getTimestampNow();
@@ -1027,7 +1022,7 @@ export async function confirmPay(
       coinPriv: coin.coinPriv,
       coinPub: coin.coinPub,
       contractTermsHash: d.contractData.contractTermsHash,
-      denomPub: coin.denomPub,
+      denomPubHash: coin.denomPubHash,
       denomSig: coin.denomSig,
       exchangeBaseUrl: coin.exchangeBaseUrl,
       feeDeposit: denom.feeDeposit,
@@ -1047,8 +1042,6 @@ export async function confirmPay(
     sessionIdOverride,
   );
 
-  logger.trace("confirmPay: submitting payment after creating purchase 
record");
-  logger.trace("purchaseRecord:", purchase);
   return submitPay(ws, proposalId);
 }
 
diff --git a/src/operations/recoup.ts b/src/operations/recoup.ts
index e1c2325d..d1b3c3bd 100644
--- a/src/operations/recoup.ts
+++ b/src/operations/recoup.ts
@@ -48,7 +48,8 @@ import { RefreshReason, OperationError } from 
"../types/walletTypes";
 import { TransactionHandle } from "../util/query";
 import { encodeCrock, getRandomBytes } from "../crypto/talerCrypto";
 import { getTimestampNow } from "../util/time";
-import { guardOperationException, scrutinizeTalerJsonResponse } from 
"./errors";
+import { guardOperationException } from "./errors";
+import { httpPostTalerJson } from "../util/http";
 
 async function incrementRecoupRetry(
   ws: InternalWalletState,
@@ -146,11 +147,12 @@ async function recoupWithdrawCoin(
 
   const recoupRequest = await ws.cryptoApi.createRecoupRequest(coin);
   const reqUrl = new URL(`/coins/${coin.coinPub}/recoup`, 
coin.exchangeBaseUrl);
-  const resp = await ws.http.postJson(reqUrl.href, recoupRequest);
-  const recoupConfirmation = await scrutinizeTalerJsonResponse(
-    resp,
-    codecForRecoupConfirmation(),
-  );
+  const recoupConfirmation = await httpPostTalerJson({
+    url: reqUrl.href,
+    body: recoupRequest,
+    codec: codecForRecoupConfirmation(),
+    http: ws.http,
+  });
 
   if (recoupConfirmation.reserve_pub !== reservePub) {
     throw Error(`Coin's reserve doesn't match reserve on recoup`);
@@ -220,11 +222,13 @@ async function recoupRefreshCoin(
   const recoupRequest = await ws.cryptoApi.createRecoupRequest(coin);
   const reqUrl = new URL(`/coins/${coin.coinPub}/recoup`, 
coin.exchangeBaseUrl);
   console.log("making recoup request");
-  const resp = await ws.http.postJson(reqUrl.href, recoupRequest);
-  const recoupConfirmation = await scrutinizeTalerJsonResponse(
-    resp,
-    codecForRecoupConfirmation(),
-  );
+  
+  const recoupConfirmation = await httpPostTalerJson({
+    url: reqUrl.href,
+    body: recoupRequest,
+    codec: codecForRecoupConfirmation(),
+    http: ws.http,
+  });
 
   if (recoupConfirmation.old_coin_pub != cs.oldCoinPub) {
     throw Error(`Coin's oldCoinPub doesn't match reserve on recoup`);
diff --git a/src/operations/withdraw.ts b/src/operations/withdraw.ts
index 19b470e8..98969d21 100644
--- a/src/operations/withdraw.ts
+++ b/src/operations/withdraw.ts
@@ -46,7 +46,7 @@ import { updateExchangeFromUrl, getExchangeTrust } from 
"./exchanges";
 import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "./versions";
 
 import * as LibtoolVersion from "../util/libtoolVersion";
-import { guardOperationException, scrutinizeTalerJsonResponse } from 
"./errors";
+import { guardOperationException } from "./errors";
 import { NotificationType } from "../types/notifications";
 import {
   getTimestampNow,
@@ -54,6 +54,7 @@ import {
   timestampCmp,
   timestampSubtractDuraction,
 } from "../util/time";
+import { httpPostTalerJson } from "../util/http";
 
 const logger = new Logger("withdraw.ts");
 
@@ -308,8 +309,13 @@ async function processPlanchet(
     `reserves/${planchet.reservePub}/withdraw`,
     exchange.baseUrl,
   ).href;
-  const resp = await ws.http.postJson(reqUrl, wd);
-  const r = await scrutinizeTalerJsonResponse(resp, 
codecForWithdrawResponse());
+
+  const r = await httpPostTalerJson({
+    url: reqUrl,
+    body: wd,
+    codec: codecForWithdrawResponse(),
+    http: ws.http,
+  });
 
   logger.trace(`got response for /withdraw`);
 
diff --git a/src/types/dbTypes.ts b/src/types/dbTypes.ts
index 4f7b89b6..b085f83d 100644
--- a/src/types/dbTypes.ts
+++ b/src/types/dbTypes.ts
@@ -1250,10 +1250,9 @@ export interface PurchaseRecord {
   contractData: WalletContractData;
 
   /**
-   * The payment request, ready to be send to the merchant's
-   * /pay URL.
+   * Deposit permissions, available once the user has accepted the payment.
    */
-  payReq: PayReq;
+  coinDepositPermissions: CoinDepositPermission[];
 
   payCoinSelection: PayCoinSelection;
 
diff --git a/src/types/notifications.ts b/src/types/notifications.ts
index 644dfdc8..ac30b6fe 100644
--- a/src/types/notifications.ts
+++ b/src/types/notifications.ts
@@ -168,6 +168,7 @@ export interface PayOperationErrorNotification {
 
 export interface ProposalOperationErrorNotification {
   type: NotificationType.ProposalOperationError;
+  error: OperationError;
 }
 
 export interface TipOperationErrorNotification {
diff --git a/src/types/talerTypes.ts b/src/types/talerTypes.ts
index 0221d26b..232f5f31 100644
--- a/src/types/talerTypes.ts
+++ b/src/types/talerTypes.ts
@@ -215,7 +215,7 @@ export interface CoinDepositPermission {
   /**
    * The denomination public key associated with this coin.
    */
-  denom_pub: string;
+  h_denom: string;
   /**
    * The amount that is subtracted from this coin with this payment.
    */
@@ -433,31 +433,6 @@ export class ContractTerms {
   extra: any;
 }
 
-/**
- * Payment body sent to the merchant's /pay.
- */
-export interface PayReq {
-  /**
-   * Coins with signature.
-   */
-  coins: CoinDepositPermission[];
-
-  /**
-   * The merchant public key, used to uniquely
-   * identify the merchant instance.
-   */
-  merchant_pub: string;
-
-  /**
-   * Order ID that's being payed for.
-   */
-  order_id: string;
-
-  /**
-   * Mode for /pay.
-   */
-  mode: "pay" | "abort-refund";
-}
 
 /**
  * Refund permission in the format that the merchant gives it to us.
@@ -809,6 +784,10 @@ export interface CoinDumpJson {
   }>;
 }
 
+export interface MerchantPayResponse {
+  sig: string;
+}
+
 export type AmountString = string;
 export type Base32String = string;
 export type EddsaSignatureString = string;
@@ -1044,3 +1023,8 @@ export const codecForWithdrawResponse = (): 
Codec<WithdrawResponse> =>
   makeCodecForObject<WithdrawResponse>()
     .property("ev_sig", codecForString)
     .build("WithdrawResponse");
+
+export const codecForMerchantPayResponse = (): Codec<MerchantPayResponse> =>
+  makeCodecForObject<MerchantPayResponse>()
+    .property("sig", codecForString)
+    .build("MerchantPayResponse");
diff --git a/src/types/walletTypes.ts b/src/types/walletTypes.ts
index 63b20095..ee7d071c 100644
--- a/src/types/walletTypes.ts
+++ b/src/types/walletTypes.ts
@@ -473,7 +473,7 @@ export interface DepositInfo {
   merchantPub: string;
   feeDeposit: AmountJson;
   wireInfoHash: string;
-  denomPub: string;
+  denomPubHash: string;
   denomSig: string;
 }
 
diff --git a/src/util/http.ts b/src/util/http.ts
index 0ac989a1..bc054096 100644
--- a/src/util/http.ts
+++ b/src/util/http.ts
@@ -14,6 +14,9 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { Codec } from "./codec";
+import { OperationFailedError } from "../operations/errors";
+
 /**
  * Helpers for doing XMLHttpRequest-s that are based on ES6 promises.
  * Allows for easy mocking for test cases.
@@ -172,3 +175,115 @@ export class BrowserHttpLib implements HttpRequestLibrary 
{
     // Nothing to do
   }
 }
+
+export interface PostJsonRequest<RespType> {
+  http: HttpRequestLibrary;
+  url: string;
+  body: any;
+  codec: Codec<RespType>;
+}
+
+/**
+ * Helper for making Taler-style HTTP POST requests with a JSON payload and 
response.
+ */
+export async function httpPostTalerJson<RespType>(
+  req: PostJsonRequest<RespType>,
+): Promise<RespType> {
+  const resp = await req.http.postJson(req.url, req.body);
+
+  if (resp.status !== 200) {
+    let exc: OperationFailedError | undefined = undefined;
+    try {
+      const errorJson = await resp.json();
+      const m = `received error response (status ${resp.status})`;
+      exc = new OperationFailedError({
+        type: "protocol",
+        message: m,
+        details: {
+          httpStatusCode: resp.status,
+          errorResponse: errorJson,
+        },
+      });
+    } catch (e) {
+      const m = "could not parse response JSON";
+      exc = new OperationFailedError({
+        type: "network",
+        message: m,
+        details: {
+          status: resp.status,
+        },
+      });
+    }
+    throw exc;
+  }
+  let json: any;
+  try {
+    json = await resp.json();
+  } catch (e) {
+    const m = "could not parse response JSON";
+    throw new OperationFailedError({
+      type: "network",
+      message: m,
+      details: {
+        status: resp.status,
+      },
+    });
+  }
+  return req.codec.decode(json);
+}
+
+
+export interface GetJsonRequest<RespType> {
+  http: HttpRequestLibrary;
+  url: string;
+  codec: Codec<RespType>;
+}
+
+/**
+ * Helper for making Taler-style HTTP GET requests with a JSON payload.
+ */
+export async function httpGetTalerJson<RespType>(
+  req: GetJsonRequest<RespType>,
+): Promise<RespType> {
+  const resp = await req.http.get(req.url);
+
+  if (resp.status !== 200) {
+    let exc: OperationFailedError | undefined = undefined;
+    try {
+      const errorJson = await resp.json();
+      const m = `received error response (status ${resp.status})`;
+      exc = new OperationFailedError({
+        type: "protocol",
+        message: m,
+        details: {
+          httpStatusCode: resp.status,
+          errorResponse: errorJson,
+        },
+      });
+    } catch (e) {
+      const m = "could not parse response JSON";
+      exc = new OperationFailedError({
+        type: "network",
+        message: m,
+        details: {
+          status: resp.status,
+        },
+      });
+    }
+    throw exc;
+  }
+  let json: any;
+  try {
+    json = await resp.json();
+  } catch (e) {
+    const m = "could not parse response JSON";
+    throw new OperationFailedError({
+      type: "network",
+      message: m,
+      details: {
+        status: resp.status,
+      },
+    });
+  }
+  return req.codec.decode(json);
+}
diff --git a/src/util/taleruri.ts b/src/util/taleruri.ts
index 2eaea284..30209d48 100644
--- a/src/util/taleruri.ts
+++ b/src/util/taleruri.ts
@@ -97,15 +97,6 @@ export function classifyTalerUri(s: string): TalerUriType {
   return TalerUriType.Unknown;
 }
 
-export function getOrderDownloadUrl(
-  merchantBaseUrl: string,
-  orderId: string,
-): string {
-  const u = new URL("proposal", merchantBaseUrl);
-  u.searchParams.set("order_id", orderId);
-  return u.href;
-}
-
 export function parsePayUri(s: string): PayUriResult | undefined {
   const pfx = "taler://pay/";
   if (!s.toLowerCase().startsWith(pfx)) {
@@ -133,7 +124,7 @@ export function parsePayUri(s: string): PayUriResult | 
undefined {
   }
 
   if (maybePath === "-") {
-    maybePath = "public/";
+    maybePath = "";
   } else {
     maybePath = decodeURIComponent(maybePath) + "/";
   }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]