commit 301a9af06c7d39bf5b71116501c7970202bfd063
parent f0433106c1da705c9f76d8500c9918910ecb5240
Author: Antoine A <>
Date: Tue, 21 Jan 2025 14:25:12 +0100
magnet-bank: dev transfer cmd and magnet-bank payto
Diffstat:
17 files changed, 488 insertions(+), 212 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -2575,6 +2575,7 @@ dependencies = [
"rand",
"serde",
"serde_json",
+ "serde_path_to_error",
"serde_urlencoded",
"serde_with",
"sqlx",
diff --git a/common/taler-api/src/lib.rs b/common/taler-api/src/lib.rs
@@ -51,7 +51,7 @@ use taler_common::{
error_code::ErrorCode,
types::{
amount::Amount,
- payto::{GenericPaytoImpl, PaytoImpl},
+ payto::{AnyPayto, PaytoImpl},
},
};
use tokio::{
@@ -195,19 +195,19 @@ pub trait WireGatewayImpl<P: PaytoImpl>: Send + Sync {
&self,
page: Page,
status: Option<TransferState>,
- ) -> impl std::future::Future<Output = ApiResult<TransferList<GenericPaytoImpl>>> + Send;
+ ) -> impl std::future::Future<Output = ApiResult<TransferList<AnyPayto>>> + Send;
fn transfer_by_id(
&self,
id: u64,
- ) -> impl std::future::Future<Output = ApiResult<Option<TransferStatus<GenericPaytoImpl>>>> + Send;
+ ) -> impl std::future::Future<Output = ApiResult<Option<TransferStatus<AnyPayto>>>> + Send;
fn outgoing_history(
&self,
params: History,
- ) -> impl std::future::Future<Output = ApiResult<OutgoingHistory<GenericPaytoImpl>>> + Send;
+ ) -> impl std::future::Future<Output = ApiResult<OutgoingHistory<AnyPayto>>> + Send;
fn incoming_history(
&self,
params: History,
- ) -> impl std::future::Future<Output = ApiResult<IncomingHistory<GenericPaytoImpl>>> + Send;
+ ) -> impl std::future::Future<Output = ApiResult<IncomingHistory<AnyPayto>>> + Send;
fn add_incoming_reserve(
&self,
req: AddIncomingRequest<P>,
diff --git a/common/taler-api/tests/api.rs b/common/taler-api/tests/api.rs
@@ -23,7 +23,7 @@ use taler_common::{
error_code::ErrorCode,
types::{
amount::amount,
- payto::{payto, GenericPaytoImpl},
+ payto::{payto, AnyPayto},
url,
},
};
@@ -71,8 +71,7 @@ async fn config() {
#[tokio::test]
async fn transfer() {
let (server, _) = setup().await;
- transfer_routine::<GenericPaytoImpl>(&server, TransferState::success, &payto("payto://test"))
- .await;
+ transfer_routine::<AnyPayto>(&server, TransferState::success, &payto("payto://test")).await;
}
#[tokio::test]
@@ -80,7 +79,7 @@ async fn outgoing_history() {
let (server, _) = setup().await;
server.get("/history/outgoing").await.assert_no_content();
- routine_pagination::<OutgoingHistory<GenericPaytoImpl>, _>(
+ routine_pagination::<OutgoingHistory<AnyPayto>, _>(
&server,
"/history/outgoing",
|it| {
@@ -109,5 +108,5 @@ async fn outgoing_history() {
#[tokio::test]
async fn admin_add_incoming() {
let (server, _) = setup().await;
- admin_add_incoming_routine::<GenericPaytoImpl>(&server, &payto("payto://test")).await;
+ admin_add_incoming_routine::<AnyPayto>(&server, &payto("payto://test")).await;
}
diff --git a/common/taler-api/tests/common/db.rs b/common/taler-api/tests/common/db.rs
@@ -23,11 +23,7 @@ use taler_common::{
IncomingBankTransaction, OutgoingBankTransaction, TransferListStatus, TransferRequest,
TransferResponse, TransferState, TransferStatus,
},
- types::{
- amount::Amount,
- payto::{GenericPayto, GenericPaytoImpl},
- timestamp::Timestamp,
- },
+ types::{amount::Amount, payto::{AnyPayto, Payto}, timestamp::Timestamp},
};
use tokio::sync::watch::{Receiver, Sender};
@@ -53,7 +49,7 @@ pub enum TransferResult {
pub async fn transfer(
db: &PgPool,
- transfer: TransferRequest<GenericPaytoImpl>,
+ transfer: TransferRequest<AnyPayto>,
) -> sqlx::Result<TransferResult> {
sqlx::query(
"
@@ -87,7 +83,7 @@ pub async fn transfer_page(
status: &Option<TransferState>,
params: &Page,
currency: &str,
-) -> sqlx::Result<Vec<TransferListStatus<GenericPaytoImpl>>> {
+) -> sqlx::Result<Vec<TransferListStatus<AnyPayto>>> {
page(
db,
"transfer_id",
@@ -127,7 +123,7 @@ pub async fn transfer_by_id(
db: &PgPool,
id: u64,
currency: &str,
-) -> sqlx::Result<Option<TransferStatus<GenericPaytoImpl>>> {
+) -> sqlx::Result<Option<TransferStatus<AnyPayto>>> {
sqlx::query(
"
SELECT
@@ -163,7 +159,7 @@ pub async fn outgoing_page(
params: &History,
currency: &str,
listen: impl FnOnce() -> Receiver<i64>,
-) -> sqlx::Result<Vec<OutgoingBankTransaction<GenericPaytoImpl>>> {
+) -> sqlx::Result<Vec<OutgoingBankTransaction<AnyPayto>>> {
history(
db,
"transfer_id",
@@ -206,7 +202,7 @@ pub enum AddIncomingResult {
pub async fn add_incoming(
db: &PgPool,
amount: &Amount,
- debit_account: &GenericPayto,
+ debit_account: &Payto<AnyPayto>,
subject: &str,
timestamp: &Timestamp,
kind: IncomingType,
@@ -221,7 +217,7 @@ pub async fn add_incoming(
.bind(key.as_slice())
.bind(subject)
.bind_amount(amount)
- .bind(debit_account.raw())
+ .bind(debit_account .raw())
.bind_timestamp(timestamp)
.bind(kind)
.try_map(|r: PgRow| {
@@ -240,7 +236,7 @@ pub async fn incoming_page(
params: &History,
currency: &str,
listen: impl FnOnce() -> Receiver<i64>,
-) -> sqlx::Result<Vec<IncomingBankTransaction<GenericPaytoImpl>>> {
+) -> sqlx::Result<Vec<IncomingBankTransaction<AnyPayto>>> {
history(
db,
"incoming_transaction_id",
diff --git a/common/taler-api/tests/common/mod.rs b/common/taler-api/tests/common/mod.rs
@@ -33,7 +33,7 @@ use taler_common::{
},
error_code::ErrorCode,
types::{
- payto::{payto, GenericPaytoImpl},
+ payto::{payto, AnyPayto},
timestamp::Timestamp,
},
};
@@ -49,7 +49,7 @@ pub struct SampleState {
incoming_channel: Sender<i64>,
}
-impl WireGatewayImpl<GenericPaytoImpl> for SampleState {
+impl WireGatewayImpl<AnyPayto> for SampleState {
fn name(&self) -> &str {
"taler-wire-gateway"
}
@@ -62,10 +62,7 @@ impl WireGatewayImpl<GenericPaytoImpl> for SampleState {
None
}
- async fn transfer(
- &self,
- req: TransferRequest<GenericPaytoImpl>,
- ) -> ApiResult<TransferResponse> {
+ async fn transfer(&self, req: TransferRequest<AnyPayto>) -> ApiResult<TransferResponse> {
let result = db::transfer(&self.pool, req).await?;
match result {
db::TransferResult::Success(transfer_response) => Ok(transfer_response),
@@ -80,21 +77,18 @@ impl WireGatewayImpl<GenericPaytoImpl> for SampleState {
&self,
page: Page,
status: Option<TransferState>,
- ) -> ApiResult<TransferList<GenericPaytoImpl>> {
+ ) -> ApiResult<TransferList<AnyPayto>> {
Ok(TransferList {
transfers: db::transfer_page(&self.pool, &status, &page, &self.currency).await?,
debit_account: payto("payto://test"),
})
}
- async fn transfer_by_id(&self, id: u64) -> ApiResult<Option<TransferStatus<GenericPaytoImpl>>> {
+ async fn transfer_by_id(&self, id: u64) -> ApiResult<Option<TransferStatus<AnyPayto>>> {
Ok(db::transfer_by_id(&self.pool, id, &self.currency).await?)
}
- async fn outgoing_history(
- &self,
- params: History,
- ) -> ApiResult<OutgoingHistory<GenericPaytoImpl>> {
+ async fn outgoing_history(&self, params: History) -> ApiResult<OutgoingHistory<AnyPayto>> {
let txs = db::outgoing_page(&self.pool, ¶ms, &self.currency, || {
self.outgoing_channel.subscribe()
})
@@ -105,10 +99,7 @@ impl WireGatewayImpl<GenericPaytoImpl> for SampleState {
})
}
- async fn incoming_history(
- &self,
- params: History,
- ) -> ApiResult<IncomingHistory<GenericPaytoImpl>> {
+ async fn incoming_history(&self, params: History) -> ApiResult<IncomingHistory<AnyPayto>> {
let txs = db::incoming_page(&self.pool, ¶ms, &self.currency, || {
self.incoming_channel.subscribe()
})
@@ -121,7 +112,7 @@ impl WireGatewayImpl<GenericPaytoImpl> for SampleState {
async fn add_incoming_reserve(
&self,
- req: AddIncomingRequest<GenericPaytoImpl>,
+ req: AddIncomingRequest<AnyPayto>,
) -> ApiResult<AddIncomingResponse> {
let timestamp = Timestamp::now();
let res = db::add_incoming(
@@ -145,7 +136,7 @@ impl WireGatewayImpl<GenericPaytoImpl> for SampleState {
async fn add_incoming_kyc(
&self,
- req: AddKycauthRequest<GenericPaytoImpl>,
+ req: AddKycauthRequest<AnyPayto>,
) -> ApiResult<AddKycauthResponse> {
let timestamp = Timestamp::now();
let res = db::add_incoming(
diff --git a/common/taler-common/Cargo.toml b/common/taler-common/Cargo.toml
@@ -13,6 +13,7 @@ tempfile.workspace = true
jiff.workspace = true
serde.workspace = true
serde_json = { workspace = true, features = ["raw_value"] }
+serde_path_to_error.workspace = true
url.workspace = true
thiserror.workspace = true
fastrand.workspace = true
diff --git a/common/taler-common/src/config.rs b/common/taler-common/src/config.rs
@@ -26,7 +26,7 @@ use url::Url;
use crate::types::{
amount::{Amount, Currency},
- payto::GenericPayto,
+ payto::{AnyPayto, Payto},
};
pub mod parser {
@@ -721,7 +721,7 @@ impl<'cfg, 'arg> Section<'cfg, 'arg> {
}
/** Access [option] as payto */
- pub fn payto(&self, option: &'arg str) -> Value<'arg, GenericPayto> {
+ pub fn payto(&self, option: &'arg str) -> Value<'arg, Payto<AnyPayto>> {
self.parse("payto", option)
}
diff --git a/common/taler-common/src/types/amount.rs b/common/taler-common/src/types/amount.rs
@@ -206,7 +206,16 @@ impl FromStr for Decimal {
impl Display for Decimal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.write_fmt(format_args!("{}.{:08}", self.val, self.frac))
+ if self.frac == 0 {
+ f.write_fmt(format_args!("{}", self.val))
+ } else {
+ let num = format!("{:08}", self.frac);
+ f.write_fmt(format_args!(
+ "{}.{:08}",
+ self.val,
+ num.trim_end_matches('0')
+ ))
+ }
}
}
diff --git a/common/taler-common/src/types/payto.rs b/common/taler-common/src/types/payto.rs
@@ -14,17 +14,17 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+use serde::de::DeserializeOwned;
use std::{fmt::Debug, ops::Deref, str::FromStr};
use url::Url;
+use super::url;
+
/// Parse a payto URI, panic if malformed
pub fn payto<Impl: PaytoImpl>(url: impl AsRef<str>) -> Payto<Impl> {
url.as_ref().parse().expect("invalid payto")
}
-/// A generic payto that accept any kind
-pub type GenericPayto = Payto<GenericPaytoImpl>;
-
/// A payto implementation
pub trait PaytoImpl: Sized {
type ParseErr: std::error::Error;
@@ -32,13 +32,15 @@ pub trait PaytoImpl: Sized {
fn kind() -> &'static str;
fn parse(path: &mut std::str::Split<'_, char>) -> Result<Self, Self::ParseErr>;
+
+ fn path(&self) -> String;
}
-/// A generic payto implementation that accept any kind
+/// A generic payto that accept any kind
#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct GenericPaytoImpl {}
+pub struct AnyPayto {}
-impl PaytoImpl for GenericPaytoImpl {
+impl PaytoImpl for AnyPayto {
type ParseErr = std::convert::Infallible;
fn parse(path: &mut std::str::Split<'_, char>) -> Result<Self, Self::ParseErr> {
@@ -49,6 +51,10 @@ impl PaytoImpl for GenericPaytoImpl {
fn kind() -> &'static str {
""
}
+
+ fn path(&self) -> String {
+ "".to_owned()
+ }
}
/// RFC 8905 payto URI
@@ -65,10 +71,10 @@ impl<Impl: PaytoImpl> Payto<Impl> {
self.raw.as_str()
}
- pub fn generic(self) -> GenericPayto {
+ pub fn generic(self) -> Payto<AnyPayto> {
Payto {
raw: self.raw,
- parsed: GenericPaytoImpl {},
+ parsed: AnyPayto {},
}
}
}
@@ -87,16 +93,25 @@ impl<Impl: PaytoImpl> std::fmt::Display for Payto<Impl> {
}
}
-impl AsRef<Url> for GenericPayto {
+impl<Impl: PaytoImpl> AsRef<Url> for Payto<Impl> {
fn as_ref(&self) -> &Url {
&self.raw
}
}
+impl<Impl: PaytoImpl> From<Impl> for Payto<Impl> {
+ fn from(parsed: Impl) -> Self {
+ let raw = url(&format!("payto://{}{}", Impl::kind(), parsed.path()));
+ Self { raw, parsed }
+ }
+}
+
#[derive(Debug, thiserror::Error)]
pub enum ParsePaytoErr<E> {
- #[error("invalid URI: {0}")]
+ #[error("invalid payto URI: {0}")]
Url(#[from] url::ParseError),
+ #[error("malformed payto URI: {0}")]
+ Malformed(#[from] serde_path_to_error::Error<serde_urlencoded::de::Error>),
#[error("expected a payto URI got {0}")]
NotPayto(String),
#[error("unsupported payto kind, expected {0} got {1}")]
@@ -107,24 +122,84 @@ pub enum ParsePaytoErr<E> {
Custom(E),
}
+fn parse_payto<Impl: PaytoImpl, Query: DeserializeOwned>(
+ s: &str,
+) -> Result<(Payto<Impl>, Query), ParsePaytoErr<Impl::ParseErr>> {
+ // Parse url
+ let raw: Url = s.parse()?;
+ // Check scheme
+ if raw.scheme() != "payto" {
+ return Err(ParsePaytoErr::NotPayto(raw.scheme().to_owned()));
+ }
+ // Check domain
+ let domain = raw.domain().unwrap_or_default();
+ let kind = Impl::kind();
+ if !kind.is_empty() && domain != kind {
+ return Err(ParsePaytoErr::UnsupportedKind(kind, domain.to_owned()));
+ }
+ // Parse path
+ let mut segments = raw.path_segments().unwrap_or_else(|| "".split('/'));
+ let parsed = Impl::parse(&mut segments).map_err(ParsePaytoErr::Custom)?;
+ if segments.next().is_some() {
+ return Err(ParsePaytoErr::TooLong(kind));
+ }
+ // Parse query
+ let de = serde_urlencoded::Deserializer::new(url::form_urlencoded::parse(
+ raw.query().unwrap_or_default().as_bytes(),
+ ));
+ let query: Query = serde_path_to_error::deserialize(de)?;
+
+ Ok((Payto { raw, parsed }, query))
+}
+
impl<Impl: PaytoImpl> FromStr for Payto<Impl> {
type Err = ParsePaytoErr<Impl::ParseErr>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
- let raw: Url = s.parse()?;
- if raw.scheme() != "payto" {
- return Err(ParsePaytoErr::NotPayto(raw.scheme().to_owned()));
- }
- let domain = raw.domain().unwrap_or_default();
- let kind = Impl::kind();
- if !kind.is_empty() && domain != kind {
- return Err(ParsePaytoErr::UnsupportedKind(kind, domain.to_owned()));
- }
- let mut segments = raw.path_segments().unwrap_or_else(|| "".split('/'));
- let parsed = Impl::parse(&mut segments).map_err(ParsePaytoErr::Custom)?;
- if segments.next().is_some() {
- return Err(ParsePaytoErr::TooLong(kind));
+ #[derive(serde::Deserialize)]
+ struct Query {}
+ let (payto, _): (_, Query) = parse_payto(s)?;
+ Ok(payto)
+ }
+}
+
+/// RFC 8905 payto URI
+#[derive(
+ Debug, Clone, PartialEq, Eq, serde_with::DeserializeFromStr, serde_with::SerializeDisplay,
+)]
+pub struct FullPayto<Impl: PaytoImpl> {
+ payto: Payto<Impl>,
+ pub receiver_name: String,
+}
+
+impl<Impl: PaytoImpl> Deref for FullPayto<Impl> {
+ type Target = Payto<Impl>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.payto
+ }
+}
+
+impl<Impl: PaytoImpl> std::fmt::Display for FullPayto<Impl> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.payto, f)
+ }
+}
+
+impl<Impl: PaytoImpl> FromStr for FullPayto<Impl> {
+ type Err = ParsePaytoErr<Impl::ParseErr>;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ #[derive(serde::Deserialize)]
+ struct Query {
+ #[serde(rename = "receiver-name")]
+ receiver_name: String,
}
- Ok(Self { raw, parsed })
+ let (payto, query): (_, Query) = parse_payto(s)?;
+
+ Ok(Self {
+ payto,
+ receiver_name: query.receiver_name,
+ })
}
}
diff --git a/common/taler-common/src/types/timestamp.rs b/common/taler-common/src/types/timestamp.rs
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2024 Taler Systems SA
+ Copyright (C) 2024-2025 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -14,6 +14,8 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+use std::fmt::Display;
+
use serde::{de::Error, ser::SerializeStruct, Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
@@ -103,6 +105,15 @@ impl Serialize for Timestamp {
}
}
+impl Display for Timestamp {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Timestamp::Never => f.write_str("never"),
+ Timestamp::Time(timestamp) => timestamp.fmt(f),
+ }
+ }
+}
+
impl From<jiff::Timestamp> for Timestamp {
fn from(time: jiff::Timestamp) -> Self {
Self::Time(time)
diff --git a/wire-gateway/magnet-bank/src/db.rs b/wire-gateway/magnet-bank/src/db.rs
@@ -29,13 +29,13 @@ use taler_common::{
},
types::{
amount::Amount,
- payto::{GenericPayto, GenericPaytoImpl},
+ payto::{AnyPayto, Payto},
timestamp::Timestamp,
},
};
use tokio::sync::watch::{Receiver, Sender};
-use crate::{constant::CURRENCY, MagnetPayto, MagnetPaytoImpl};
+use crate::{constant::CURRENCY, MagnetPayto};
pub async fn notification_listener(
pool: PgPool,
@@ -65,24 +65,50 @@ pub struct TxIn {
pub code: u64,
pub amount: Amount,
pub subject: String,
- pub debit_payto: MagnetPayto,
+ pub debtor: Payto<MagnetPayto>,
pub timestamp: Timestamp,
}
+impl Display for TxIn {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let TxIn {
+ code,
+ amount,
+ subject,
+ debtor,
+ timestamp,
+ } = self;
+ write!(f, "{timestamp} {amount} {code} {debtor} '{subject}'")
+ }
+}
+
#[derive(Debug, Clone)]
pub struct TxOut {
pub code: u64,
pub amount: Amount,
pub subject: String,
- pub credit_payto: MagnetPayto,
+ pub creditor: Payto<MagnetPayto>,
pub timestamp: Timestamp,
}
+impl Display for TxOut {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let TxOut {
+ code,
+ amount,
+ subject,
+ creditor,
+ timestamp,
+ } = self;
+ write!(f, "{timestamp} {amount} {code} {creditor} '{subject}'")
+ }
+}
+
#[derive(Debug, Clone)]
pub struct TxInAdmin {
pub amount: Amount,
pub subject: String,
- pub debit_payto: MagnetPayto,
+ pub debit_payto: Payto<MagnetPayto>,
pub timestamp: Timestamp,
pub metadata: IncomingSubject,
}
@@ -105,7 +131,7 @@ pub struct Initiated {
pub id: u64,
pub amount: Amount,
pub subject: String,
- pub creditor: GenericPayto,
+ pub creditor: Payto<MagnetPayto>,
}
impl Display for Initiated {
@@ -175,7 +201,7 @@ pub async fn register_tx_in(
.bind(tx.code as i64)
.bind_amount(&tx.amount)
.bind(&tx.subject)
- .bind(tx.debit_payto.raw())
+ .bind(tx.debtor.raw())
.bind_timestamp(&tx.timestamp)
.bind(subject.as_ref().map(|it| it.ty()))
.bind(subject.as_ref().map(|it| it.key()))
@@ -208,7 +234,7 @@ pub async fn register_tx_out(
.bind(tx.code as i64)
.bind_amount(&tx.amount)
.bind(&tx.subject)
- .bind(tx.credit_payto.raw())
+ .bind(tx.creditor.raw())
.bind_timestamp(&tx.timestamp)
.bind(subject.as_ref().map(|it| it.0.as_ref()))
.bind(subject.as_ref().map(|it| it.1.as_str()))
@@ -232,7 +258,7 @@ pub enum TransferResult {
pub async fn make_transfer<'a>(
db: impl PgExecutor<'a>,
- req: &TransferRequest<MagnetPaytoImpl>,
+ req: &TransferRequest<MagnetPayto>,
timestamp: &Timestamp,
) -> sqlx::Result<TransferResult> {
let subject = format!("{} {}", req.wtid, req.exchange_base_url);
@@ -269,7 +295,7 @@ pub async fn transfer_page<'a>(
db: impl PgExecutor<'a>,
status: &Option<TransferState>,
params: &Page,
-) -> sqlx::Result<Vec<TransferListStatus<GenericPaytoImpl>>> {
+) -> sqlx::Result<Vec<TransferListStatus<AnyPayto>>> {
page(
db,
"initiated_id",
@@ -311,7 +337,7 @@ pub async fn outgoing_history(
db: &PgPool,
params: &History,
listen: impl FnOnce() -> Receiver<i64>,
-) -> sqlx::Result<Vec<OutgoingBankTransaction<GenericPaytoImpl>>> {
+) -> sqlx::Result<Vec<OutgoingBankTransaction<AnyPayto>>> {
history(
db,
"tx_out_id",
@@ -352,7 +378,7 @@ pub async fn incoming_history(
db: &PgPool,
params: &History,
listen: impl FnOnce() -> Receiver<i64>,
-) -> sqlx::Result<Vec<IncomingBankTransaction<GenericPaytoImpl>>> {
+) -> sqlx::Result<Vec<IncomingBankTransaction<AnyPayto>>> {
history(
db,
"tx_in_id",
@@ -403,7 +429,7 @@ pub async fn incoming_history(
pub async fn transfer_by_id<'a>(
db: impl PgExecutor<'a>,
id: u64,
-) -> sqlx::Result<Option<TransferStatus<GenericPaytoImpl>>> {
+) -> sqlx::Result<Option<TransferStatus<AnyPayto>>> {
sqlx::query(
"
SELECT
@@ -559,7 +585,7 @@ mod test {
code: code,
amount: amount("EUR:10"),
subject: "subject".to_owned(),
- debit_payto: payto("payto://magnet-bank/todo"),
+ debtor: payto("payto://magnet-bank/todo"),
timestamp: Timestamp::now_stable(),
};
// Insert
@@ -743,7 +769,7 @@ mod test {
code,
amount: amount("EUR:10"),
subject: "subject".to_owned(),
- credit_payto: payto("payto://magnet-bank/todo"),
+ creditor: payto("payto://magnet-bank/todo"),
timestamp: Timestamp::now_stable(),
};
// Insert
diff --git a/wire-gateway/magnet-bank/src/dev.rs b/wire-gateway/magnet-bank/src/dev.rs
@@ -15,9 +15,15 @@
*/
use clap::ValueEnum;
+use jiff::Zoned;
use taler_common::{
config::Config,
- types::{payto::payto, timestamp::Timestamp},
+ types::{
+ amount::Amount,
+ payto::{payto, FullPayto, Payto},
+ timestamp::Timestamp,
+ url,
+ },
};
use tracing::info;
@@ -26,6 +32,7 @@ use crate::{
db::{TxIn, TxOut},
keys,
magnet::{AuthClient, Direction},
+ MagnetPayto,
};
#[derive(Debug, Clone, PartialEq, Eq, ValueEnum)]
@@ -42,11 +49,20 @@ pub enum DevCmd {
/// Print account info
Accounts,
Tx {
- #[clap(long, short)]
- account: String,
+ account: Payto<MagnetPayto>,
#[clap(long, short, value_enum, default_value_t = DirArg::Both)]
direction: DirArg,
},
+ Transfer {
+ #[clap(long)]
+ debtor: Payto<MagnetPayto>,
+ #[clap(long)]
+ creditor: FullPayto<MagnetPayto>,
+ #[clap(long)]
+ amount: Amount,
+ #[clap(long)]
+ subject: String,
+ },
}
pub async fn dev(cfg: Config, cmd: DevCmd) -> anyhow::Result<()> {
@@ -57,10 +73,17 @@ pub async fn dev(cfg: Config, cmd: DevCmd) -> anyhow::Result<()> {
match cmd {
DevCmd::Accounts => {
let res = client.list_accounts().await?;
- dbg!(res);
+ for partner in res.partners {
+ for account in partner.bank_accounts {
+ let mut payto = url(&format!("payto://magnet-bank/{}", account.number));
+ payto
+ .query_pairs_mut()
+ .append_pair("receiver-name", &partner.partner.name);
+ info!("{} {} {payto}", account.code, account.currency.symbol);
+ }
+ }
}
DevCmd::Tx { account, direction } => {
- let client = client.account(&account);
let dir = match direction {
DirArg::Incoming => Direction::Incoming,
DirArg::Outgoing => Direction::Outgoing,
@@ -69,7 +92,9 @@ pub async fn dev(cfg: Config, cmd: DevCmd) -> anyhow::Result<()> {
// Register incoming
let mut next = None;
loop {
- let page = client.page_tx(dir, 5, &next, &None).await?;
+ let page = client
+ .page_tx(dir, 5, &account.account, &next, &None)
+ .await?;
next = page.next;
for item in page.list {
let tx = item.tx;
@@ -79,23 +104,20 @@ pub async fn dev(cfg: Config, cmd: DevCmd) -> anyhow::Result<()> {
code: tx.code,
amount: amount.parse().unwrap(),
subject: tx.subject,
- debit_payto: payto("payto://magnet-bank/todo"),
+ debtor: payto("payto://magnet-bank/todo"),
timestamp: Timestamp::from(tx.value_date),
};
- info!("incoming {} '{}'", tx.amount, tx.subject);
+ info!("in {tx}");
} else {
let amount = format!("{}:{}", tx.currency, -tx.amount);
- let tx_out = TxOut {
+ let tx = TxOut {
code: tx.code,
amount: amount.parse().unwrap(),
subject: tx.subject,
- credit_payto: payto("payto://magnet-bank/todo"),
+ creditor: payto("payto://magnet-bank/todo"),
timestamp: Timestamp::from(tx.value_date),
};
- info!(
- "outgoing {} {} {} '{}' {:?}",
- tx_out.code, tx.tx_date, tx_out.amount, tx_out.subject, tx.status
- );
+ info!("out {tx}");
}
}
if next.is_none() {
@@ -103,6 +125,37 @@ pub async fn dev(cfg: Config, cmd: DevCmd) -> anyhow::Result<()> {
}
}
}
+ DevCmd::Transfer {
+ debtor,
+ creditor,
+ amount,
+ subject,
+ } => {
+ let debtor = client.account(&debtor.account).await?;
+ let now = Zoned::now();
+ let date = now.date();
+
+ let init = client
+ .init_tx(
+ debtor.code,
+ amount.val as f64,
+ &subject,
+ date,
+ &creditor.receiver_name,
+ &creditor.account,
+ )
+ .await?;
+ client
+ .sign_tx(
+ &keys.signing_key,
+ &debtor.number,
+ init.code,
+ init.amount,
+ date,
+ &creditor.account,
+ )
+ .await?;
+ }
}
Ok(())
}
diff --git a/wire-gateway/magnet-bank/src/lib.rs b/wire-gateway/magnet-bank/src/lib.rs
@@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-use taler_common::types::payto::{Payto, PaytoImpl};
+use taler_common::types::payto::PaytoImpl;
pub mod config;
pub mod constant;
@@ -24,20 +24,18 @@ pub mod keys;
pub mod magnet;
pub mod wire_gateway;
-pub type MagnetPayto = Payto<MagnetPaytoImpl>;
-
#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct MagnetPaytoImpl {
- pub account_number: String,
+pub struct MagnetPayto {
+ pub account: String,
}
#[derive(Debug, thiserror::Error)]
pub enum MagnetPaytoErr {
#[error("missing Magnet Bank account number in path")]
- MissingAccountNumber,
+ MissingAccount,
}
-impl PaytoImpl for MagnetPaytoImpl {
+impl PaytoImpl for MagnetPayto {
type ParseErr = MagnetPaytoErr;
fn kind() -> &'static str {
@@ -45,9 +43,13 @@ impl PaytoImpl for MagnetPaytoImpl {
}
fn parse(path: &mut std::str::Split<'_, char>) -> Result<Self, Self::ParseErr> {
- let account_number = path.next().ok_or(MagnetPaytoErr::MissingAccountNumber)?;
+ let account = path.next().ok_or(MagnetPaytoErr::MissingAccount)?;
Ok(Self {
- account_number: account_number.to_owned(),
+ account: account.to_owned(),
})
}
+
+ fn path(&self) -> String {
+ format!("/{}", self.account)
+ }
}
diff --git a/wire-gateway/magnet-bank/src/magnet.rs b/wire-gateway/magnet-bank/src/magnet.rs
@@ -16,7 +16,12 @@
use base64::{prelude::BASE64_STANDARD, Engine};
use error::ApiResult;
-use p256::{ecdsa::SigningKey, PublicKey};
+use jiff::Timestamp;
+use p256::{
+ ecdsa::{signature::Signer as _, DerSignature, SigningKey},
+ PublicKey,
+};
+use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use spki::EncodePublicKey;
use taler_common::types::amount;
@@ -26,7 +31,7 @@ use crate::magnet::{error::MagnetBuilder, oauth::OAuthBuilder};
pub mod error;
mod oauth;
-#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Token {
#[serde(rename = "oauth_token")]
pub key: String,
@@ -34,13 +39,13 @@ pub struct Token {
pub secret: String,
}
-#[derive(serde::Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
pub struct TokenAuth {
pub oauth_token: String,
pub oauth_verifier: String,
}
-#[derive(serde::Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
pub struct Consumer {
#[serde(rename = "consumerKey")]
pub key: String,
@@ -52,7 +57,7 @@ pub struct Consumer {
pub lifetime: u64,
}
-#[derive(serde::Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
pub struct TokenInfo {
#[serde(rename = "keszult")]
pub created: jiff::Timestamp,
@@ -65,7 +70,7 @@ pub struct TokenInfo {
pub authenticated: bool,
}
-#[derive(serde::Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
pub struct SmsCodeSubmission {
#[serde(rename = "csatorna")]
pub channel: String,
@@ -73,7 +78,7 @@ pub struct SmsCodeSubmission {
pub sent_to: Vec<String>,
}
-#[derive(serde::Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
pub struct ScaResult {
#[serde(rename = "csatorna")]
pub channel: String,
@@ -81,7 +86,7 @@ pub struct ScaResult {
pub sent_to: Vec<String>,
}
-#[derive(serde::Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
pub struct Partner {
#[serde(rename = "megnevezes")]
pub name: String,
@@ -93,61 +98,61 @@ pub struct Partner {
pub status: String, // TODO enum
}
-#[derive(serde::Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
pub struct AccountType {
#[serde(rename = "kod")]
- code: u64,
+ pub code: u64,
#[serde(rename = "megnevezes")]
- name: String,
+ pub name: String,
}
-#[derive(serde::Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
pub struct Currency {
#[serde(rename = "jel")]
- symbol: amount::Currency,
+ pub symbol: amount::Currency,
#[serde(rename = "megnevezes")]
- name: String,
+ pub name: String,
}
-#[derive(serde::Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
pub struct Account {
#[serde(rename = "alapertelmezett")]
- default: bool,
+ pub default: bool,
#[serde(rename = "bankszamlaTipus")]
- ty: AccountType,
+ pub ty: AccountType,
#[serde(rename = "deviza")]
- currency: Currency,
+ pub currency: Currency,
#[serde(rename = "ibanSzamlaszam")]
- iban: String,
+ pub iban: String,
#[serde(rename = "kod")]
- code: u64,
+ pub code: u64,
#[serde(rename = "szamlaszam")]
- number: String,
+ pub number: String,
#[serde(rename = "tulajdonosKod")]
- owner_code: u64,
+ pub owner_code: u64,
#[serde(rename = "lakossagi")]
- resident: bool,
+ pub resident: bool,
#[serde(rename = "megnevezes")]
- name: Option<String>,
- partner: Partner,
+ pub name: Option<String>,
+ pub partner: Partner,
}
-#[derive(serde::Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
pub struct PartnerAccounts {
- partner: Partner,
+ pub partner: Partner,
#[serde(rename = "bankszamlaList")]
- bank_accounts: Vec<Account>,
+ pub bank_accounts: Vec<Account>,
#[serde(rename = "kertJogosultsag")]
- requested_permission: u64,
+ pub requested_permission: u64,
}
-#[derive(serde::Deserialize, Debug)]
+#[derive(Debug, Deserialize)]
pub struct PartnerList {
#[serde(rename = "partnerSzamlaList")]
- parteners: Vec<PartnerAccounts>,
+ pub partners: Vec<PartnerAccounts>,
}
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum TxStatus {
#[serde(rename = "G")]
ToBeRecorded,
@@ -171,7 +176,7 @@ pub enum TxStatus {
UnderReview,
}
-#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Direction {
#[serde(rename = "T")]
Outgoing,
@@ -181,7 +186,50 @@ pub enum Direction {
Both,
}
-#[derive(Debug, serde::Deserialize)]
+#[derive(Debug, Deserialize)]
+pub struct TxInfo {
+ #[serde(rename = "alairas1idopont")]
+ pub first_signature: Option<Timestamp>,
+ #[serde(rename = "alairas2idopont")]
+ pub second_signature: Option<Timestamp>,
+ #[serde(rename = "alairo1")]
+ pub first_signatory: Option<Partner>,
+ #[serde(rename = "alairo2")]
+ pub second_signatory: Option<Partner>,
+ #[serde(rename = "kod")]
+ pub code: u64,
+ #[serde(rename = "bankszamla")]
+ pub account: Account,
+ #[serde(rename = "deviza")]
+ pub currency: Currency,
+ #[serde(rename = "eszamla")]
+ pub counter_account: String,
+ #[serde(rename = "epartner")]
+ pub counter_name: String,
+ #[serde(rename = "statusz")]
+ pub status: TxStatus,
+ #[serde(rename = "osszegSigned")]
+ pub amount: f64,
+ #[serde(rename = "reszteljesites")]
+ pub partial_execution: bool,
+ #[serde(rename = "sorbaallitas")]
+ pub queued: bool,
+ pub eam: Option<u64>,
+}
+
+#[derive(Debug, Deserialize)]
+struct TxInfoWrapper {
+ #[serde(rename = "tranzakcio")]
+ info: TxInfo,
+}
+
+#[derive(Debug, Deserialize)]
+struct AccountWrapper {
+ #[serde(rename = "bankszamla")]
+ account: Account,
+}
+
+#[derive(Debug, Deserialize)]
pub struct Transaction {
#[serde(rename = "kod")]
pub code: u64,
@@ -206,10 +254,10 @@ pub struct Transaction {
#[serde(rename = "eszamla")]
pub debtor: String,
#[serde(rename = "tranzakcioTipus")]
- pub ty: String,
+ pub ty: Option<String>,
}
-#[derive(Debug, serde::Deserialize)]
+#[derive(Debug, Deserialize)]
pub struct Next {
#[serde(rename = "next")]
pub next_id: u64,
@@ -217,7 +265,7 @@ pub struct Next {
pub next_type: String,
}
-#[derive(Debug, serde::Deserialize)]
+#[derive(Debug, Deserialize)]
pub struct TransactionPage {
#[serde(flatten)]
pub next: Option<Next>,
@@ -225,7 +273,7 @@ pub struct TransactionPage {
pub list: Vec<TransactionWrapper>,
}
-#[derive(Debug, serde::Deserialize)]
+#[derive(Debug, Deserialize)]
pub struct TransactionWrapper {
#[serde(rename = "tranzakcioDto")]
pub tx: Transaction,
@@ -298,7 +346,7 @@ pub struct ApiClient<'a> {
access: &'a Token,
}
-impl<'a> ApiClient<'a> {
+impl ApiClient<'_> {
fn join(&self, path: &str) -> reqwest::Url {
self.api_url.join(path).unwrap()
}
@@ -356,40 +404,27 @@ impl<'a> ApiClient<'a> {
.await
}
- pub fn account(self, account: &'a str) -> AccountClient<'a> {
- AccountClient {
- client: self.client,
- api_url: self.api_url,
- consumer: self.consumer,
- access: self.access,
- account,
- }
- }
-}
-
-pub struct AccountClient<'a> {
- client: &'a reqwest::Client,
- api_url: &'a reqwest::Url,
- consumer: &'a Token,
- access: &'a Token,
- account: &'a str,
-}
-
-impl AccountClient<'_> {
- fn join(&self, path: &str) -> reqwest::Url {
- self.api_url.join(path).unwrap()
+ pub async fn account(&self, account: &str) -> ApiResult<Account> {
+ Ok(self
+ .client
+ .get(self.join(&format!("/RESTApi/resources/v2/bankszamla/{account}")))
+ .oauth(self.consumer, Some(self.access), None)
+ .await
+ .magnet_json::<AccountWrapper>()
+ .await?
+ .account)
}
pub async fn page_tx(
&self,
direction: Direction,
limit: u16,
+ account: &str,
next: &Option<Next>,
status: &Option<TxStatus>,
) -> ApiResult<TransactionPage> {
let mut req = self.client.get(self.join(&format!(
- "/RESTApi/resources/v2/tranzakcio/paginator/{}/{limit}",
- self.account
+ "/RESTApi/resources/v2/tranzakcio/paginator/{account}/{limit}"
)));
if let Some(next) = next {
req = req
@@ -409,4 +444,92 @@ impl AccountClient<'_> {
.magnet_call()
.await
}
+
+ pub async fn init_tx(
+ &self,
+ account_code: u64,
+ amount: f64,
+ subject: &str,
+ date: jiff::civil::Date,
+ creditor_name: &str,
+ creditor_account: &str,
+ ) -> ApiResult<TxInfo> {
+ #[derive(Serialize)]
+ struct Req<'a> {
+ #[serde(rename = "bankszamlaKod")]
+ account_code: u64,
+ #[serde(rename = "osszeg")]
+ amount: f64,
+ #[serde(rename = "kozlemeny")]
+ subject: &'a str,
+ #[serde(rename = "ertekNap")]
+ date: jiff::civil::Date,
+ #[serde(rename = "ellenpartner")]
+ creditor_name: &'a str,
+ #[serde(rename = "ellenszamla")]
+ creditor_account: &'a str,
+ }
+
+ Ok(self
+ .client
+ .post(self.join("/RESTApi/resources/v2/esetiatutalas"))
+ .json(&Req {
+ account_code,
+ amount,
+ subject,
+ date,
+ creditor_name,
+ creditor_account,
+ })
+ .oauth(self.consumer, Some(self.access), None)
+ .await
+ .magnet_call::<TxInfoWrapper>()
+ .await?
+ .info)
+ }
+
+ pub async fn sign_tx(
+ &self,
+ signing_key: &SigningKey,
+ account: &str,
+ tx_code: u64,
+ amount: f64,
+ date: jiff::civil::Date,
+ creditor: &str,
+ ) -> ApiResult<TxInfo> {
+ #[derive(Serialize)]
+ struct Req<'a> {
+ #[serde(rename = "tranzakcioKod")]
+ tx_code: u64,
+ #[serde(rename = "forrasszamla")]
+ debtor: &'a str,
+ #[serde(rename = "ellenszamla")]
+ creditor: &'a str,
+ #[serde(rename = "osszeg")]
+ amount: f64,
+ #[serde(rename = "ertekNap")]
+ date: jiff::civil::Date,
+ signature: &'a str,
+ }
+
+ let content: String = format!("{tx_code};{account};{creditor};{amount};{date};");
+ let signature: DerSignature = signing_key.sign(content.as_bytes());
+ let encoded = BASE64_STANDARD.encode(signature.as_bytes());
+ Ok(self
+ .client
+ .put(self.join("/RESTApi/resources/v2/tranzakcio/alairas"))
+ .json(&Req {
+ tx_code,
+ debtor: account,
+ creditor,
+ amount,
+ date,
+ signature: &encoded,
+ })
+ .oauth(self.consumer, Some(self.access), None)
+ .await
+ .magnet_call::<TxInfoWrapper>()
+ .await?
+ .info)
+ }
}
diff --git a/wire-gateway/magnet-bank/src/magnet/error.rs b/wire-gateway/magnet-bank/src/magnet/error.rs
@@ -20,21 +20,13 @@ use thiserror::Error;
use tracing::error;
#[derive(Deserialize, Debug)]
-pub struct MagnetResponse<T> {
- timestamp: jiff::civil::DateTime,
- #[serde(flatten)]
- body: MagnetBody<T>,
+struct Header {
+ #[serde(alias = "errorCode")]
+ pub error_code: Option<u16>,
}
#[derive(Deserialize, Debug)]
-pub struct Empty {}
-
-#[derive(Deserialize, Debug)]
-#[serde(untagged)]
-pub enum MagnetBody<T> {
- Error(MagnetError),
- Ok(T),
-}
+struct Empty {}
#[derive(Deserialize, Error, Debug)]
#[error("{error_code} {short_message} '{long_message}'")]
@@ -128,13 +120,14 @@ async fn error_handling(res: reqwest::Result<Response>) -> ApiResult<String> {
/** Parse magnet JSON response */
async fn magnet_json<T: DeserializeOwned>(res: reqwest::Result<Response>) -> ApiResult<T> {
let body = error_handling(res).await?;
- let deserializer = &mut serde_json::Deserializer::from_str(&body);
-
- let body: MagnetResponse<T> =
- serde_path_to_error::deserialize(deserializer).map_err(ApiError::Json)?;
- match body.body {
- MagnetBody::Error(e) => Err(ApiError::Magnet(e)),
- MagnetBody::Ok(t) => Ok(t),
+ fn parse<T: DeserializeOwned>(str: &str) -> ApiResult<T> {
+ let deserializer = &mut serde_json::Deserializer::from_str(str);
+ serde_path_to_error::deserialize(deserializer).map_err(ApiError::Json)
+ }
+ let header: Header = parse(&body)?;
+ match header.error_code {
+ Some(_) => Err(ApiError::Magnet(parse(&body)?)),
+ None => parse(&body),
}
}
diff --git a/wire-gateway/magnet-bank/src/wire_gateway.rs b/wire-gateway/magnet-bank/src/wire_gateway.rs
@@ -28,19 +28,22 @@ use taler_common::{
TransferState, TransferStatus,
},
error_code::ErrorCode,
- types::{payto::GenericPaytoImpl, timestamp::Timestamp},
+ types::{
+ payto::{AnyPayto, Payto},
+ timestamp::Timestamp,
+ },
};
use tokio::sync::watch::Sender;
use crate::{
constant::CURRENCY,
db::{self, AddIncomingResult, TxInAdmin},
- MagnetPayto, MagnetPaytoImpl,
+ MagnetPayto,
};
pub struct MagnetWireGateway {
pub pool: sqlx::PgPool,
- pub payto: MagnetPayto,
+ pub payto: Payto<MagnetPayto>,
pub in_channel: Sender<i64>,
pub taler_in_channel: Sender<i64>,
pub out_channel: Sender<i64>,
@@ -48,7 +51,7 @@ pub struct MagnetWireGateway {
}
impl MagnetWireGateway {
- pub async fn start(pool: sqlx::PgPool, payto: MagnetPayto) -> Self {
+ pub async fn start(pool: sqlx::PgPool, payto: Payto<MagnetPayto>) -> Self {
let in_channel = Sender::new(0);
let taler_in_channel = Sender::new(0);
let out_channel = Sender::new(0);
@@ -72,7 +75,7 @@ impl MagnetWireGateway {
}
}
-impl WireGatewayImpl<MagnetPaytoImpl> for MagnetWireGateway {
+impl WireGatewayImpl<MagnetPayto> for MagnetWireGateway {
fn name(&self) -> &str {
"magnet-bank"
}
@@ -85,7 +88,7 @@ impl WireGatewayImpl<MagnetPaytoImpl> for MagnetWireGateway {
None
}
- async fn transfer(&self, req: TransferRequest<MagnetPaytoImpl>) -> ApiResult<TransferResponse> {
+ async fn transfer(&self, req: TransferRequest<MagnetPayto>) -> ApiResult<TransferResponse> {
let result = db::make_transfer(&self.pool, &req, &Timestamp::now()).await?;
match result {
db::TransferResult::Success { id, timestamp } => Ok(TransferResponse {
@@ -104,21 +107,18 @@ impl WireGatewayImpl<MagnetPaytoImpl> for MagnetWireGateway {
&self,
page: Page,
status: Option<TransferState>,
- ) -> ApiResult<TransferList<GenericPaytoImpl>> {
+ ) -> ApiResult<TransferList<AnyPayto>> {
Ok(TransferList {
transfers: db::transfer_page(&self.pool, &status, &page).await?,
debit_account: self.payto.clone().generic(),
})
}
- async fn transfer_by_id(&self, id: u64) -> ApiResult<Option<TransferStatus<GenericPaytoImpl>>> {
+ async fn transfer_by_id(&self, id: u64) -> ApiResult<Option<TransferStatus<AnyPayto>>> {
Ok(db::transfer_by_id(&self.pool, id).await?)
}
- async fn outgoing_history(
- &self,
- params: History,
- ) -> ApiResult<OutgoingHistory<GenericPaytoImpl>> {
+ async fn outgoing_history(&self, params: History) -> ApiResult<OutgoingHistory<AnyPayto>> {
Ok(OutgoingHistory {
outgoing_transactions: db::outgoing_history(&self.pool, ¶ms, || {
self.taler_out_channel.subscribe()
@@ -128,10 +128,7 @@ impl WireGatewayImpl<MagnetPaytoImpl> for MagnetWireGateway {
})
}
- async fn incoming_history(
- &self,
- params: History,
- ) -> ApiResult<IncomingHistory<GenericPaytoImpl>> {
+ async fn incoming_history(&self, params: History) -> ApiResult<IncomingHistory<AnyPayto>> {
Ok(IncomingHistory {
incoming_transactions: db::incoming_history(&self.pool, ¶ms, || {
self.taler_in_channel.subscribe()
@@ -143,7 +140,7 @@ impl WireGatewayImpl<MagnetPaytoImpl> for MagnetWireGateway {
async fn add_incoming_reserve(
&self,
- req: AddIncomingRequest<MagnetPaytoImpl>,
+ req: AddIncomingRequest<MagnetPayto>,
) -> ApiResult<AddIncomingResponse> {
let res = db::register_tx_in_admin(
&self.pool,
@@ -170,7 +167,7 @@ impl WireGatewayImpl<MagnetPaytoImpl> for MagnetWireGateway {
async fn add_incoming_kyc(
&self,
- req: AddKycauthRequest<MagnetPaytoImpl>,
+ req: AddKycauthRequest<MagnetPayto>,
) -> ApiResult<AddKycauthResponse> {
let res = db::register_tx_in_admin(
&self.pool,
diff --git a/wire-gateway/magnet-bank/tests/api.rs b/wire-gateway/magnet-bank/tests/api.rs
@@ -16,7 +16,7 @@
use std::sync::Arc;
-use magnet_bank::{db, wire_gateway::MagnetWireGateway, MagnetPaytoImpl};
+use magnet_bank::{db, wire_gateway::MagnetWireGateway, MagnetPayto};
use sqlx::PgPool;
use taler_api::{auth::AuthMethod, standard_layer, subject::OutgoingSubject};
use taler_common::{
@@ -47,7 +47,7 @@ async fn setup() -> (TestServer, PgPool) {
#[tokio::test]
async fn transfer() {
let (server, _) = setup().await;
- transfer_routine::<MagnetPaytoImpl>(
+ transfer_routine::<MagnetPayto>(
&server,
TransferState::pending,
&payto("payto://magnet-bank/todo"),
@@ -59,7 +59,7 @@ async fn transfer() {
async fn outgoing_history() {
let (server, pool) = setup().await;
server.get("/history/outgoing").await.assert_no_content();
- routine_pagination::<OutgoingHistory<MagnetPaytoImpl>, _>(
+ routine_pagination::<OutgoingHistory<MagnetPayto>, _>(
&server,
"/history/outgoing",
|it| {
@@ -78,7 +78,7 @@ async fn outgoing_history() {
code: i as u64,
amount: amount("EUR:10"),
subject: "subject".to_owned(),
- credit_payto: payto("payto://magnet-bank/todo"),
+ creditor: payto("payto://magnet-bank/todo"),
timestamp: Timestamp::now_stable(),
},
&Some(OutgoingSubject(
@@ -97,6 +97,5 @@ async fn outgoing_history() {
#[tokio::test]
async fn admin_add_incoming() {
let (server, _) = setup().await;
- admin_add_incoming_routine::<MagnetPaytoImpl>(&server, &payto("payto://magnet-bank/todo"))
- .await;
+ admin_add_incoming_routine::<MagnetPayto>(&server, &payto("payto://magnet-bank/todo")).await;
}