intent reference generation + propagation
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
import 'package:pshared/api/requests/payment/base.dart';
|
||||
|
||||
|
||||
class InitiatePaymentsRequest extends PaymentBaseRequest {
|
||||
final String quoteRef;
|
||||
final String? intentRef;
|
||||
final List<String>? intentRefs;
|
||||
|
||||
const InitiatePaymentsRequest({
|
||||
required super.idempotencyKey,
|
||||
super.metadata,
|
||||
required this.quoteRef,
|
||||
this.intentRef,
|
||||
this.intentRefs,
|
||||
});
|
||||
|
||||
factory InitiatePaymentsRequest.fromJson(Map<String, dynamic> json) {
|
||||
@@ -17,6 +20,10 @@ class InitiatePaymentsRequest extends PaymentBaseRequest {
|
||||
(key, value) => MapEntry(key, value as String),
|
||||
),
|
||||
quoteRef: json['quoteRef'] as String,
|
||||
intentRef: json['intentRef'] as String?,
|
||||
intentRefs: (json['intentRefs'] as List<dynamic>?)
|
||||
?.map((value) => value as String)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,6 +33,8 @@ class InitiatePaymentsRequest extends PaymentBaseRequest {
|
||||
'idempotencyKey': idempotencyKey,
|
||||
'metadata': metadata,
|
||||
'quoteRef': quoteRef,
|
||||
if (intentRef != null) 'intentRef': intentRef,
|
||||
if (intentRefs != null) 'intentRefs': intentRefs,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,18 @@ part 'payment_quote.g.dart';
|
||||
@JsonSerializable()
|
||||
class PaymentQuoteDTO {
|
||||
final String? quoteRef;
|
||||
final String? intentRef;
|
||||
final QuoteAmountsDTO? amounts;
|
||||
final QuoteFeesDTO? fees;
|
||||
final FxQuoteDTO? fxQuote;
|
||||
|
||||
const PaymentQuoteDTO({this.quoteRef, this.amounts, this.fees, this.fxQuote});
|
||||
const PaymentQuoteDTO({
|
||||
this.quoteRef,
|
||||
this.intentRef,
|
||||
this.amounts,
|
||||
this.fees,
|
||||
this.fxQuote,
|
||||
});
|
||||
|
||||
factory PaymentQuoteDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$PaymentQuoteDTOFromJson(json);
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:pshared/models/payment/quote/quote.dart';
|
||||
extension PaymentQuoteDTOMapper on PaymentQuoteDTO {
|
||||
PaymentQuote toDomain({String? idempotencyKey}) => PaymentQuote(
|
||||
quoteRef: quoteRef,
|
||||
intentRef: intentRef,
|
||||
idempotencyKey: idempotencyKey,
|
||||
amounts: amounts?.toDomain(),
|
||||
fees: fees?.toDomain(),
|
||||
@@ -17,6 +18,7 @@ extension PaymentQuoteDTOMapper on PaymentQuoteDTO {
|
||||
extension PaymentQuoteMapper on PaymentQuote {
|
||||
PaymentQuoteDTO toDTO() => PaymentQuoteDTO(
|
||||
quoteRef: quoteRef,
|
||||
intentRef: intentRef,
|
||||
amounts: amounts?.toDTO(),
|
||||
fees: fees?.toDTO(),
|
||||
fxQuote: fxQuote?.toDTO(),
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:pshared/models/payment/quote/fees.dart';
|
||||
|
||||
class PaymentQuote {
|
||||
final String? quoteRef;
|
||||
final String? intentRef;
|
||||
final String? idempotencyKey;
|
||||
final QuoteAmounts? amounts;
|
||||
final QuoteFees? fees;
|
||||
@@ -11,6 +12,7 @@ class PaymentQuote {
|
||||
|
||||
const PaymentQuote({
|
||||
required this.quoteRef,
|
||||
required this.intentRef,
|
||||
required this.idempotencyKey,
|
||||
required this.amounts,
|
||||
required this.fees,
|
||||
|
||||
@@ -7,7 +7,6 @@ import 'package:pshared/provider/resource.dart';
|
||||
import 'package:pshared/service/payment/multiple.dart';
|
||||
import 'package:pshared/utils/exception.dart';
|
||||
|
||||
|
||||
class MultiPaymentProvider extends ChangeNotifier {
|
||||
late OrganizationsProvider _organization;
|
||||
late MultiQuotationProvider _quotation;
|
||||
@@ -31,6 +30,8 @@ class MultiPaymentProvider extends ChangeNotifier {
|
||||
Future<List<Payment>> pay({
|
||||
String? idempotencyKey,
|
||||
Map<String, String>? metadata,
|
||||
String? intentRef,
|
||||
List<String>? intentRefs,
|
||||
}) async {
|
||||
if (!_organization.isOrganizationSet) {
|
||||
throw StateError('Organization is not set');
|
||||
@@ -53,6 +54,8 @@ class MultiPaymentProvider extends ChangeNotifier {
|
||||
quoteRef,
|
||||
idempotencyKey: idempotencyKey,
|
||||
metadata: metadata,
|
||||
intentRef: intentRef,
|
||||
intentRefs: intentRefs,
|
||||
);
|
||||
|
||||
_setResource(
|
||||
|
||||
@@ -13,7 +13,6 @@ import 'package:pshared/models/payment/quote/quotes.dart';
|
||||
import 'package:pshared/service/authorization/service.dart';
|
||||
import 'package:pshared/service/services.dart';
|
||||
|
||||
|
||||
class MultiplePaymentsService {
|
||||
static final _logger = Logger('service.payment.multiple');
|
||||
static const String _objectType = Services.payments;
|
||||
@@ -38,6 +37,8 @@ class MultiplePaymentsService {
|
||||
String quoteRef, {
|
||||
String? idempotencyKey,
|
||||
Map<String, String>? metadata,
|
||||
String? intentRef,
|
||||
List<String>? intentRefs,
|
||||
}) async {
|
||||
_logger.fine(
|
||||
'Executing multiple payments for quote $quoteRef in $organizationRef',
|
||||
@@ -46,6 +47,8 @@ class MultiplePaymentsService {
|
||||
idempotencyKey: idempotencyKey ?? const Uuid().v4(),
|
||||
quoteRef: quoteRef,
|
||||
metadata: metadata,
|
||||
intentRef: intentRef,
|
||||
intentRefs: intentRefs,
|
||||
);
|
||||
|
||||
final response = await AuthorizationService.getPOSTResponse(
|
||||
|
||||
@@ -114,6 +114,7 @@ void main() {
|
||||
'idempotencyKey': 'idem-1',
|
||||
'quote': {
|
||||
'quoteRef': 'q-1',
|
||||
'intentRef': 'intent-1',
|
||||
'amounts': {
|
||||
'sourcePrincipal': {'amount': '10', 'currency': 'USDT'},
|
||||
'sourceDebitTotal': {'amount': '10.75', 'currency': 'USDT'},
|
||||
@@ -148,6 +149,7 @@ void main() {
|
||||
});
|
||||
|
||||
expect(response.quote.fxQuote?.pricedAtUnixMs, equals(1771945907000));
|
||||
expect(response.quote.intentRef, equals('intent-1'));
|
||||
expect(response.quote.amounts?.sourceDebitTotal?.amount, equals('10.75'));
|
||||
expect(response.quote.fees?.lines?.length, equals(1));
|
||||
});
|
||||
@@ -174,16 +176,35 @@ void main() {
|
||||
final request = InitiatePaymentsRequest(
|
||||
idempotencyKey: 'idem-3',
|
||||
quoteRef: 'q-2',
|
||||
intentRefs: const ['intent-a', 'intent-b'],
|
||||
metadata: const {'client_payment_ref': 'cp-1'},
|
||||
);
|
||||
|
||||
final json = request.toJson();
|
||||
expect(json['idempotencyKey'], equals('idem-3'));
|
||||
expect(json['quoteRef'], equals('q-2'));
|
||||
expect(json['intentRefs'], equals(const ['intent-a', 'intent-b']));
|
||||
expect(
|
||||
(json['metadata'] as Map<String, dynamic>)['client_payment_ref'],
|
||||
equals('cp-1'),
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'initiate multi payments request supports single intentRef selector',
|
||||
() {
|
||||
final request = InitiatePaymentsRequest(
|
||||
idempotencyKey: 'idem-4',
|
||||
quoteRef: 'q-2',
|
||||
intentRef: 'intent-single',
|
||||
);
|
||||
|
||||
final json = request.toJson();
|
||||
expect(json['idempotencyKey'], equals('idem-4'));
|
||||
expect(json['quoteRef'], equals('q-2'));
|
||||
expect(json['intentRef'], equals('intent-single'));
|
||||
expect(json.containsKey('intentRefs'), isFalse);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -188,6 +188,7 @@ class MultiplePayoutsProvider extends ChangeNotifier {
|
||||
try {
|
||||
_setState(MultiplePayoutsState.sending);
|
||||
_error = null;
|
||||
final intentRefs = _quotedIntentRefs();
|
||||
|
||||
final result = await payment.pay(
|
||||
metadata: <String, String>{
|
||||
@@ -197,6 +198,7 @@ class MultiplePayoutsProvider extends ChangeNotifier {
|
||||
'upload_rows': _rows.length.toString(),
|
||||
...?_uploadAmountMetadata(),
|
||||
},
|
||||
intentRefs: intentRefs.isEmpty ? null : intentRefs,
|
||||
);
|
||||
|
||||
_sentCount = result.length;
|
||||
@@ -272,6 +274,20 @@ class MultiplePayoutsProvider extends ChangeNotifier {
|
||||
List<PaymentQuote> _quoteItems() =>
|
||||
_quotation?.quotation?.items ?? const <PaymentQuote>[];
|
||||
|
||||
List<String> _quotedIntentRefs() {
|
||||
final seen = <String>{};
|
||||
final intentRefs = <String>[];
|
||||
for (final quote in _quoteItems()) {
|
||||
final intentRef = (quote.intentRef ?? '').trim();
|
||||
if (intentRef.isEmpty || seen.contains(intentRef)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(intentRef);
|
||||
intentRefs.add(intentRef);
|
||||
}
|
||||
return intentRefs;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_quotation?.removeListener(_onQuotationChanged);
|
||||
|
||||
Reference in New Issue
Block a user