diff options
Diffstat (limited to 'packages')
-rw-r--r-- | packages/taler-harness/src/harness/harness.ts | 31 | ||||
-rw-r--r-- | packages/taler-harness/src/integrationtests/test-payment-template.ts | 95 | ||||
-rw-r--r-- | packages/taler-harness/src/integrationtests/testrunner.ts | 2 | ||||
-rw-r--r-- | packages/taler-harness/tsconfig.json | 2 | ||||
-rw-r--r-- | packages/taler-util/src/index.ts | 1 | ||||
-rw-r--r-- | packages/taler-util/src/merchant-api-types.ts (renamed from packages/taler-harness/src/harness/merchantApiTypes.ts) | 56 | ||||
-rw-r--r-- | packages/taler-util/src/taler-types.ts | 15 | ||||
-rw-r--r-- | packages/taler-util/src/taleruri.test.ts | 36 | ||||
-rw-r--r-- | packages/taler-util/src/taleruri.ts | 65 | ||||
-rw-r--r-- | packages/taler-util/src/wallet-types.ts | 11 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/wallet-api-types.ts | 14 | ||||
-rw-r--r-- | 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 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, |