gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: wallet-core,harness: implemen


From: gnunet
Subject: [taler-wallet-core] branch master updated: wallet-core,harness: implement pay templating
Date: Sat, 11 Feb 2023 14:24:41 +0100

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 04ab9f378 wallet-core,harness: implement pay templating
04ab9f378 is described below

commit 04ab9f37801f6a42b85581cc79667239d3fc79e5
Author: Florian Dold <florian@dold.me>
AuthorDate: Sat Feb 11 14:24:29 2023 +0100

    wallet-core,harness: implement pay templating
---
 packages/taler-harness/src/harness/harness.ts      | 31 +++++--
 .../src/integrationtests/test-payment-template.ts  | 95 ++++++++++++++++++++++
 .../src/integrationtests/testrunner.ts             |  2 +
 packages/taler-harness/tsconfig.json               |  2 +-
 packages/taler-util/src/index.ts                   |  1 +
 .../src/merchant-api-types.ts}                     | 56 ++++++++++---
 packages/taler-util/src/taler-types.ts             | 15 ++--
 packages/taler-util/src/taleruri.test.ts           | 36 ++++++++
 packages/taler-util/src/taleruri.ts                | 65 ++++++++++++++-
 packages/taler-util/src/wallet-types.ts            | 11 +++
 packages/taler-wallet-core/src/wallet-api-types.ts | 14 +++-
 packages/taler-wallet-core/src/wallet.ts           | 56 +++++++++++--
 12 files changed, 346 insertions(+), 38 deletions(-)

diff --git a/packages/taler-harness/src/harness/harness.ts 
b/packages/taler-harness/src/harness/harness.ts
index 4e5d8238c..3659ea538 100644
--- a/packages/taler-harness/src/harness/harness.ts
+++ b/packages/taler-harness/src/harness/harness.ts
@@ -38,6 +38,7 @@ import {
   hash,
   j2s,
   Logger,
+  MerchantTemplateAddDetails,
   parsePaytoUri,
   stringToBytes,
   TalerProtocolDuration,
@@ -66,15 +67,15 @@ import { CoinConfig } from "./denomStructures.js";
 import { LibeufinNexusApi, LibeufinSandboxApi } from "./libeufin-apis.js";
 import {
   codecForMerchantOrderPrivateStatusResponse,
-  codecForPostOrderResponse,
+  codecForMerchantPostOrderResponse,
   MerchantInstancesResponse,
   MerchantOrderPrivateStatusResponse,
-  PostOrderRequest,
-  PostOrderResponse,
+  MerchantPostOrderRequest,
+  MerchantPostOrderResponse,
   TipCreateConfirmation,
   TipCreateRequest,
   TippingReserveStatus,
-} from "./merchantApiTypes.js";
+} from "@gnu-taler/taler-util";
 import {
   createRemoteWallet,
   getClientFromRemoteWallet,
@@ -1473,15 +1474,31 @@ export namespace MerchantPrivateApi {
   export async function createOrder(
     merchantService: MerchantServiceInterface,
     instanceName: string,
-    req: PostOrderRequest,
+    req: MerchantPostOrderRequest,
     withAuthorization: WithAuthorization = {},
-  ): Promise<PostOrderResponse> {
+  ): Promise<MerchantPostOrderResponse> {
     const baseUrl = merchantService.makeInstanceBaseUrl(instanceName);
     let url = new URL("private/orders", baseUrl);
     const resp = await axios.post(url.href, req, {
       headers: withAuthorization as Record<string, string>,
     });
-    return codecForPostOrderResponse().decode(resp.data);
+    return codecForMerchantPostOrderResponse().decode(resp.data);
+  }
+
+  export async function createTemplate(
+    merchantService: MerchantServiceInterface,
+    instanceName: string,
+    req: MerchantTemplateAddDetails,
+    withAuthorization: WithAuthorization = {},
+  ) {
+    const baseUrl = merchantService.makeInstanceBaseUrl(instanceName);
+    let url = new URL("private/templates", baseUrl);
+    const resp = await axios.post(url.href, req, {
+      headers: withAuthorization as Record<string, string>,
+    });
+    if (resp.status !== 204) {
+      throw Error("failed to create template");
+    }
   }
 
   export async function queryPrivateOrderStatus(
diff --git 
a/packages/taler-harness/src/integrationtests/test-payment-template.ts 
b/packages/taler-harness/src/integrationtests/test-payment-template.ts
new file mode 100644
index 000000000..41e43e28a
--- /dev/null
+++ b/packages/taler-harness/src/integrationtests/test-payment-template.ts
@@ -0,0 +1,95 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import { ConfirmPayResultType, Duration, PreparePayResultType, 
TalerProtocolTimestamp } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
+import {
+  createSimpleTestkudosEnvironment,
+  withdrawViaBank,
+  makeTestPayment,
+} from "../harness/helpers.js";
+
+/**
+ * Test for taler://payment-template/ URIs
+ */
+export async function runPaymentTemplateTest(t: GlobalTestState) {
+  // Set up test environment
+
+  const { wallet, bank, exchange, merchant } =
+    await createSimpleTestkudosEnvironment(t);
+
+  await MerchantPrivateApi.createTemplate(merchant, "default", {
+    template_id: "template1",
+    template_description: "my test template",
+    template_contract: {
+      minimum_age: 0,
+      pay_duration: Duration.toTalerProtocolDuration(
+        Duration.fromSpec({
+          minutes: 2,
+        }),
+      ),
+      summary: "hello, I'm a summary",
+    },
+  });
+
+  // Withdraw digital cash into the wallet.
+
+  await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+
+  // Request a template payment
+
+  const preparePayResult = await wallet.client.call(
+    WalletApiOperation.PreparePayForTemplate,
+    {
+      talerPayTemplateUri: 
`taler+http://pay-template/localhost:${merchant.port}/template1?amount=TESTKUDOS:1`,
+      templateParams: {},
+    },
+  );
+
+  console.log(preparePayResult);
+
+  t.assertTrue(
+    preparePayResult.status === PreparePayResultType.PaymentPossible,
+  );
+
+  // Pay for it
+
+  const r2 = await wallet.client.call(WalletApiOperation.ConfirmPay, {
+    proposalId: preparePayResult.proposalId,
+  });
+
+  t.assertTrue(r2.type === ConfirmPayResultType.Done);
+
+  // Check if payment was successful.
+
+  const orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
+    merchant,
+    {
+      orderId: preparePayResult.contractTerms.order_id,
+      instance: "default",
+    },
+  );
+
+  t.assertTrue(orderStatus.order_status === "paid");
+
+  await wallet.runUntilDone();
+}
+
+runPaymentTemplateTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts 
b/packages/taler-harness/src/integrationtests/testrunner.ts
index 025f2e514..a20300e02 100644
--- a/packages/taler-harness/src/integrationtests/testrunner.ts
+++ b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -100,6 +100,7 @@ import { runKycTest } from "./test-kyc.js";
 import { runPaymentAbortTest } from "./test-payment-abort.js";
 import { runWithdrawalFeesTest } from "./test-withdrawal-fees.js";
 import { runWalletBalanceTest } from "./test-wallet-balance.js";
+import { runPaymentTemplateTest } from "./test-payment-template.js";
 
 /**
  * Test runner.
@@ -163,6 +164,7 @@ const allTests: TestMainFunction[] = [
   runPaymentIdempotencyTest,
   runPaymentMultipleTest,
   runPaymentTest,
+  runPaymentTemplateTest,
   runPaymentAbortTest,
   runPaymentTransientTest,
   runPaymentZeroTest,
diff --git a/packages/taler-harness/tsconfig.json 
b/packages/taler-harness/tsconfig.json
index 447d3f946..d022b16e8 100644
--- a/packages/taler-harness/tsconfig.json
+++ b/packages/taler-harness/tsconfig.json
@@ -21,7 +21,7 @@
     "baseUrl": "./src",
     "typeRoots": ["./node_modules/@types"]
   },
-  "include": ["src/**/*"],
+  "include": ["src/**/*", "../taler-util/src/merchant-api-types.ts"],
   "references": [
     {
       "path": "../taler-wallet-core/"
diff --git a/packages/taler-util/src/index.ts b/packages/taler-util/src/index.ts
index 2f674d097..661b0332f 100644
--- a/packages/taler-util/src/index.ts
+++ b/packages/taler-util/src/index.ts
@@ -35,3 +35,4 @@ export { RequestThrottler } from "./RequestThrottler.js";
 export * from "./CancellationToken.js";
 export * from "./contract-terms.js";
 export * from "./base64.js";
+export * from "./merchant-api-types.js";
diff --git a/packages/taler-harness/src/harness/merchantApiTypes.ts 
b/packages/taler-util/src/merchant-api-types.ts
similarity index 86%
rename from packages/taler-harness/src/harness/merchantApiTypes.ts
rename to packages/taler-util/src/merchant-api-types.ts
index 1985e9150..61002191a 100644
--- a/packages/taler-harness/src/harness/merchantApiTypes.ts
+++ b/packages/taler-util/src/merchant-api-types.ts
@@ -26,7 +26,6 @@
  */
 import {
   MerchantContractTerms,
-  Duration,
   Codec,
   buildCodecForObject,
   codecForString,
@@ -47,7 +46,7 @@ import {
   TalerProtocolTimestamp,
 } from "@gnu-taler/taler-util";
 
-export interface PostOrderRequest {
+export interface MerchantPostOrderRequest {
   // The order must at least contain the minimal
   // order detail, but can override all
   order: Partial<MerchantContractTerms>;
@@ -71,18 +70,18 @@ export interface PostOrderRequest {
 
 export type ClaimToken = string;
 
-export interface PostOrderResponse {
+export interface MerchantPostOrderResponse {
   order_id: string;
   token?: ClaimToken;
 }
 
-export const codecForPostOrderResponse = (): Codec<PostOrderResponse> =>
-  buildCodecForObject<PostOrderResponse>()
+export const codecForMerchantPostOrderResponse = (): 
Codec<MerchantPostOrderResponse> =>
+  buildCodecForObject<MerchantPostOrderResponse>()
     .property("order_id", codecForString())
     .property("token", codecOptional(codecForString()))
     .build("PostOrderResponse");
 
-export const codecForRefundDetails = (): Codec<RefundDetails> =>
+export const codecForMerchantRefundDetails = (): Codec<RefundDetails> =>
   buildCodecForObject<RefundDetails>()
     .property("reason", codecForString())
     .property("pending", codecForBoolean())
@@ -90,9 +89,9 @@ export const codecForRefundDetails = (): Codec<RefundDetails> 
=>
     .property("timestamp", codecForTimestamp)
     .build("PostOrderResponse");
 
-export const codecForCheckPaymentPaidResponse =
-  (): Codec<CheckPaymentPaidResponse> =>
-    buildCodecForObject<CheckPaymentPaidResponse>()
+export const codecForMerchantCheckPaymentPaidResponse =
+  (): Codec<MerchantCheckPaymentPaidResponse> =>
+    buildCodecForObject<MerchantCheckPaymentPaidResponse>()
       .property("order_status_url", codecForString())
       .property("order_status", codecForConstString("paid"))
       .property("refunded", codecForBoolean())
@@ -128,13 +127,13 @@ export const codecForMerchantOrderPrivateStatusResponse =
   (): Codec<MerchantOrderPrivateStatusResponse> =>
     buildCodecForUnion<MerchantOrderPrivateStatusResponse>()
       .discriminateOn("order_status")
-      .alternative("paid", codecForCheckPaymentPaidResponse())
+      .alternative("paid", codecForMerchantCheckPaymentPaidResponse())
       .alternative("unpaid", codecForCheckPaymentUnpaidResponse())
       .alternative("claimed", codecForCheckPaymentClaimedResponse())
       .build("MerchantOrderPrivateStatusResponse");
 
 export type MerchantOrderPrivateStatusResponse =
-  | CheckPaymentPaidResponse
+  | MerchantCheckPaymentPaidResponse
   | CheckPaymentUnpaidResponse
   | CheckPaymentClaimedResponse;
 
@@ -145,7 +144,7 @@ export interface CheckPaymentClaimedResponse {
   contract_terms: MerchantContractTerms;
 }
 
-export interface CheckPaymentPaidResponse {
+export interface MerchantCheckPaymentPaidResponse {
   // did the customer pay for this contract
   order_status: "paid";
 
@@ -334,3 +333,36 @@ export interface MerchantInstanceDetail {
   // front-ends do not have to support wallets selecting payment targets.
   payment_targets: string[];
 }
+
+export interface MerchantTemplateContractDetails {
+  // Human-readable summary for the template.
+  summary?: string;
+
+  // The price is imposed by the merchant and cannot be changed by the 
customer.
+  // This parameter is optional.
+  amount?: AmountString;
+
+  // Minimum age buyer must have (in years). Default is 0.
+  minimum_age: number;
+
+  // The time the customer need to pay before his order will be deleted.
+  // It is deleted if the customer did not pay and if the duration is over.
+  pay_duration: TalerProtocolDuration;
+}
+
+export interface MerchantTemplateAddDetails {
+
+  // Template ID to use.
+  template_id: string;
+
+  // Human-readable description for the template.
+  template_description: string;
+
+  // A base64-encoded image selected by the merchant.
+  // This parameter is optional.
+  // We are not sure about it.
+  image?: string;
+
+  // Additional information in a separate template.
+  template_contract: MerchantTemplateContractDetails;
+}
\ No newline at end of file
diff --git a/packages/taler-util/src/taler-types.ts 
b/packages/taler-util/src/taler-types.ts
index bb15f0494..6e7df2c04 100644
--- a/packages/taler-util/src/taler-types.ts
+++ b/packages/taler-util/src/taler-types.ts
@@ -1481,10 +1481,11 @@ export const codecForWithdrawResponse = (): 
Codec<ExchangeWithdrawResponse> =>
     .property("ev_sig", codecForBlindedDenominationSignature())
     .build("WithdrawResponse");
 
-export const codecForWithdrawBatchResponse = (): 
Codec<ExchangeWithdrawBatchResponse> =>
-  buildCodecForObject<ExchangeWithdrawBatchResponse>()
-    .property("ev_sigs", codecForList(codecForWithdrawResponse()))
-    .build("WithdrawBatchResponse");
+export const codecForWithdrawBatchResponse =
+  (): Codec<ExchangeWithdrawBatchResponse> =>
+    buildCodecForObject<ExchangeWithdrawBatchResponse>()
+      .property("ev_sigs", codecForList(codecForWithdrawResponse()))
+      .build("WithdrawBatchResponse");
 
 export const codecForMerchantPayResponse = (): Codec<MerchantPayResponse> =>
   buildCodecForObject<MerchantPayResponse>()
@@ -1757,7 +1758,6 @@ export interface ExchangeBatchWithdrawRequest {
   planchets: ExchangeWithdrawRequest[];
 }
 
-
 export interface ExchangeRefreshRevealRequest {
   new_denoms_h: HashCodeString[];
   coin_evs: CoinEnvelope[];
@@ -2113,3 +2113,8 @@ export const codecForWalletKycUuid = (): 
Codec<WalletKycUuid> =>
     .property("requirement_row", codecForNumber())
     .property("h_payto", codecForString())
     .build("WalletKycUuid");
+
+export interface MerchantUsingTemplateDetails {
+  summary?: string;
+  amount?: AmountString;
+}
diff --git a/packages/taler-util/src/taleruri.test.ts 
b/packages/taler-util/src/taleruri.test.ts
index 3ee243fb3..a6c4d89fc 100644
--- a/packages/taler-util/src/taleruri.test.ts
+++ b/packages/taler-util/src/taleruri.test.ts
@@ -22,6 +22,8 @@ import {
   parseTipUri,
   parsePayPushUri,
   constructPayPushUri,
+  parsePayTemplateUri,
+  constructPayUri,
 } from "./taleruri.js";
 
 test("taler pay url parsing: wrong scheme", (t) => {
@@ -225,3 +227,37 @@ test("taler peer to peer push URI (construction)", (t) => {
   });
   t.deepEqual(url, "taler://pay-push/foo.example.com/bla/123");
 });
+
+test("taler pay URI (construction)", (t) => {
+  const url1 = constructPayUri("http://localhost:123/";, "foo", "");
+  t.deepEqual(url1, "taler+http://pay/localhost:123/foo/";);
+
+  const url2 = constructPayUri("http://localhost:123/";, "foo", "bla");
+  t.deepEqual(url2, "taler+http://pay/localhost:123/foo/bla";);
+});
+
+test("taler pay template URI (parsing)", (t) => {
+  const url1 =
+    
"taler://pay-template/merchant.example.com/FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY?amount=KUDOS:5";
+  const r1 = parsePayTemplateUri(url1);
+  if (!r1) {
+    t.fail();
+    return;
+  }
+  t.deepEqual(r1.merchantBaseUrl, "https://merchant.example.com/";);
+  t.deepEqual(r1.templateId, "FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY");
+  t.deepEqual(r1.templateParams.amount, "KUDOS:5");
+});
+
+test("taler pay template URI (parsing, http with port)", (t) => {
+  const url1 =
+    
"taler+http://pay-template/merchant.example.com:1234/FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY?amount=KUDOS:5";;
+  const r1 = parsePayTemplateUri(url1);
+  if (!r1) {
+    t.fail();
+    return;
+  }
+  t.deepEqual(r1.merchantBaseUrl, "http://merchant.example.com:1234/";);
+  t.deepEqual(r1.templateId, "FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY");
+  t.deepEqual(r1.templateParams.amount, "KUDOS:5");
+});
diff --git a/packages/taler-util/src/taleruri.ts 
b/packages/taler-util/src/taleruri.ts
index 4e47acbce..2aa9cb030 100644
--- a/packages/taler-util/src/taleruri.ts
+++ b/packages/taler-util/src/taleruri.ts
@@ -16,7 +16,6 @@
 
 import { BackupRecovery } from "./backup-types.js";
 import { canonicalizeBaseUrl } from "./helpers.js";
-import { initNodePrng } from "./prng-node.js";
 import { URLSearchParams, URL } from "./url.js";
 
 export interface PayUriResult {
@@ -27,6 +26,12 @@ export interface PayUriResult {
   noncePriv: string | undefined;
 }
 
+export interface PayTemplateUriResult {
+  merchantBaseUrl: string;
+  templateId: string;
+  templateParams: Record<string, string>;
+}
+
 export interface WithdrawUriResult {
   bankIntegrationApiBaseUrl: string;
   withdrawalOperationId: string;
@@ -91,6 +96,7 @@ export function parseWithdrawUri(s: string): 
WithdrawUriResult | undefined {
 
 export enum TalerUriType {
   TalerPay = "taler-pay",
+  TalerTemplate = "taler-template",
   TalerWithdraw = "taler-withdraw",
   TalerTip = "taler-tip",
   TalerRefund = "taler-refund",
@@ -103,6 +109,7 @@ export enum TalerUriType {
 
 const talerActionPayPull = "pay-pull";
 const talerActionPayPush = "pay-push";
+const talerActionPayTemplate = "pay-template";
 
 /**
  * Classify a taler:// URI.
@@ -121,6 +128,12 @@ export function classifyTalerUri(s: string): TalerUriType {
   if (sl.startsWith("taler+http://pay/";)) {
     return TalerUriType.TalerPay;
   }
+  if (sl.startsWith("taler://pay-template/")) {
+    return TalerUriType.TalerPay;
+  }
+  if (sl.startsWith("taler+http://pay-template/";)) {
+    return TalerUriType.TalerPay;
+  }
   if (sl.startsWith("taler://tip/")) {
     return TalerUriType.TalerTip;
   }
@@ -216,6 +229,38 @@ export function parsePayUri(s: string): PayUriResult | 
undefined {
   };
 }
 
+export function parsePayTemplateUri(
+  s: string,
+): PayTemplateUriResult | undefined {
+  const pi = parseProtoInfo(s, talerActionPayTemplate);
+  if (!pi) {
+    return undefined;
+  }
+  const c = pi?.rest.split("?");
+  const q = new URLSearchParams(c[1] ?? "");
+  const parts = c[0].split("/");
+  if (parts.length < 2) {
+    return undefined;
+  }
+  const host = parts[0].toLowerCase();
+  const templateId = parts[parts.length - 1];
+  const pathSegments = parts.slice(1, parts.length - 1);
+  const p = [host, ...pathSegments].join("/");
+  const merchantBaseUrl = canonicalizeBaseUrl(`${pi.innerProto}://${p}/`);
+
+  const params: Record<string, string> = {};
+
+  q.forEach((v, k) => {
+    params[k] = v;
+  });
+
+  return {
+    merchantBaseUrl,
+    templateId,
+    templateParams: params,
+  };
+}
+
 export function constructPayUri(
   merchantBaseUrl: string,
   orderId: string,
@@ -227,9 +272,21 @@ export function constructPayUri(
   const url = new URL(base);
   const isHttp = base.startsWith("http://";);
   let result = isHttp ? `taler+http://pay/` : `taler://pay/`;
-  result += `${url.hostname}${url.pathname}${orderId}/${sessionId}?`;
-  if (claimToken) result += `c=${claimToken}`;
-  if (noncePriv) result += `n=${noncePriv}`;
+  result += url.hostname;
+  if (url.port != "") {
+    result += `:${url.port}`;
+  }
+  result += `${url.pathname}${orderId}/${sessionId}`;
+  let queryPart = "";
+  if (claimToken) {
+    queryPart += `c=${claimToken}`;
+  }
+  if (noncePriv) {
+    queryPart += `n=${noncePriv}`;
+  }
+  if (queryPart) {
+    result += "?" + queryPart;
+  }
   return result;
 }
 
diff --git a/packages/taler-util/src/wallet-types.ts 
b/packages/taler-util/src/wallet-types.ts
index d57a221f3..0f29b964b 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -1418,6 +1418,17 @@ export const codecForPreparePayRequest = (): 
Codec<PreparePayRequest> =>
     .property("talerPayUri", codecForString())
     .build("PreparePay");
 
+export interface PreparePayTemplateRequest {
+  talerPayTemplateUri: string;
+  templateParams: Record<string, string>;
+}
+
+export const codecForPreparePayTemplateRequest = (): 
Codec<PreparePayTemplateRequest> =>
+  buildCodecForObject<PreparePayTemplateRequest>()
+    .property("talerPayTemplateUri", codecForString())
+    .property("templateParams", codecForAny())
+    .build("PreparePayTemplate");
+
 export interface ConfirmPayRequest {
   proposalId: string;
   sessionId?: string;
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts 
b/packages/taler-wallet-core/src/wallet-api-types.ts
index 61d1417f9..da57253a0 100644
--- a/packages/taler-wallet-core/src/wallet-api-types.ts
+++ b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -78,6 +78,7 @@ import {
   PrepareDepositResponse,
   PreparePayRequest,
   PreparePayResult,
+  PreparePayTemplateRequest,
   PreparePeerPullPaymentRequest,
   PreparePeerPullPaymentResponse,
   PreparePeerPushPaymentRequest,
@@ -126,6 +127,7 @@ export enum WalletApiOperation {
   WithdrawTestkudos = "withdrawTestkudos",
   WithdrawTestBalance = "withdrawTestBalance",
   PreparePayForUri = "preparePayForUri",
+  PreparePayForTemplate = "preparePayForTemplate",
   GetContractTermsDetails = "getContractTermsDetails",
   RunIntegrationTest = "runIntegrationTest",
   TestCrypto = "testCrypto",
@@ -313,7 +315,7 @@ export type AcceptManualWithdrawalOp = {
 // group: Merchant Payments
 
 /**
- * Prepare to make a payment
+ * Prepare to make a payment based on a taler://pay/ URI.
  */
 export type PreparePayForUriOp = {
   op: WalletApiOperation.PreparePayForUri;
@@ -321,6 +323,15 @@ export type PreparePayForUriOp = {
   response: PreparePayResult;
 };
 
+/**
+ * Prepare to make a payment based on a taler://pay-template/ URI.
+ */
+export type PreparePayForTemplateOp = {
+  op: WalletApiOperation.PreparePayForTemplate;
+  request: PreparePayTemplateRequest;
+  response: PreparePayResult;
+};
+
 export type GetContractTermsDetailsOp = {
   op: WalletApiOperation.GetContractTermsDetails;
   request: GetContractTermsDetailsRequest;
@@ -835,6 +846,7 @@ export type WalletOperations = {
   [WalletApiOperation.GetVersion]: GetVersionOp;
   [WalletApiOperation.WithdrawFakebank]: WithdrawFakebankOp;
   [WalletApiOperation.PreparePayForUri]: PreparePayForUriOp;
+  [WalletApiOperation.PreparePayForTemplate]: PreparePayForTemplateOp;
   [WalletApiOperation.GetContractTermsDetails]: GetContractTermsDetailsOp;
   [WalletApiOperation.WithdrawTestkudos]: WithdrawTestkudosOp;
   [WalletApiOperation.ConfirmPay]: ConfirmPayOp;
diff --git a/packages/taler-wallet-core/src/wallet.ts 
b/packages/taler-wallet-core/src/wallet.ts
index f1ed592bd..57ae85c1c 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -56,8 +56,10 @@ import {
   codecForInitiatePeerPushPaymentRequest,
   codecForIntegrationTestArgs,
   codecForListKnownBankAccounts,
+  codecForMerchantPostOrderResponse,
   codecForPrepareDepositRequest,
   codecForPreparePayRequest,
+  codecForPreparePayTemplateRequest,
   codecForPreparePeerPullPaymentRequest,
   codecForPreparePeerPushPaymentRequest,
   codecForPrepareRefundRequest,
@@ -77,6 +79,7 @@ import {
   CoinDumpJson,
   CoinRefreshRequest,
   CoinStatus,
+  constructPayUri,
   CoreApiResponse,
   DenominationInfo,
   DenomOperationMap,
@@ -88,7 +91,6 @@ import {
   ExchangesListResponse,
   ExchangeTosStatusDetails,
   FeeDescription,
-  GetBalanceDetailRequest,
   GetExchangeTosResult,
   InitResponse,
   j2s,
@@ -96,7 +98,9 @@ import {
   KnownBankAccountsInfo,
   Logger,
   ManualWithdrawalDetails,
+  MerchantUsingTemplateDetails,
   NotificationType,
+  parsePayTemplateUri,
   parsePaytoUri,
   RefreshReason,
   TalerErrorCode,
@@ -156,11 +160,7 @@ import {
   runBackupCycle,
 } from "./operations/backup/index.js";
 import { setWalletDeviceId } from "./operations/backup/state.js";
-import {
-  getBalanceDetail,
-  getBalances,
-  getMerchantPaymentBalanceDetails,
-} from "./operations/balance.js";
+import { getBalanceDetail, getBalances } from "./operations/balance.js";
 import {
   getExchangeTosStatus,
   makeExchangeListItem,
@@ -186,7 +186,6 @@ import {
 } from "./operations/exchanges.js";
 import { getMerchantInfo } from "./operations/merchants.js";
 import {
-  abortPay as abortPay,
   applyRefund,
   applyRefundFromPurchaseId,
   confirmPay,
@@ -1171,11 +1170,50 @@ async function dispatchRequestInternal<Op extends 
WalletApiOperation>(
       await runPending(ws, true);
       return {};
     }
-    // FIXME: Deprecate one of the aliases!
     case WalletApiOperation.PreparePayForUri: {
       const req = codecForPreparePayRequest().decode(payload);
       return await preparePayForUri(ws, req.talerPayUri);
     }
+    case WalletApiOperation.PreparePayForTemplate: {
+      const req = codecForPreparePayTemplateRequest().decode(payload);
+      const url = parsePayTemplateUri(req.talerPayTemplateUri);
+      const templateDetails: MerchantUsingTemplateDetails = {};
+      if (!url) {
+        throw Error("invalid taler-template URI");
+      }
+      if (
+        url.templateParams.amount &&
+        typeof url.templateParams.amount === "string"
+      ) {
+        templateDetails.amount =
+          req.templateParams.amount ?? url.templateParams.amount;
+      }
+      if (
+        url.templateParams.summary &&
+        typeof url.templateParams.summary === "string"
+      ) {
+        templateDetails.summary =
+          req.templateParams.summary ?? url.templateParams.summary;
+      }
+      const reqUrl = new URL(
+        `templates/${url.templateId}`,
+        url.merchantBaseUrl,
+      );
+      const httpReq = await ws.http.postJson(reqUrl.href, templateDetails);
+      const resp = await readSuccessResponseJsonOrThrow(
+        httpReq,
+        codecForMerchantPostOrderResponse(),
+      );
+
+      const payUri = constructPayUri(
+        url.merchantBaseUrl,
+        resp.order_id,
+        "",
+        resp.token,
+      );
+
+      return await preparePayForUri(ws, payUri);
+    }
     case WalletApiOperation.ConfirmPay: {
       const req = codecForConfirmPayRequest().decode(payload);
       return await confirmPay(ws, req.proposalId, req.sessionId);
@@ -1434,6 +1472,8 @@ async function dispatchRequestInternal<Op extends 
WalletApiOperation>(
     case WalletApiOperation.GetVersion: {
       return getVersion(ws);
     }
+    //default:
+    //  assertUnreachable(operation);
   }
   throw TalerError.fromDetail(
     TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN,

-- 
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]