removed legacy from bff
This commit is contained in:
@@ -41,26 +41,26 @@ type FxQuote struct {
|
||||
}
|
||||
|
||||
type PaymentQuote struct {
|
||||
QuoteRef string `json:"quoteRef,omitempty"`
|
||||
DebitAmount *paymenttypes.Money `json:"debitAmount,omitempty"`
|
||||
DebitSettlementAmount *paymenttypes.Money `json:"debitSettlementAmount,omitempty"`
|
||||
ExpectedSettlementAmount *paymenttypes.Money `json:"expectedSettlementAmount,omitempty"`
|
||||
ExpectedFeeTotal *paymenttypes.Money `json:"expectedFeeTotal,omitempty"`
|
||||
FeeLines []FeeLine `json:"feeLines,omitempty"`
|
||||
FxQuote *FxQuote `json:"fxQuote,omitempty"`
|
||||
QuoteRef string `json:"quoteRef,omitempty"`
|
||||
Amounts *QuoteAmounts `json:"amounts,omitempty"`
|
||||
Fees *QuoteFees `json:"fees,omitempty"`
|
||||
FxQuote *FxQuote `json:"fxQuote,omitempty"`
|
||||
}
|
||||
|
||||
type PaymentQuoteAggregate struct {
|
||||
DebitAmounts []*paymenttypes.Money `json:"debitAmounts,omitempty"`
|
||||
ExpectedSettlementAmounts []*paymenttypes.Money `json:"expectedSettlementAmounts,omitempty"`
|
||||
ExpectedFeeTotals []*paymenttypes.Money `json:"expectedFeeTotals,omitempty"`
|
||||
type QuoteAmounts struct {
|
||||
SourcePrincipal *paymenttypes.Money `json:"sourcePrincipal,omitempty"`
|
||||
SourceDebitTotal *paymenttypes.Money `json:"sourceDebitTotal,omitempty"`
|
||||
DestinationSettlement *paymenttypes.Money `json:"destinationSettlement,omitempty"`
|
||||
}
|
||||
|
||||
type QuoteFees struct {
|
||||
Lines []FeeLine `json:"lines,omitempty"`
|
||||
}
|
||||
|
||||
type PaymentQuotes struct {
|
||||
IdempotencyKey string `json:"idempotencyKey"`
|
||||
QuoteRef string `json:"quoteRef,omitempty"`
|
||||
Aggregate *PaymentQuoteAggregate `json:"aggregate,omitempty"`
|
||||
Quotes []PaymentQuote `json:"quotes,omitempty"`
|
||||
IdempotencyKey string `json:"idempotencyKey,omitempty"`
|
||||
QuoteRef string `json:"quoteRef,omitempty"`
|
||||
Items []PaymentQuote `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
type Payment struct {
|
||||
@@ -196,12 +196,13 @@ func toPaymentQuote(q *quotationv2.PaymentQuote) *PaymentQuote {
|
||||
if q == nil {
|
||||
return nil
|
||||
}
|
||||
amounts := toQuoteAmounts(q)
|
||||
fees := toQuoteFees(q.GetFeeLines())
|
||||
return &PaymentQuote{
|
||||
QuoteRef: q.GetQuoteRef(),
|
||||
DebitAmount: toMoney(q.GetPayerTotalDebitAmount()),
|
||||
ExpectedSettlementAmount: toMoney(q.GetDestinationAmount()),
|
||||
FeeLines: toFeeLines(q.GetFeeLines()),
|
||||
FxQuote: toFxQuote(q.GetFxQuote()),
|
||||
QuoteRef: q.GetQuoteRef(),
|
||||
Amounts: amounts,
|
||||
Fees: fees,
|
||||
FxQuote: toFxQuote(q.GetFxQuote()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,22 +210,45 @@ func toPaymentQuotes(resp *quotationv2.QuotePaymentsResponse) *PaymentQuotes {
|
||||
if resp == nil {
|
||||
return nil
|
||||
}
|
||||
quotes := make([]PaymentQuote, 0, len(resp.GetQuotes()))
|
||||
items := make([]PaymentQuote, 0, len(resp.GetQuotes()))
|
||||
for _, quote := range resp.GetQuotes() {
|
||||
if dto := toPaymentQuote(quote); dto != nil {
|
||||
quotes = append(quotes, *dto)
|
||||
items = append(items, *dto)
|
||||
}
|
||||
}
|
||||
if len(quotes) == 0 {
|
||||
quotes = nil
|
||||
if len(items) == 0 {
|
||||
items = nil
|
||||
}
|
||||
return &PaymentQuotes{
|
||||
IdempotencyKey: resp.GetIdempotencyKey(),
|
||||
QuoteRef: resp.GetQuoteRef(),
|
||||
Quotes: quotes,
|
||||
Items: items,
|
||||
}
|
||||
}
|
||||
|
||||
func toQuoteAmounts(q *quotationv2.PaymentQuote) *QuoteAmounts {
|
||||
if q == nil {
|
||||
return nil
|
||||
}
|
||||
amounts := &QuoteAmounts{
|
||||
SourcePrincipal: toMoney(q.GetTransferPrincipalAmount()),
|
||||
SourceDebitTotal: toMoney(q.GetPayerTotalDebitAmount()),
|
||||
DestinationSettlement: toMoney(q.GetDestinationAmount()),
|
||||
}
|
||||
if amounts.SourcePrincipal == nil && amounts.SourceDebitTotal == nil && amounts.DestinationSettlement == nil {
|
||||
return nil
|
||||
}
|
||||
return amounts
|
||||
}
|
||||
|
||||
func toQuoteFees(lines []*feesv1.DerivedPostingLine) *QuoteFees {
|
||||
feeLines := toFeeLines(lines)
|
||||
if len(feeLines) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &QuoteFees{Lines: feeLines}
|
||||
}
|
||||
|
||||
func toPayments(items []*orchestrationv2.Payment) []Payment {
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
22
frontend/pshared/lib/data/dto/payment/quote_amounts.dart
Normal file
22
frontend/pshared/lib/data/dto/payment/quote_amounts.dart
Normal 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);
|
||||
}
|
||||
16
frontend/pshared/lib/data/dto/payment/quote_fees.dart
Normal file
16
frontend/pshared/lib/data/dto/payment/quote_fees.dart
Normal 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);
|
||||
}
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
19
frontend/pshared/lib/data/mapper/payment/quote/amounts.dart
Normal file
19
frontend/pshared/lib/data/mapper/payment/quote/amounts.dart
Normal 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(),
|
||||
);
|
||||
}
|
||||
13
frontend/pshared/lib/data/mapper/payment/quote/fees.dart
Normal file
13
frontend/pshared/lib/data/mapper/payment/quote/fees.dart
Normal 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());
|
||||
}
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
13
frontend/pshared/lib/models/payment/quote/amounts.dart
Normal file
13
frontend/pshared/lib/models/payment/quote/amounts.dart
Normal 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,
|
||||
});
|
||||
}
|
||||
7
frontend/pshared/lib/models/payment/quote/fees.dart
Normal file
7
frontend/pshared/lib/models/payment/quote/fees.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
import 'package:pshared/models/payment/fees/line.dart';
|
||||
|
||||
class QuoteFees {
|
||||
final List<FeeLine>? lines;
|
||||
|
||||
const QuoteFees({required this.lines});
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
92
frontend/pshared/lib/utils/payment/quote_helpers.dart
Normal file
92
frontend/pshared/lib/utils/payment/quote_helpers.dart
Normal 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;
|
||||
}
|
||||
@@ -82,8 +82,22 @@ void main() {
|
||||
'idempotencyKey': 'idem-1',
|
||||
'quote': {
|
||||
'quoteRef': 'q-1',
|
||||
'debitAmount': {'amount': '10', 'currency': 'USDT'},
|
||||
'expectedSettlementAmount': {'amount': '760', 'currency': 'RUB'},
|
||||
'amounts': {
|
||||
'sourcePrincipal': {'amount': '10', 'currency': 'USDT'},
|
||||
'sourceDebitTotal': {'amount': '10.75', 'currency': 'USDT'},
|
||||
'destinationSettlement': {'amount': '760', 'currency': 'RUB'},
|
||||
},
|
||||
'fees': {
|
||||
'lines': [
|
||||
{
|
||||
'ledgerAccountRef': 'ledger:fees',
|
||||
'amount': {'amount': '0.75', 'currency': 'USDT'},
|
||||
'lineType': 'posting_line_type_fee',
|
||||
'side': 'entry_side_debit',
|
||||
'meta': {'fee_target': 'wallet'},
|
||||
},
|
||||
],
|
||||
},
|
||||
'fxQuote': {
|
||||
'quoteRef': 'fx-1',
|
||||
'baseCurrency': 'USDT',
|
||||
@@ -102,6 +116,8 @@ void main() {
|
||||
});
|
||||
|
||||
expect(response.quote.fxQuote?.pricedAtUnixMs, equals(1771945907000));
|
||||
expect(response.quote.amounts?.sourceDebitTotal?.amount, equals('10.75'));
|
||||
expect(response.quote.fees?.lines?.length, equals(1));
|
||||
});
|
||||
|
||||
test('initiate payment by quote keeps expected fields', () {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:pshared/models/payment/payment.dart';
|
||||
import 'package:pshared/utils/payment/quote_helpers.dart';
|
||||
|
||||
import 'package:pweb/pages/report/details/summary_card/amount_headline.dart';
|
||||
import 'package:pweb/pages/report/details/summary_card/copy_id.dart';
|
||||
@@ -13,7 +14,6 @@ import 'package:pweb/utils/clipboard.dart';
|
||||
|
||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||
|
||||
|
||||
class PaymentSummaryCard extends StatelessWidget {
|
||||
final Payment payment;
|
||||
final VoidCallback? onDownloadAct;
|
||||
@@ -31,11 +31,11 @@ class PaymentSummaryCard extends StatelessWidget {
|
||||
final status = statusFromPayment(payment);
|
||||
final dateLabel = formatDateLabel(context, resolvePaymentDate(payment));
|
||||
|
||||
final primaryAmount = payment.lastQuote?.debitAmount ??
|
||||
payment.lastQuote?.expectedSettlementAmount;
|
||||
final toAmount = payment.lastQuote?.expectedSettlementAmount;
|
||||
final fee = payment.lastQuote?.expectedFeeTotal ??
|
||||
payment.lastQuote?.networkFee?.networkFee;
|
||||
final primaryAmount =
|
||||
payment.lastQuote?.amounts?.sourceDebitTotal ??
|
||||
payment.lastQuote?.amounts?.destinationSettlement;
|
||||
final toAmount = payment.lastQuote?.amounts?.destinationSettlement;
|
||||
final fee = quoteFeeTotal(payment.lastQuote);
|
||||
|
||||
final amountLabel = formatMoney(primaryAmount);
|
||||
final toAmountLabel = formatMoney(toAmount);
|
||||
@@ -108,11 +108,8 @@ class PaymentSummaryCard extends StatelessWidget {
|
||||
child: CopyableId(
|
||||
label: loc.paymentIdLabel,
|
||||
value: paymentRef,
|
||||
onCopy: () => copyToClipboard(
|
||||
context,
|
||||
paymentRef,
|
||||
loc.paymentIdCopied,
|
||||
),
|
||||
onCopy: () =>
|
||||
copyToClipboard(context, paymentRef, loc.paymentIdCopied),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -2,19 +2,20 @@ import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:pshared/models/money.dart';
|
||||
import 'package:pshared/models/payment/payment.dart';
|
||||
import 'package:pshared/models/payment/quote/quote.dart';
|
||||
import 'package:pshared/models/payment/quote/status_type.dart';
|
||||
import 'package:pshared/models/payment/wallet.dart';
|
||||
import 'package:pshared/provider/payment/multiple/provider.dart';
|
||||
import 'package:pshared/provider/payment/multiple/quotation.dart';
|
||||
import 'package:pshared/utils/currency.dart';
|
||||
import 'package:pshared/utils/money.dart';
|
||||
import 'package:pshared/utils/payment/quote_helpers.dart';
|
||||
|
||||
import 'package:pweb/models/payment/multiple_payouts/csv_row.dart';
|
||||
import 'package:pweb/models/payment/multiple_payouts/state.dart';
|
||||
import 'package:pweb/utils/payment/multiple_csv_parser.dart';
|
||||
import 'package:pweb/utils/payment/multiple_intent_builder.dart';
|
||||
|
||||
|
||||
class MultiplePayoutsProvider extends ChangeNotifier {
|
||||
final MultipleCsvParser _csvParser;
|
||||
final MultipleIntentBuilder _intentBuilder;
|
||||
@@ -34,10 +35,7 @@ class MultiplePayoutsProvider extends ChangeNotifier {
|
||||
}) : _csvParser = csvParser ?? MultipleCsvParser(),
|
||||
_intentBuilder = intentBuilder ?? MultipleIntentBuilder();
|
||||
|
||||
void update(
|
||||
MultiQuotationProvider quotation,
|
||||
MultiPaymentProvider payment,
|
||||
) {
|
||||
void update(MultiQuotationProvider quotation, MultiPaymentProvider payment) {
|
||||
_bindQuotation(quotation);
|
||||
_payment = payment;
|
||||
}
|
||||
@@ -60,7 +58,9 @@ class MultiplePayoutsProvider extends ChangeNotifier {
|
||||
if (quotation.isLoading) return QuoteStatusType.loading;
|
||||
if (quotation.error != null) return QuoteStatusType.error;
|
||||
if (quotation.quotation == null) return QuoteStatusType.missing;
|
||||
if (_isQuoteExpired(quotation.quoteExpiresAt)) return QuoteStatusType.expired;
|
||||
if (_isQuoteExpired(quotation.quoteExpiresAt)) {
|
||||
return QuoteStatusType.expired;
|
||||
}
|
||||
return QuoteStatusType.active;
|
||||
}
|
||||
|
||||
@@ -78,10 +78,10 @@ class MultiplePayoutsProvider extends ChangeNotifier {
|
||||
|
||||
Money? aggregateDebitAmountFor(Wallet? sourceWallet) {
|
||||
if (_rows.isEmpty) return null;
|
||||
return _moneyForSourceCurrency(
|
||||
_quotation?.quotation?.aggregate?.debitAmounts,
|
||||
sourceWallet,
|
||||
final totals = aggregateMoneyByCurrency(
|
||||
_quoteItems().map((quote) => quote.amounts?.sourceDebitTotal),
|
||||
);
|
||||
return _moneyForSourceCurrency(totals, sourceWallet);
|
||||
}
|
||||
|
||||
Money? get requestedSentAmount {
|
||||
@@ -99,18 +99,16 @@ class MultiplePayoutsProvider extends ChangeNotifier {
|
||||
|
||||
Money? aggregateSettlementAmountFor(Wallet? sourceWallet) {
|
||||
if (_rows.isEmpty) return null;
|
||||
return _moneyForSourceCurrency(
|
||||
_quotation?.quotation?.aggregate?.expectedSettlementAmounts,
|
||||
sourceWallet,
|
||||
final totals = aggregateMoneyByCurrency(
|
||||
_quoteItems().map((quote) => quote.amounts?.destinationSettlement),
|
||||
);
|
||||
return _moneyForSourceCurrency(totals, sourceWallet);
|
||||
}
|
||||
|
||||
Money? aggregateFeeAmountFor(Wallet? sourceWallet) {
|
||||
if (_rows.isEmpty) return null;
|
||||
return _moneyForSourceCurrency(
|
||||
_quotation?.quotation?.aggregate?.expectedFeeTotals,
|
||||
sourceWallet,
|
||||
);
|
||||
final totals = aggregateMoneyByCurrency(_quoteItems().map(quoteFeeTotal));
|
||||
return _moneyForSourceCurrency(totals, sourceWallet);
|
||||
}
|
||||
|
||||
double? aggregateFeePercentFor(Wallet? sourceWallet) {
|
||||
@@ -256,10 +254,7 @@ class MultiplePayoutsProvider extends ChangeNotifier {
|
||||
};
|
||||
}
|
||||
|
||||
Money? _moneyForSourceCurrency(
|
||||
List<Money>? values,
|
||||
Wallet? sourceWallet,
|
||||
) {
|
||||
Money? _moneyForSourceCurrency(List<Money>? values, Wallet? sourceWallet) {
|
||||
if (values == null || values.isEmpty) return null;
|
||||
|
||||
if (sourceWallet != null) {
|
||||
@@ -274,6 +269,9 @@ class MultiplePayoutsProvider extends ChangeNotifier {
|
||||
return values.first;
|
||||
}
|
||||
|
||||
List<PaymentQuote> _quoteItems() =>
|
||||
_quotation?.quotation?.items ?? const <PaymentQuote>[];
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_quotation?.removeListener(_onQuotationChanged);
|
||||
|
||||
@@ -5,10 +5,9 @@ import 'package:pshared/utils/money.dart';
|
||||
|
||||
import 'package:pweb/models/payment/payment_state.dart';
|
||||
|
||||
|
||||
OperationItem mapPaymentToOperation(Payment payment) {
|
||||
final debit = payment.lastQuote?.debitAmount;
|
||||
final settlement = payment.lastQuote?.expectedSettlementAmount;
|
||||
final debit = payment.lastQuote?.amounts?.sourceDebitTotal;
|
||||
final settlement = payment.lastQuote?.amounts?.destinationSettlement;
|
||||
final amountMoney = debit ?? settlement;
|
||||
|
||||
final amount = parseMoneyAmount(amountMoney?.amount);
|
||||
@@ -18,18 +17,17 @@ OperationItem mapPaymentToOperation(Payment payment) {
|
||||
: parseMoneyAmount(settlement.amount);
|
||||
final toCurrency = settlement?.currency ?? currency;
|
||||
|
||||
final payId = _firstNonEmpty([
|
||||
payment.paymentRef,
|
||||
payment.idempotencyKey,
|
||||
]) ??
|
||||
'-';
|
||||
final name = _firstNonEmpty([
|
||||
final payId =
|
||||
_firstNonEmpty([payment.paymentRef, payment.idempotencyKey]) ?? '-';
|
||||
final name =
|
||||
_firstNonEmpty([
|
||||
payment.lastQuote?.quoteRef,
|
||||
payment.paymentRef,
|
||||
payment.idempotencyKey,
|
||||
]) ??
|
||||
'-';
|
||||
final comment = _firstNonEmpty([
|
||||
final comment =
|
||||
_firstNonEmpty([
|
||||
payment.failureReason,
|
||||
payment.failureCode,
|
||||
payment.state,
|
||||
@@ -72,17 +70,17 @@ DateTime resolvePaymentDate(Payment payment) {
|
||||
|
||||
final expiresAt = payment.lastQuote?.fxQuote?.expiresAtUnixMs;
|
||||
if (expiresAt != null && expiresAt > 0) {
|
||||
return DateTime.fromMillisecondsSinceEpoch(expiresAt, isUtc: true).toLocal();
|
||||
return DateTime.fromMillisecondsSinceEpoch(
|
||||
expiresAt,
|
||||
isUtc: true,
|
||||
).toLocal();
|
||||
}
|
||||
|
||||
return DateTime.fromMillisecondsSinceEpoch(0);
|
||||
}
|
||||
|
||||
String? paymentIdFromOperation(OperationItem operation) {
|
||||
final candidates = [
|
||||
operation.paymentRef,
|
||||
operation.payId,
|
||||
];
|
||||
final candidates = [operation.paymentRef, operation.payId];
|
||||
for (final candidate in candidates) {
|
||||
final trimmed = candidate?.trim();
|
||||
if (trimmed != null && trimmed.isNotEmpty && trimmed != '-') {
|
||||
|
||||
Reference in New Issue
Block a user