diff options
Diffstat (limited to 'packages')
-rw-r--r-- | packages/taler-util/src/index.ts | 1 | ||||
-rw-r--r-- | packages/taler-util/src/qr.ts | 161 | ||||
-rw-r--r-- | packages/taler-util/src/wallet-types.ts | 14 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/wallet-api-types.ts | 10 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/wallet.ts | 8 |
5 files changed, 194 insertions, 0 deletions
diff --git a/packages/taler-util/src/index.ts b/packages/taler-util/src/index.ts index 9f99f2f5a..287e03584 100644 --- a/packages/taler-util/src/index.ts +++ b/packages/taler-util/src/index.ts @@ -50,6 +50,7 @@ export * from "./observability.js"; export * from "./operation.js"; export * from "./payto.js"; export * from "./promises.js"; +export * from "./qr.js"; export * from "./rfc3548.js"; export * from "./taler-crypto.js"; export * from "./taler-types.js"; diff --git a/packages/taler-util/src/qr.ts b/packages/taler-util/src/qr.ts new file mode 100644 index 000000000..2e8ffc652 --- /dev/null +++ b/packages/taler-util/src/qr.ts @@ -0,0 +1,161 @@ +/* + This file is part of GNU Taler + (C) 2024 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 { Amounts } from "./index.node.js"; +import { parsePaytoUri } from "./payto.js"; + +type EncodeResult = { type: "ok"; qrContent: string } | { type: "skip" }; + +/** + * See "Schweizer Implementation Guidelines QR-Rechnung". + */ +function encodePaytoAsSwissQrBill(paytoUri: string): EncodeResult { + const parsedPayto = parsePaytoUri(paytoUri); + if (!parsedPayto) { + throw Error("invalid payto URI"); + } + if (parsedPayto.targetType !== "iban") { + return { type: "skip" }; + } + const amountStr = parsedPayto.params["amount"]; + const iban = parsedPayto.targetPath; + const countryCode = iban.slice(0, 2); + const lines = [ + "SPC", // QRType + "0200", // Version + "1", // Character set (1: UTF-8) + iban, // Beneficiary IBAN + // Group: Beneficiary + "S", // Address type (S: structured) + parsedPayto.params["receiver-name"], // Beneficiary name + "", // street + "", // apt. nr. + "", // postal code + "", // town + countryCode, // Country + // Group: Ultimate Debtor (not used in version 0200) + "", // Ultimate Debtor Address type (S: structured) + "", // Ultimate Debtor name + "", // Ultimate Debtor street + "", // Ultimate Debtor apt. nr + "", // Ultimate Debtor postal code + "", // Ultimate Debtor town + "", // Ultimate Debtor country + // Group: Amount + Amounts.stringifyValue(amountStr, 2), // Amount + Amounts.currencyOf(amountStr), // Currency + // Group: Debtor + "", // Address type (S: structured) + "", // Debtor name + "", // Debtor street + "", // Debtor apt. nr + "", // Debtor postal code + "", // Debtor town + "", // Debtor country + // Group: Reference + "NON", // reference type + "", // Reference + // Group: Additional Information + parsedPayto.params["message"], // Unstructured data + "EPD", // End of payment data + ]; + + return { + type: "ok", + qrContent: lines.join("\n"), + }; +} + +/** + * See "Quick Response Code - Guidelines to + * Enable the Data Capture for the + * Initiation of a SEPA Credit Transfer". + */ +function encodePaytoAsEpcQr(paytoUri: string): EncodeResult { + const parsedPayto = parsePaytoUri(paytoUri); + if (!parsedPayto) { + throw Error("invalid payto URI"); + } + if (parsedPayto.targetType !== "iban") { + return { type: "skip" }; + } + const amountStr = parsedPayto.params["amount"]; + Amounts.stringifyValue; + const lines = [ + "BCD", // service tag + "002", // version + "1", // character set (1: UTF-8) + "SCT", // Identification + "", // optional BIC + parsedPayto.params["receiver-name"], // Beneficiary name + parsedPayto.targetPath, // Beneficiary IBAN + `${Amounts.currencyOf(amountStr)}${Amounts.stringifyValue(amountStr, 2)}`, // Amount + "", // AT-44 Purpose + parsedPayto.params["message"], // AT-05 Unstructured remittance information + ]; + + return { + type: "ok", + qrContent: lines.join("\n"), + }; +} + +/** + * Specification of a QR code that includes payment information. + */ +export interface QrCodeSpec { + /** + * Type of the QR code. + * + * Depending on the type, different visual styles + * might be applied. + */ + type: string; + + /** + * Content of the QR code that should be rendered. + */ + qrContent: string; +} + +/** + * Get applicable QR code specifications for the given payto URI. + */ +export function getQrCodesForPayto(paytoUri: string): QrCodeSpec[] { + const res: QrCodeSpec[] = []; + { + const qr = encodePaytoAsEpcQr(paytoUri); + if (qr.type == "ok") { + res.push({ + type: "epc-qr", + qrContent: qr.qrContent, + }); + } + } + { + const qr = encodePaytoAsSwissQrBill(paytoUri); + if (qr.type == "ok") { + res.push({ + type: "epc-qr", + qrContent: qr.qrContent, + }); + } + } + return res; +} diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts index 360d8b183..2c92d9295 100644 --- a/packages/taler-util/src/wallet-types.ts +++ b/packages/taler-util/src/wallet-types.ts @@ -55,6 +55,7 @@ import { } from "./index.js"; import { VersionMatchResult } from "./libtool-version.js"; import { PaytoString, PaytoUri, codecForPaytoString } from "./payto.js"; +import { QrCodeSpec } from "./qr.js"; import { AgeCommitmentProof } from "./taler-crypto.js"; import { TalerErrorCode } from "./taler-error-codes.js"; import { @@ -3425,3 +3426,16 @@ export const codecForGetDepositWireTypesForCurrencyRequest = export interface GetDepositWireTypesForCurrencyResponse { wireTypes: string[]; } + +export interface GetQrCodesForPaytoRequest { + paytoUri: string; +} + +export const codecForGetQrCodesForPaytoRequest = () => + buildCodecForObject<GetQrCodesForPaytoRequest>() + .property("paytoUri", codecForString()) + .build("GetQrCodesForPaytoRequest"); + +export interface GetQrCodesForPaytoResponse { + codes: QrCodeSpec[]; +} diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts index c54ec1360..ce8be2927 100644 --- a/packages/taler-wallet-core/src/wallet-api-types.ts +++ b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -81,6 +81,8 @@ import { GetExchangeTosResult, GetPlanForOperationRequest, GetPlanForOperationResponse, + GetQrCodesForPaytoRequest, + GetQrCodesForPaytoResponse, GetWithdrawalDetailsForAmountRequest, GetWithdrawalDetailsForUriRequest, HintNetworkAvailabilityRequest, @@ -269,6 +271,7 @@ export enum WalletApiOperation { HintNetworkAvailability = "hintNetworkAvailability", CanonicalizeBaseUrl = "canonicalizeBaseUrl", GetDepositWireTypesForCurrency = "getDepositWireTypesForCurrency", + GetQrCodesForPayto = "getQrCodesForPayto", TestingWaitTransactionsFinal = "testingWaitTransactionsFinal", TestingWaitRefreshesFinal = "testingWaitRefreshesFinal", TestingWaitTransactionState = "testingWaitTransactionState", @@ -999,6 +1002,12 @@ export type CanonicalizeBaseUrlOp = { response: CanonicalizeBaseUrlResponse; }; +export type GetQrCodesForPaytoOp = { + op: WalletApiOperation.GetQrCodesForPayto; + request: GetQrCodesForPaytoRequest; + response: GetQrCodesForPaytoResponse; +}; + // group: Database Management /** @@ -1382,6 +1391,7 @@ export type WalletOperations = { [WalletApiOperation.TestingResetAllRetries]: TestingResetAllRetriesOp; [WalletApiOperation.HintNetworkAvailability]: HintNetworkAvailabilityOp; [WalletApiOperation.GetDepositWireTypesForCurrency]: GetDepositWireTypesForCurrencyOp; + [WalletApiOperation.GetQrCodesForPayto]: GetQrCodesForPaytoOp; }; export type WalletCoreRequestType< diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index a58e3aff0..69f0e5f0b 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -107,6 +107,7 @@ import { codecForGetExchangeEntryByUrlRequest, codecForGetExchangeResourcesRequest, codecForGetExchangeTosRequest, + codecForGetQrCodesForPaytoRequest, codecForGetWithdrawalDetailsForAmountRequest, codecForGetWithdrawalDetailsForUri, codecForHintNetworkAvailabilityRequest, @@ -149,6 +150,7 @@ import { codecForValidateIbanRequest, codecForWithdrawTestBalance, getErrorDetailFromException, + getQrCodesForPayto, j2s, openPromise, parsePaytoUri, @@ -1061,6 +1063,12 @@ async function dispatchRequestInternal( const req = codecForPreparePayTemplateRequest().decode(payload); return preparePayForTemplate(wex, req); } + case WalletApiOperation.GetQrCodesForPayto: { + const req = codecForGetQrCodesForPaytoRequest().decode(payload); + return { + codes: getQrCodesForPayto(req.paytoUri), + }; + } case WalletApiOperation.ConfirmPay: { const req = codecForConfirmPayRequest().decode(payload); let transactionId; |