211 lines
7.5 KiB
Dart
211 lines
7.5 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
import 'package:pshared/api/requests/payment/initiate.dart';
|
|
import 'package:pshared/api/requests/payment/initiate_payments.dart';
|
|
import 'package:pshared/api/requests/payment/quote.dart';
|
|
import 'package:pshared/api/responses/payment/quotation.dart';
|
|
import 'package:pshared/data/dto/money.dart';
|
|
import 'package:pshared/data/dto/payment/currency_pair.dart';
|
|
import 'package:pshared/data/dto/payment/endpoint.dart';
|
|
import 'package:pshared/data/dto/payment/intent/fx.dart';
|
|
import 'package:pshared/data/dto/payment/intent/payment.dart';
|
|
import 'package:pshared/data/mapper/payment/payment.dart';
|
|
import 'package:pshared/models/payment/asset.dart';
|
|
import 'package:pshared/models/payment/chain_network.dart';
|
|
import 'package:pshared/models/payment/methods/card_token.dart';
|
|
import 'package:pshared/models/payment/methods/crypto_address.dart';
|
|
import 'package:pshared/models/payment/methods/managed_wallet.dart';
|
|
|
|
void main() {
|
|
group('Payment request DTO contract', () {
|
|
test('serializes endpoint types to backend canonical values', () {
|
|
final managed = ManagedWalletPaymentMethod(
|
|
managedWalletRef: 'mw-1',
|
|
).toDTO();
|
|
final external = CryptoAddressPaymentMethod(
|
|
asset: const PaymentAsset(
|
|
chain: ChainNetwork.tronMainnet,
|
|
tokenSymbol: 'USDT',
|
|
),
|
|
address: 'TXYZ',
|
|
).toDTO();
|
|
final cardToken = CardTokenPaymentMethod(
|
|
token: 'tok_1',
|
|
maskedPan: '4111',
|
|
).toDTO();
|
|
|
|
expect(managed.type, equals('managedWallet'));
|
|
expect(external.type, equals('cryptoAddress'));
|
|
expect(cardToken.type, equals('cardToken'));
|
|
});
|
|
|
|
test('quote payment request uses expected backend field names', () {
|
|
final request = QuotePaymentRequest(
|
|
idempotencyKey: '',
|
|
previewOnly: true,
|
|
intent: const PaymentIntentDTO(
|
|
kind: 'payout',
|
|
source: PaymentEndpointDTO(
|
|
type: 'ledger',
|
|
data: {'ledger_account_ref': 'ledger:src'},
|
|
),
|
|
destination: PaymentEndpointDTO(
|
|
type: 'cardToken',
|
|
data: {'token': 'tok_1', 'masked_pan': '4111'},
|
|
),
|
|
amount: MoneyDTO(amount: '10', currency: 'USD'),
|
|
settlementMode: 'fix_received',
|
|
),
|
|
);
|
|
|
|
final json =
|
|
jsonDecode(jsonEncode(request.toJson())) as Map<String, dynamic>;
|
|
|
|
expect(json['idempotencyKey'], equals(''));
|
|
expect(json['previewOnly'], isTrue);
|
|
expect(json['intent'], isA<Map<String, dynamic>>());
|
|
|
|
final intent = json['intent'] as Map<String, dynamic>;
|
|
expect(intent['kind'], equals('payout'));
|
|
expect(intent['settlement_mode'], equals('fix_received'));
|
|
expect(intent.containsKey('settlement_currency'), isFalse);
|
|
|
|
final source = intent['source'] as Map<String, dynamic>;
|
|
final destination = intent['destination'] as Map<String, dynamic>;
|
|
expect(source['type'], equals('ledger'));
|
|
expect(destination['type'], equals('cardToken'));
|
|
});
|
|
|
|
test('quote payment request serializes fx side to backend value', () {
|
|
final request = QuotePaymentRequest(
|
|
idempotencyKey: '',
|
|
previewOnly: true,
|
|
intent: const PaymentIntentDTO(
|
|
kind: 'payout',
|
|
source: PaymentEndpointDTO(
|
|
type: 'managedWallet',
|
|
data: {'managed_wallet_ref': 'mw-1'},
|
|
),
|
|
destination: PaymentEndpointDTO(
|
|
type: 'cardToken',
|
|
data: {'token': 'tok_1', 'masked_pan': '4111'},
|
|
),
|
|
amount: MoneyDTO(amount: '10', currency: 'USDT'),
|
|
settlementMode: 'fix_source',
|
|
fx: FxIntentDTO(
|
|
pair: CurrencyPairDTO(base: 'RUB', quote: 'USDT'),
|
|
side: 'buy_base_sell_quote',
|
|
),
|
|
),
|
|
);
|
|
|
|
final json =
|
|
jsonDecode(jsonEncode(request.toJson())) as Map<String, dynamic>;
|
|
final intent = json['intent'] as Map<String, dynamic>;
|
|
final fx = intent['fx'] as Map<String, dynamic>;
|
|
expect(fx['side'], equals('buy_base_sell_quote'));
|
|
});
|
|
|
|
test('quote response parses backend fx quote pricedAtUnixMs', () {
|
|
final response = PaymentQuoteResponse.fromJson({
|
|
'accessToken': {'token': 'token', 'expiration': '2026-02-25T00:00:00Z'},
|
|
'idempotencyKey': 'idem-1',
|
|
'quote': {
|
|
'quoteRef': 'q-1',
|
|
'intentRef': 'intent-1',
|
|
'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',
|
|
'quoteCurrency': 'RUB',
|
|
'side': 'sell_base_buy_quote',
|
|
'price': '76',
|
|
'baseAmount': {'amount': '10', 'currency': 'USDT'},
|
|
'quoteAmount': {'amount': '760', 'currency': 'RUB'},
|
|
'expiresAtUnixMs': 1771945907749,
|
|
'pricedAtUnixMs': 1771945907000,
|
|
'provider': 'binance',
|
|
'rateRef': 'rate-1',
|
|
'firm': false,
|
|
},
|
|
},
|
|
});
|
|
|
|
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));
|
|
});
|
|
|
|
test('initiate payment by quote keeps expected fields', () {
|
|
final request = InitiatePaymentRequest(
|
|
idempotencyKey: 'idem-2',
|
|
quoteRef: 'q-1',
|
|
metadata: const {'intent_ref': 'intent-1'},
|
|
);
|
|
|
|
final json = request.toJson();
|
|
expect(json['idempotencyKey'], equals('idem-2'));
|
|
expect(json['quoteRef'], equals('q-1'));
|
|
expect(
|
|
(json['metadata'] as Map<String, dynamic>)['intent_ref'],
|
|
equals('intent-1'),
|
|
);
|
|
expect(json.containsKey('intent'), isTrue);
|
|
expect(json['intent'], isNull);
|
|
});
|
|
|
|
test('initiate multi payments request keeps expected fields', () {
|
|
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);
|
|
},
|
|
);
|
|
});
|
|
}
|