removed legacy from bff

This commit is contained in:
Stephan D
2026-02-24 21:18:23 +01:00
parent a998b59072
commit da11be526a
26 changed files with 343 additions and 273 deletions

View File

@@ -1,20 +0,0 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/money.dart';
part 'network_fee.g.dart';
@JsonSerializable()
class NetworkFeeDTO {
final MoneyDTO? networkFee;
final String? estimationContext;
const NetworkFeeDTO({
this.networkFee,
this.estimationContext,
});
factory NetworkFeeDTO.fromJson(Map<String, dynamic> json) => _$NetworkFeeDTOFromJson(json);
Map<String, dynamic> toJson() => _$NetworkFeeDTOToJson(this);
}

View File

@@ -1,35 +1,21 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/payment/fee_line.dart';
import 'package:pshared/data/dto/payment/fx_quote.dart';
import 'package:pshared/data/dto/money.dart';
import 'package:pshared/data/dto/payment/network_fee.dart';
import 'package:pshared/data/dto/payment/quote_amounts.dart';
import 'package:pshared/data/dto/payment/quote_fees.dart';
part 'payment_quote.g.dart';
@JsonSerializable()
class PaymentQuoteDTO {
final String? quoteRef;
final MoneyDTO? debitAmount;
final MoneyDTO? debitSettlementAmount;
final MoneyDTO? expectedSettlementAmount;
final MoneyDTO? expectedFeeTotal;
final List<FeeLineDTO>? feeLines;
final NetworkFeeDTO? networkFee;
final QuoteAmountsDTO? amounts;
final QuoteFeesDTO? fees;
final FxQuoteDTO? fxQuote;
const PaymentQuoteDTO({
this.quoteRef,
this.debitAmount,
this.debitSettlementAmount,
this.expectedSettlementAmount,
this.expectedFeeTotal,
this.feeLines,
this.networkFee,
this.fxQuote,
});
const PaymentQuoteDTO({this.quoteRef, this.amounts, this.fees, this.fxQuote});
factory PaymentQuoteDTO.fromJson(Map<String, dynamic> json) => _$PaymentQuoteDTOFromJson(json);
factory PaymentQuoteDTO.fromJson(Map<String, dynamic> json) =>
_$PaymentQuoteDTOFromJson(json);
Map<String, dynamic> toJson() => _$PaymentQuoteDTOToJson(this);
}

View File

@@ -1,24 +0,0 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/money.dart';
part 'quote_aggregate.g.dart';
@JsonSerializable()
class PaymentQuoteAggregateDTO {
final List<MoneyDTO>? debitAmounts;
final List<MoneyDTO>? expectedSettlementAmounts;
final List<MoneyDTO>? expectedFeeTotals;
final List<MoneyDTO>? networkFeeTotals;
const PaymentQuoteAggregateDTO({
this.debitAmounts,
this.expectedSettlementAmounts,
this.expectedFeeTotals,
this.networkFeeTotals,
});
factory PaymentQuoteAggregateDTO.fromJson(Map<String, dynamic> json) => _$PaymentQuoteAggregateDTOFromJson(json);
Map<String, dynamic> toJson() => _$PaymentQuoteAggregateDTOToJson(this);
}

View File

@@ -0,0 +1,22 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/money.dart';
part 'quote_amounts.g.dart';
@JsonSerializable()
class QuoteAmountsDTO {
final MoneyDTO? sourcePrincipal;
final MoneyDTO? sourceDebitTotal;
final MoneyDTO? destinationSettlement;
const QuoteAmountsDTO({
this.sourcePrincipal,
this.sourceDebitTotal,
this.destinationSettlement,
});
factory QuoteAmountsDTO.fromJson(Map<String, dynamic> json) =>
_$QuoteAmountsDTOFromJson(json);
Map<String, dynamic> toJson() => _$QuoteAmountsDTOToJson(this);
}

View File

@@ -0,0 +1,16 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/payment/fee_line.dart';
part 'quote_fees.g.dart';
@JsonSerializable()
class QuoteFeesDTO {
final List<FeeLineDTO>? lines;
const QuoteFeesDTO({this.lines});
factory QuoteFeesDTO.fromJson(Map<String, dynamic> json) =>
_$QuoteFeesDTOFromJson(json);
Map<String, dynamic> toJson() => _$QuoteFeesDTOToJson(this);
}

View File

@@ -1,6 +1,5 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:pshared/data/dto/payment/quote_aggregate.dart';
import 'package:pshared/data/dto/payment/payment_quote.dart';
part 'quotes.g.dart';
@@ -9,14 +8,12 @@ part 'quotes.g.dart';
class PaymentQuotesDTO {
final String quoteRef;
final String? idempotencyKey;
final PaymentQuoteAggregateDTO? aggregate;
final List<PaymentQuoteDTO>? quotes;
final List<PaymentQuoteDTO>? items;
const PaymentQuotesDTO({
required this.quoteRef,
this.idempotencyKey,
this.aggregate,
this.quotes,
this.items,
});
factory PaymentQuotesDTO.fromJson(Map<String, dynamic> json) =>

View File

@@ -1,18 +0,0 @@
import 'package:pshared/data/dto/payment/network_fee.dart';
import 'package:pshared/data/mapper/money.dart';
import 'package:pshared/models/payment/fees/network.dart';
extension NetworkFeeDTOMapper on NetworkFeeDTO {
NetworkFee toDomain() => NetworkFee(
networkFee: networkFee?.toDomain(),
estimationContext: estimationContext,
);
}
extension NetworkFeeMapper on NetworkFee {
NetworkFeeDTO toDTO() => NetworkFeeDTO(
networkFee: networkFee?.toDTO(),
estimationContext: estimationContext,
);
}

View File

@@ -1,21 +1,15 @@
import 'package:pshared/data/dto/payment/payment_quote.dart';
import 'package:pshared/data/mapper/payment/fees/line.dart';
import 'package:pshared/data/mapper/payment/fx_quote.dart';
import 'package:pshared/data/mapper/money.dart';
import 'package:pshared/data/mapper/payment/network_fee.dart';
import 'package:pshared/data/mapper/payment/quote/amounts.dart';
import 'package:pshared/data/mapper/payment/quote/fees.dart';
import 'package:pshared/models/payment/quote/quote.dart';
extension PaymentQuoteDTOMapper on PaymentQuoteDTO {
PaymentQuote toDomain({String? idempotencyKey}) => PaymentQuote(
quoteRef: quoteRef,
idempotencyKey: idempotencyKey,
debitAmount: debitAmount?.toDomain(),
debitSettlementAmount: debitSettlementAmount?.toDomain(),
expectedSettlementAmount: expectedSettlementAmount?.toDomain(),
expectedFeeTotal: expectedFeeTotal?.toDomain(),
feeLines: feeLines?.map((line) => line.toDomain()).toList(),
networkFee: networkFee?.toDomain(),
amounts: amounts?.toDomain(),
fees: fees?.toDomain(),
fxQuote: fxQuote?.toDomain(),
);
}
@@ -23,12 +17,8 @@ extension PaymentQuoteDTOMapper on PaymentQuoteDTO {
extension PaymentQuoteMapper on PaymentQuote {
PaymentQuoteDTO toDTO() => PaymentQuoteDTO(
quoteRef: quoteRef,
debitAmount: debitAmount?.toDTO(),
debitSettlementAmount: debitSettlementAmount?.toDTO(),
expectedSettlementAmount: expectedSettlementAmount?.toDTO(),
expectedFeeTotal: expectedFeeTotal?.toDTO(),
feeLines: feeLines?.map((line) => line.toDTO()).toList(),
networkFee: networkFee?.toDTO(),
amounts: amounts?.toDTO(),
fees: fees?.toDTO(),
fxQuote: fxQuote?.toDTO(),
);
}

View File

@@ -1,22 +0,0 @@
import 'package:pshared/data/dto/payment/quote_aggregate.dart';
import 'package:pshared/data/mapper/money.dart';
import 'package:pshared/models/payment/quote/aggregate.dart';
extension PaymentQuoteAggregateDTOMapper on PaymentQuoteAggregateDTO {
PaymentQuoteAggregate toDomain() => PaymentQuoteAggregate(
debitAmounts: debitAmounts?.map((amount) => amount.toDomain()).toList(),
expectedSettlementAmounts: expectedSettlementAmounts?.map((amount) => amount.toDomain()).toList(),
expectedFeeTotals: expectedFeeTotals?.map((amount) => amount.toDomain()).toList(),
networkFeeTotals: networkFeeTotals?.map((amount) => amount.toDomain()).toList(),
);
}
extension PaymentQuoteAggregateMapper on PaymentQuoteAggregate {
PaymentQuoteAggregateDTO toDTO() => PaymentQuoteAggregateDTO(
debitAmounts: debitAmounts?.map((amount) => amount.toDTO()).toList(),
expectedSettlementAmounts: expectedSettlementAmounts?.map((amount) => amount.toDTO()).toList(),
expectedFeeTotals: expectedFeeTotals?.map((amount) => amount.toDTO()).toList(),
networkFeeTotals: networkFeeTotals?.map((amount) => amount.toDTO()).toList(),
);
}

View File

@@ -0,0 +1,19 @@
import 'package:pshared/data/dto/payment/quote_amounts.dart';
import 'package:pshared/data/mapper/money.dart';
import 'package:pshared/models/payment/quote/amounts.dart';
extension QuoteAmountsDTOMapper on QuoteAmountsDTO {
QuoteAmounts toDomain() => QuoteAmounts(
sourcePrincipal: sourcePrincipal?.toDomain(),
sourceDebitTotal: sourceDebitTotal?.toDomain(),
destinationSettlement: destinationSettlement?.toDomain(),
);
}
extension QuoteAmountsMapper on QuoteAmounts {
QuoteAmountsDTO toDTO() => QuoteAmountsDTO(
sourcePrincipal: sourcePrincipal?.toDTO(),
sourceDebitTotal: sourceDebitTotal?.toDTO(),
destinationSettlement: destinationSettlement?.toDTO(),
);
}

View File

@@ -0,0 +1,13 @@
import 'package:pshared/data/dto/payment/quote_fees.dart';
import 'package:pshared/data/mapper/payment/fees/line.dart';
import 'package:pshared/models/payment/quote/fees.dart';
extension QuoteFeesDTOMapper on QuoteFeesDTO {
QuoteFees toDomain() =>
QuoteFees(lines: lines?.map((line) => line.toDomain()).toList());
}
extension QuoteFeesMapper on QuoteFees {
QuoteFeesDTO toDTO() =>
QuoteFeesDTO(lines: lines?.map((line) => line.toDTO()).toList());
}

View File

@@ -1,14 +1,12 @@
import 'package:pshared/data/dto/payment/quotes.dart';
import 'package:pshared/data/mapper/payment/quote.dart';
import 'package:pshared/data/mapper/payment/quote/aggregate.dart';
import 'package:pshared/models/payment/quote/quotes.dart';
extension PaymentQuotesDTOMapper on PaymentQuotesDTO {
PaymentQuotes toDomain({String? idempotencyKey}) => PaymentQuotes(
quoteRef: quoteRef,
idempotencyKey: idempotencyKey ?? this.idempotencyKey,
aggregate: aggregate?.toDomain(),
quotes: quotes?.map((quote) => quote.toDomain()).toList(),
items: items?.map((quote) => quote.toDomain()).toList(),
);
}
@@ -16,7 +14,6 @@ extension PaymentQuotesMapper on PaymentQuotes {
PaymentQuotesDTO toDTO() => PaymentQuotesDTO(
quoteRef: quoteRef,
idempotencyKey: idempotencyKey,
aggregate: aggregate?.toDTO(),
quotes: quotes?.map((quote) => quote.toDTO()).toList(),
items: items?.map((quote) => quote.toDTO()).toList(),
);
}

View File

@@ -1,12 +0,0 @@
import 'package:pshared/models/money.dart';
class NetworkFee {
final Money? networkFee;
final String? estimationContext;
const NetworkFee({
required this.networkFee,
required this.estimationContext,
});
}

View File

@@ -1,16 +0,0 @@
import 'package:pshared/models/money.dart';
class PaymentQuoteAggregate {
final List<Money>? debitAmounts;
final List<Money>? expectedSettlementAmounts;
final List<Money>? expectedFeeTotals;
final List<Money>? networkFeeTotals;
const PaymentQuoteAggregate({
required this.debitAmounts,
required this.expectedSettlementAmounts,
required this.expectedFeeTotals,
required this.networkFeeTotals,
});
}

View File

@@ -0,0 +1,13 @@
import 'package:pshared/models/money.dart';
class QuoteAmounts {
final Money? sourcePrincipal;
final Money? sourceDebitTotal;
final Money? destinationSettlement;
const QuoteAmounts({
required this.sourcePrincipal,
required this.sourceDebitTotal,
required this.destinationSettlement,
});
}

View File

@@ -0,0 +1,7 @@
import 'package:pshared/models/payment/fees/line.dart';
class QuoteFees {
final List<FeeLine>? lines;
const QuoteFees({required this.lines});
}

View File

@@ -1,29 +1,19 @@
import 'package:pshared/models/payment/fees/line.dart';
import 'package:pshared/models/payment/fx/quote.dart';
import 'package:pshared/models/money.dart';
import 'package:pshared/models/payment/fees/network.dart';
import 'package:pshared/models/payment/quote/amounts.dart';
import 'package:pshared/models/payment/quote/fees.dart';
class PaymentQuote {
final String? quoteRef;
final String? idempotencyKey;
final Money? debitAmount;
final Money? debitSettlementAmount;
final Money? expectedSettlementAmount;
final Money? expectedFeeTotal;
final List<FeeLine>? feeLines;
final NetworkFee? networkFee;
final QuoteAmounts? amounts;
final QuoteFees? fees;
final FxQuote? fxQuote;
const PaymentQuote({
required this.quoteRef,
required this.idempotencyKey,
required this.debitAmount,
required this.debitSettlementAmount,
required this.expectedSettlementAmount,
required this.expectedFeeTotal,
required this.feeLines,
required this.networkFee,
required this.amounts,
required this.fees,
required this.fxQuote,
});
}

View File

@@ -1,17 +1,13 @@
import 'package:pshared/models/payment/quote/quote.dart';
import 'package:pshared/models/payment/quote/aggregate.dart';
class PaymentQuotes {
final String quoteRef;
final String? idempotencyKey;
final PaymentQuoteAggregate? aggregate;
final List<PaymentQuote>? quotes;
final List<PaymentQuote>? items;
const PaymentQuotes({
required this.quoteRef,
required this.idempotencyKey,
required this.aggregate,
required this.quotes,
required this.items,
});
}

View File

@@ -34,11 +34,11 @@ class MultiQuotationProvider extends ChangeNotifier {
quotation != null && !_quotation.isLoading && _quotation.error == null;
DateTime? get quoteExpiresAt {
final quotes = quotation?.quotes;
if (quotes == null || quotes.isEmpty) return null;
final items = quotation?.items;
if (items == null || items.isEmpty) return null;
int? minExpiresAt;
for (final quote in quotes) {
for (final quote in items) {
final expiresAtUnixMs = quote.fxQuote?.expiresAtUnixMs;
if (expiresAtUnixMs == null) continue;
minExpiresAt = minExpiresAt == null

View File

@@ -23,12 +23,16 @@ import 'package:pshared/provider/recipient/pmethods.dart';
import 'package:pshared/provider/resource.dart';
import 'package:pshared/provider/payment/quotation/intent_builder.dart';
import 'package:pshared/service/payment/quotation.dart';
import 'package:pshared/utils/payment/quote_helpers.dart';
import 'package:pshared/utils/exception.dart';
class QuotationProvider extends ChangeNotifier {
static final _logger = Logger('provider.payment.quotation');
Resource<PaymentQuote> _quotation = Resource(data: null, isLoading: false, error: null);
Resource<PaymentQuote> _quotation = Resource(
data: null,
isLoading: false,
error: null,
);
late OrganizationsProvider _organizations;
bool _isLoaded = false;
PaymentIntent? _lastIntent;
@@ -37,7 +41,7 @@ class QuotationProvider extends ChangeNotifier {
AutoRefreshMode _autoRefreshMode = AutoRefreshMode.on;
void update(
OrganizationsProvider venue,
OrganizationsProvider venue,
PaymentAmountProvider payment,
WalletsController wallets,
PaymentFlowProvider flow,
@@ -62,7 +66,8 @@ class QuotationProvider extends ChangeNotifier {
bool get isLoading => _quotation.isLoading;
Exception? get error => _quotation.error;
bool get canRefresh => _lastIntent != null;
bool get isReady => _isLoaded && !_quotation.isLoading && _quotation.error == null;
bool get isReady =>
_isLoaded && !_quotation.isLoading && _quotation.error == null;
AutoRefreshMode get autoRefreshMode => _autoRefreshMode;
DateTime? get quoteExpiresAt {
@@ -71,10 +76,10 @@ class QuotationProvider extends ChangeNotifier {
return DateTime.fromMillisecondsSinceEpoch(expiresAtUnixMs, isUtc: true);
}
Asset? get fee => _assetFromMoney(quotation?.expectedFeeTotal);
Asset? get total => _assetFromMoney(quotation?.debitAmount);
Asset? get recipientGets => _assetFromMoney(quotation?.expectedSettlementAmount);
Asset? get fee => _assetFromMoney(quoteFeeTotal(quotation));
Asset? get total => _assetFromMoney(quotation?.amounts?.sourceDebitTotal);
Asset? get recipientGets =>
_assetFromMoney(quotation?.amounts?.destinationSettlement);
Asset? _assetFromMoney(Money? money) {
if (money == null) return null;
@@ -101,26 +106,32 @@ class QuotationProvider extends ChangeNotifier {
}
Future<PaymentQuote?> getQuotation(PaymentIntent intent) async {
if (!_organizations.isOrganizationSet) throw StateError('Organization is not set');
if (!_organizations.isOrganizationSet) {
throw StateError('Organization is not set');
}
_lastIntent = intent;
try {
_setResource(_quotation.copyWith(isLoading: true, error: null));
final response = await QuotationService.getQuotation(
_organizations.current.id,
_organizations.current.id,
QuotePaymentRequest(
idempotencyKey: Uuid().v4(),
intent: intent.toDTO(),
),
);
_isLoaded = true;
_setResource(_quotation.copyWith(data: response, isLoading: false, error: null));
_setResource(
_quotation.copyWith(data: response, isLoading: false, error: null),
);
} catch (e, st) {
_logger.warning('Failed to get quotation', e, st);
_setResource(_quotation.copyWith(
data: null,
error: toException(e),
isLoading: false,
));
_setResource(
_quotation.copyWith(
data: null,
error: toException(e),
isLoading: false,
),
);
}
return _quotation.data;
}

View File

@@ -0,0 +1,92 @@
import 'package:pshared/models/money.dart';
import 'package:pshared/models/payment/fees/line.dart';
import 'package:pshared/models/payment/quote/quote.dart';
import 'package:pshared/utils/currency.dart';
import 'package:pshared/utils/money.dart';
Money? quoteFeeTotal(PaymentQuote? quote) {
final preferredCurrency =
quote?.amounts?.sourcePrincipal?.currency ??
quote?.amounts?.sourceDebitTotal?.currency;
return quoteFeeTotalFromLines(
quote?.fees?.lines,
preferredCurrency: preferredCurrency,
);
}
Money? quoteFeeTotalFromLines(
List<FeeLine>? lines, {
String? preferredCurrency,
}) {
if (lines == null || lines.isEmpty) return null;
final normalizedPreferred = _normalizeCurrency(preferredCurrency);
final totalsByCurrency = <String, double>{};
for (final line in lines) {
final money = line.amount;
if (money == null) continue;
final currency = _normalizeCurrency(money.currency);
if (currency == null) continue;
final amount = parseMoneyAmount(money.amount, fallback: double.nan);
if (amount.isNaN) continue;
final sign = _lineSign(line.side);
final signedAmount = sign * amount.abs();
totalsByCurrency[currency] =
(totalsByCurrency[currency] ?? 0) + signedAmount;
}
if (totalsByCurrency.isEmpty) return null;
final selectedCurrency =
normalizedPreferred != null &&
totalsByCurrency.containsKey(normalizedPreferred)
? normalizedPreferred
: totalsByCurrency.keys.first;
final total = totalsByCurrency[selectedCurrency];
if (total == null) return null;
return Money(amount: amountToString(total), currency: selectedCurrency);
}
List<Money> aggregateMoneyByCurrency(Iterable<Money?> values) {
final totals = <String, double>{};
for (final value in values) {
if (value == null) continue;
final currency = _normalizeCurrency(value.currency);
if (currency == null) continue;
final amount = parseMoneyAmount(value.amount, fallback: double.nan);
if (amount.isNaN) continue;
totals[currency] = (totals[currency] ?? 0) + amount;
}
return totals.entries
.map(
(entry) =>
Money(amount: amountToString(entry.value), currency: entry.key),
)
.toList();
}
double _lineSign(String? side) {
final normalized = side?.trim().toLowerCase() ?? '';
switch (normalized) {
case 'entry_side_credit':
case 'credit':
return -1;
default:
return 1;
}
}
String? _normalizeCurrency(String? currency) {
final normalized = currency?.trim().toUpperCase();
if (normalized == null || normalized.isEmpty) return null;
return normalized;
}