import 'dart:developer'; import 'package:data_channel/data_channel.dart'; import 'package:dio/dio.dart'; import 'package:injectable/injectable.dart'; import '../../../common/api/api_client.dart'; import '../../../common/api/api_failure.dart'; import '../../../common/api/interceptors/sensitive_aware_log_interceptor.dart'; import '../../../common/extension/extension.dart'; import '../../../common/function/app_function.dart'; import '../../../common/url/api_path.dart'; import '../../../domain/wallet/wallet.dart'; import '../wallet_dtos.dart'; import '../wallet_response.dart'; @injectable class WalletRemoteDataProvider { final ApiClient _apiClient; final String _logName = 'WalletRemoteDataProvider'; WalletRemoteDataProvider(this._apiClient); Future> fetchWallet() => _request( 'fetchWallet', () => _apiClient.get(ApiPath.wallet, headers: getAuthorizationHeader()), (response) => WalletSummaryDto.fromJson(walletDataOf(response)), ); Future> fetchTransactions({ required int page, required int limit, WalletCurrency? currency, List types = const [], DateTime? from, DateTime? to, }) => _request( 'fetchTransactions', () => _apiClient.get( ApiPath.walletTransactions, headers: getAuthorizationHeader(), params: { 'page': page, 'limit': limit, if (currency != null) 'currency': currency.apiValue, if (types.isNotEmpty) 'type': types.map((e) => e.apiValue).join(','), if (from != null) 'from': from.toServerDate, if (to != null) 'to': to.toServerDate, }, ), _parseTransactionPage, ); Future> fetchExpiring() => _request( 'fetchExpiring', () => _apiClient.get( ApiPath.walletExpiring, headers: getAuthorizationHeader(), ), (response) => WalletExpiringDto.fromJson(walletDataOf(response)), ); Future> createPaymentCode({ required String pin, }) => _request( 'createPaymentCode', () => _apiClient.post( ApiPath.walletPaymentCode, headers: getAuthorizationHeader(), data: {'pin': pin}, extra: {kSensitiveRequest: true}, ), (response) => PaymentCodeDto.fromJson(walletDataOf(response)), ); Future> previewExchange({ required int coins, }) => _request( 'previewExchange', () => _apiClient.get( ApiPath.walletExchangePreview, headers: getAuthorizationHeader(), params: {'coins': coins}, ), (response) => ExchangePreviewDto.fromJson(walletDataOf(response)), ); Future> exchange({ required int coins, required String pin, required String idempotencyKey, }) => _request( 'exchange', () => _apiClient.post( ApiPath.walletExchange, headers: {...getAuthorizationHeader(), 'Idempotency-Key': idempotencyKey}, data: {'coins': coins, 'pin': pin}, extra: {kSensitiveRequest: true}, ), (response) => ExchangeResultDto.fromJson(walletDataOf(response)), ); Future> checkRecipient({ required String phone, }) => _request( 'checkRecipient', () => _apiClient.get( ApiPath.walletTransferRecipient, headers: getAuthorizationHeader(), params: {'phone': phone}, ), (response) => TransferRecipientDto.fromJson(walletDataOf(response)), ); Future> transfer({ required WalletCurrency currency, required int amount, required String recipientPhone, required String pin, required String idempotencyKey, }) => _request( 'transfer', () => _apiClient.post( ApiPath.walletTransfer, headers: {...getAuthorizationHeader(), 'Idempotency-Key': idempotencyKey}, data: { 'currency': currency.apiValue, 'amount': amount, 'recipient_phone': recipientPhone, 'pin': pin, }, extra: {kSensitiveRequest: true}, ), (response) => TransferResultDto.fromJson(walletDataOf(response)), ); /// `data` bisa berupa list langsung dengan `pagination` di envelope, atau /// objek `{ data, pagination }`. WalletTransactionPageDto _parseTransactionPage(Response response) { final body = response.data as Map; final payload = body['data']; final rawItems = payload is Map ? payload['data'] : payload; final rawPagination = payload is Map ? payload['pagination'] : body['pagination']; return WalletTransactionPageDto( items: (rawItems as List? ?? []) .map( (e) => WalletTransactionDto.fromJson( Map.from(e as Map), ), ) .toList(), pagination: rawPagination is Map ? WalletPaginationDto.fromJson( Map.from(rawPagination), ) : const WalletPaginationDto(), ); } Future> _request( String name, Future Function() call, T Function(Response response) parse, ) async { try { final response = await call(); final failure = parseWalletFailure(response); if (failure != null) return DC.error(failure); return DC.data(parse(response)); } on ApiFailure catch (e, s) { log(name, name: _logName, error: e, stackTrace: s); return DC.error(WalletFailure.serverError(e)); } } }