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', comment: 'invoice-7', ), ); final json = jsonDecode(jsonEncode(request.toJson())) as Map; expect(json['idempotencyKey'], equals('')); expect(json['previewOnly'], isTrue); expect(json['intent'], isA>()); final intent = json['intent'] as Map; expect(intent['kind'], equals('payout')); expect(intent['settlement_mode'], equals('fix_received')); expect(intent['comment'], equals('invoice-7')); expect(intent.containsKey('settlement_currency'), isFalse); final source = intent['source'] as Map; final destination = intent['destination'] as Map; 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; final intent = json['intent'] as Map; final fx = intent['fx'] as Map; 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', clientPaymentRef: 'cp-1', ); final json = request.toJson(); expect(json['idempotencyKey'], equals('idem-2')); expect(json['quoteRef'], equals('q-1')); expect(json['clientPaymentRef'], equals('cp-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', clientPaymentRef: 'cp-1', ); final json = request.toJson(); expect(json['idempotencyKey'], equals('idem-3')); expect(json['quoteRef'], equals('q-2')); expect(json['clientPaymentRef'], equals('cp-1')); expect(json.containsKey('intentRef'), isFalse); expect(json.containsKey('intentRefs'), isFalse); }); }); }