migration to address book service
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
import 'package:pshared/models/describable.dart';
|
import 'package:pshared/models/describable.dart';
|
||||||
import 'package:pshared/models/payment/type.dart';
|
import 'package:pshared/models/payment/methods/card.dart';
|
||||||
|
import 'package:pshared/models/payment/methods/crypto_address.dart';
|
||||||
import 'package:pshared/models/payment/methods/data.dart';
|
import 'package:pshared/models/payment/methods/data.dart';
|
||||||
|
import 'package:pshared/models/payment/methods/iban.dart';
|
||||||
|
import 'package:pshared/models/payment/methods/russian_bank.dart';
|
||||||
|
import 'package:pshared/models/payment/methods/wallet.dart';
|
||||||
|
import 'package:pshared/models/payment/type.dart';
|
||||||
import 'package:pshared/models/permissions/bound.dart';
|
import 'package:pshared/models/permissions/bound.dart';
|
||||||
import 'package:pshared/models/permissions/bound/storable.dart';
|
import 'package:pshared/models/permissions/bound/storable.dart';
|
||||||
import 'package:pshared/models/storable.dart';
|
import 'package:pshared/models/storable.dart';
|
||||||
@@ -28,6 +32,19 @@ class PaymentMethod implements PermissionBoundStorable, Describable {
|
|||||||
|
|
||||||
PaymentType get type => data.type;
|
PaymentType get type => data.type;
|
||||||
|
|
||||||
|
T dataAs<T extends PaymentMethodData>() {
|
||||||
|
final currentData = data;
|
||||||
|
if (currentData is T) return currentData;
|
||||||
|
throw StateError('Payment method data is ${currentData.runtimeType}, requested $T for type $type');
|
||||||
|
}
|
||||||
|
T? dataAsOrNull<T extends PaymentMethodData>() => data is T ? data as T : null;
|
||||||
|
|
||||||
|
CardPaymentMethod? get cardData => dataAsOrNull<CardPaymentMethod>();
|
||||||
|
IbanPaymentMethod? get ibanData => dataAsOrNull<IbanPaymentMethod>();
|
||||||
|
RussianBankAccountPaymentMethod? get bankAccountData => dataAsOrNull<RussianBankAccountPaymentMethod>();
|
||||||
|
WalletPaymentMethod? get walletData => dataAsOrNull<WalletPaymentMethod>();
|
||||||
|
CryptoAddressPaymentMethod? get cryptoAddressData => dataAsOrNull<CryptoAddressPaymentMethod>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get id => storable.id;
|
String get id => storable.id;
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -10,17 +10,19 @@ import 'package:pshared/service/recipient/pmethods.dart';
|
|||||||
|
|
||||||
class PaymentMethodsProvider extends GenericProvider<PaymentMethod> {
|
class PaymentMethodsProvider extends GenericProvider<PaymentMethod> {
|
||||||
late OrganizationsProvider _organizations;
|
late OrganizationsProvider _organizations;
|
||||||
late RecipientsProvider _recipients;
|
|
||||||
|
|
||||||
PaymentMethodsProvider() : super(service: PaymentMethodService.basicService);
|
PaymentMethodsProvider() : super(service: PaymentMethodService.basicService);
|
||||||
|
|
||||||
List<PaymentMethod> get methods => List<PaymentMethod>.unmodifiable(items.toList()..sort((a, b) => a.storable.createdAt.compareTo(b.storable.createdAt)));
|
List<PaymentMethod> get methods => List<PaymentMethod>.unmodifiable(items.toList()..sort((a, b) => a.storable.createdAt.compareTo(b.storable.createdAt)));
|
||||||
|
|
||||||
void updateProviders(OrganizationsProvider organizations, RecipientsProvider recipients) {
|
void updateProviders(OrganizationsProvider organizations, RecipientsProvider recipients) {
|
||||||
|
if (recipients.currentObject != null) loadMethods(organizations, recipients.currentObject?.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadMethods(OrganizationsProvider organizations, String? recipientRef) async {
|
||||||
_organizations = organizations;
|
_organizations = organizations;
|
||||||
_recipients = recipients;
|
if (_organizations.isOrganizationSet && (recipientRef != null)) {
|
||||||
if (_organizations.isOrganizationSet && (_recipients.currentObject != null)) {
|
return load(_organizations.current.id, recipientRef);
|
||||||
load(_organizations.current.id, _recipients.currentObject!.id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,16 +2,12 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/methods/card.dart';
|
|
||||||
import 'package:pshared/models/payment/methods/crypto_address.dart';
|
|
||||||
import 'package:pshared/models/payment/methods/iban.dart';
|
|
||||||
import 'package:pshared/models/payment/methods/russian_bank.dart';
|
|
||||||
import 'package:pshared/models/payment/methods/wallet.dart';
|
|
||||||
import 'package:pshared/models/payment/type.dart';
|
import 'package:pshared/models/payment/type.dart';
|
||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.dart';
|
||||||
import 'package:pshared/models/recipient/status.dart';
|
import 'package:pshared/models/recipient/status.dart';
|
||||||
import 'package:pshared/models/recipient/type.dart';
|
import 'package:pshared/models/recipient/type.dart';
|
||||||
import 'package:pshared/provider/organizations.dart';
|
import 'package:pshared/provider/organizations.dart';
|
||||||
|
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||||
|
|
||||||
import 'package:pweb/pages/address_book/form/view.dart';
|
import 'package:pweb/pages/address_book/form/view.dart';
|
||||||
import 'package:pweb/services/amplitude.dart';
|
import 'package:pweb/services/amplitude.dart';
|
||||||
@@ -36,6 +32,25 @@ class _AdressBookRecipientFormState extends State<AdressBookRecipientForm> {
|
|||||||
RecipientType _type = RecipientType.internal;
|
RecipientType _type = RecipientType.internal;
|
||||||
RecipientStatus _status = RecipientStatus.ready;
|
RecipientStatus _status = RecipientStatus.ready;
|
||||||
final Map<PaymentType, Object?> _methods = {};
|
final Map<PaymentType, Object?> _methods = {};
|
||||||
|
late PaymentMethodsProvider _methodsProvider;
|
||||||
|
|
||||||
|
Future<void> _loadMethods() async {
|
||||||
|
_methodsProvider = PaymentMethodsProvider()..addListener(_onProviderChanged);
|
||||||
|
await _methodsProvider.loadMethods(
|
||||||
|
context.read<OrganizationsProvider>(),
|
||||||
|
widget.recipient?.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (final m in _methodsProvider.methods) {
|
||||||
|
_methods[m.type] = switch (m.type) {
|
||||||
|
PaymentType.card => m.cardData,
|
||||||
|
PaymentType.iban => m.ibanData,
|
||||||
|
PaymentType.wallet => m.walletData,
|
||||||
|
PaymentType.bankAccount => m.bankAccountData,
|
||||||
|
PaymentType.cryptoAddress => m.cryptoAddressData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -45,12 +60,7 @@ class _AdressBookRecipientFormState extends State<AdressBookRecipientForm> {
|
|||||||
_emailCtrl = TextEditingController(text: r?.email ?? '');
|
_emailCtrl = TextEditingController(text: r?.email ?? '');
|
||||||
_type = r?.type ?? RecipientType.internal;
|
_type = r?.type ?? RecipientType.internal;
|
||||||
_status = r?.status ?? RecipientStatus.ready;
|
_status = r?.status ?? RecipientStatus.ready;
|
||||||
|
_loadMethods();
|
||||||
if (r?.card != null) _methods[PaymentType.card] = r!.card;
|
|
||||||
if (r?.iban != null) _methods[PaymentType.iban] = r!.iban;
|
|
||||||
if (r?.wallet != null) _methods[PaymentType.wallet] = r!.wallet;
|
|
||||||
if (r?.bank != null) _methods[PaymentType.bankAccount] = r!.bank;
|
|
||||||
if (r?.cryptoAddress != null) _methods[PaymentType.cryptoAddress] = r!.cryptoAddress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Change when registration is ready
|
//TODO: Change when registration is ready
|
||||||
@@ -81,6 +91,15 @@ class _AdressBookRecipientFormState extends State<AdressBookRecipientForm> {
|
|||||||
widget.onSaved?.call(recipient);
|
widget.onSaved?.call(recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_methodsProvider.removeListener(_onProviderChanged);
|
||||||
|
_methodsProvider.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onProviderChanged() => setState(() {});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => FormView(
|
Widget build(BuildContext context) => FormView(
|
||||||
formKey: _formKey,
|
formKey: _formKey,
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import 'package:provider/provider.dart';
|
|||||||
|
|
||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.dart';
|
||||||
import 'package:pshared/models/recipient/filter.dart';
|
import 'package:pshared/models/recipient/filter.dart';
|
||||||
|
import 'package:pshared/provider/recipient/provider.dart';
|
||||||
|
|
||||||
import 'package:pweb/pages/address_book/page/filter_button.dart';
|
import 'package:pweb/pages/address_book/page/filter_button.dart';
|
||||||
import 'package:pweb/pages/address_book/page/header.dart';
|
import 'package:pweb/pages/address_book/page/header.dart';
|
||||||
import 'package:pweb/pages/address_book/page/list.dart';
|
import 'package:pweb/pages/address_book/page/list.dart';
|
||||||
import 'package:pweb/pages/address_book/page/search.dart';
|
import 'package:pweb/pages/address_book/page/search.dart';
|
||||||
import 'package:pweb/providers/recipient.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ class _RecipientAddressBookPageState extends State<RecipientAddressBookPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final provider = context.read<RecipientProvider>();
|
final provider = context.read<RecipientsProvider>();
|
||||||
_searchController = TextEditingController(text: provider.query);
|
_searchController = TextEditingController(text: provider.query);
|
||||||
_searchFocusNode = FocusNode();
|
_searchFocusNode = FocusNode();
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ class _RecipientAddressBookPageState extends State<RecipientAddressBookPage> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _syncSearchField(RecipientProvider provider) {
|
void _syncSearchField(RecipientsProvider provider) {
|
||||||
final query = provider.query;
|
final query = provider.query;
|
||||||
if (_searchController.text == query) return;
|
if (_searchController.text == query) return;
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ class _RecipientAddressBookPageState extends State<RecipientAddressBookPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
final loc = AppLocalizations.of(context)!;
|
final loc = AppLocalizations.of(context)!;
|
||||||
final provider = context.watch<RecipientProvider>();
|
final provider = context.watch<RecipientsProvider>();
|
||||||
_syncSearchField(provider);
|
_syncSearchField(provider);
|
||||||
|
|
||||||
if (provider.isLoading) {
|
if (provider.isLoading) {
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/payment/type.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.dart';
|
||||||
|
import 'package:pshared/provider/organizations.dart';
|
||||||
|
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||||
|
|
||||||
import 'package:pweb/pages/address_book/page/recipient/info_row.dart';
|
import 'package:pweb/pages/address_book/page/recipient/info_row.dart';
|
||||||
|
import 'package:pweb/utils/payment/label.dart';
|
||||||
|
|
||||||
|
|
||||||
class RecipientPaymentRow extends StatelessWidget {
|
class RecipientPaymentRow extends StatefulWidget {
|
||||||
final Recipient recipient;
|
final Recipient recipient;
|
||||||
final double spacing;
|
final double spacing;
|
||||||
|
|
||||||
@@ -16,37 +20,43 @@ class RecipientPaymentRow extends StatelessWidget {
|
|||||||
this.spacing = 18
|
this.spacing = 18
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RecipientPaymentRow> createState() => _RecipientPaymentRowState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RecipientPaymentRowState extends State<RecipientPaymentRow> {
|
||||||
|
late final PaymentMethodsProvider _methodsProvider;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_methodsProvider = PaymentMethodsProvider()
|
||||||
|
..addListener(_onProviderChanged)
|
||||||
|
..loadMethods(
|
||||||
|
context.read<OrganizationsProvider>(),
|
||||||
|
widget.recipient.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_methodsProvider.removeListener(_onProviderChanged);
|
||||||
|
_methodsProvider.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onProviderChanged() => setState(() {});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
if (!_methodsProvider.isReady) return const Center(child: CircularProgressIndicator());
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
spacing: spacing,
|
spacing: widget.spacing,
|
||||||
children: [
|
children: _methodsProvider.methods.map((m) => RecipientAddressBookInfoRow(
|
||||||
if (recipient.bank?.accountNumber.isNotEmpty ?? false)
|
type: m.type,
|
||||||
RecipientAddressBookInfoRow(
|
value: getPaymentTypeDescription(context, m),
|
||||||
type: PaymentType.bankAccount,
|
)).toList(),
|
||||||
value: recipient.bank!.accountNumber
|
|
||||||
),
|
|
||||||
if (recipient.card?.pan.isNotEmpty ?? false)
|
|
||||||
RecipientAddressBookInfoRow(
|
|
||||||
type: PaymentType.card,
|
|
||||||
value: recipient.card!.pan
|
|
||||||
),
|
|
||||||
if (recipient.iban?.iban.isNotEmpty ?? false)
|
|
||||||
RecipientAddressBookInfoRow(
|
|
||||||
type: PaymentType.iban,
|
|
||||||
value: recipient.iban!.iban
|
|
||||||
),
|
|
||||||
if (recipient.wallet?.walletId.isNotEmpty ?? false)
|
|
||||||
RecipientAddressBookInfoRow(
|
|
||||||
type: PaymentType.wallet,
|
|
||||||
value: recipient.wallet!.walletId
|
|
||||||
),
|
|
||||||
if (recipient.cryptoAddress?.address.isNotEmpty ?? false)
|
|
||||||
RecipientAddressBookInfoRow(
|
|
||||||
type: PaymentType.cryptoAddress,
|
|
||||||
value: recipient.cryptoAddress!.address,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.dart';
|
||||||
import 'package:pshared/provider/organizations.dart';
|
import 'package:pshared/provider/organizations.dart';
|
||||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
import 'package:pshared/provider/recipient/pmethods.dart';
|
||||||
import 'package:pshared/provider/recipient/provider.dart';
|
|
||||||
|
|
||||||
import 'package:pweb/pages/dashboard/payouts/single/adress_book/avatar.dart';
|
import 'package:pweb/pages/dashboard/payouts/single/adress_book/avatar.dart';
|
||||||
import 'package:pweb/pages/dashboard/payouts/single/adress_book/long_list/info_row.dart';
|
import 'package:pweb/pages/dashboard/payouts/single/adress_book/long_list/info_row.dart';
|
||||||
@@ -37,13 +36,24 @@ class _RecipientItemState extends State<RecipientItem> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_methodsProvider = PaymentMethodsProvider();
|
_methodsProvider = PaymentMethodsProvider()
|
||||||
_methodsProvider.updateProviders(
|
..addListener(_onProviderChanged)
|
||||||
context.read<OrganizationsProvider>(),
|
..loadMethods(
|
||||||
context.read<RecipientsProvider>(),
|
context.read<OrganizationsProvider>(),
|
||||||
);
|
widget.recipient.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_methodsProvider.removeListener(_onProviderChanged);
|
||||||
|
_methodsProvider.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onProviderChanged() => setState(() {});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (!_methodsProvider.isReady) return const Center(child: CircularProgressIndicator());
|
if (!_methodsProvider.isReady) return const Center(child: CircularProgressIndicator());
|
||||||
@@ -78,7 +88,7 @@ class _RecipientItemState extends State<RecipientItem> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: _methodsProvider.methods.map((m) => PaymentInfoRow(
|
children: _methodsProvider.methods.map((m) => PaymentInfoRow(
|
||||||
label: getPaymentTypeLabel(context, m.type),
|
label: getPaymentTypeLabel(context, m.type),
|
||||||
value: _displayString(m),
|
value: getPaymentTypeDescription(context, m),
|
||||||
)).toList(),
|
)).toList(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'package:pshared/models/recipient/recipient.dart';
|
import 'package:pshared/models/recipient/recipient.dart';
|
||||||
import 'package:pshared/provider/recipient/pmethods.dart';
|
|
||||||
import 'package:pshared/provider/recipient/provider.dart';
|
import 'package:pshared/provider/recipient/provider.dart';
|
||||||
|
|
||||||
import 'package:pweb/providers/payment_flow_provider.dart';
|
import 'package:pweb/providers/payment_flow_provider.dart';
|
||||||
@@ -48,8 +47,6 @@ class _PaymentPageState extends State<PaymentPage> {
|
|||||||
|
|
||||||
void _initializePaymentPage() {
|
void _initializePaymentPage() {
|
||||||
final pageSelector = context.read<PageSelectorProvider>();
|
final pageSelector = context.read<PageSelectorProvider>();
|
||||||
final methodsProvider = context.read<PaymentMethodsProvider>();
|
|
||||||
final recipientProvider = context.read<RecipientsProvider>();
|
|
||||||
|
|
||||||
pageSelector.handleWalletAutoSelection();
|
pageSelector.handleWalletAutoSelection();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
|
|
||||||
import 'package:pshared/models/payment/methods/type.dart';
|
import 'package:pshared/models/payment/methods/type.dart';
|
||||||
import 'package:pshared/models/payment/type.dart';
|
import 'package:pshared/models/payment/type.dart';
|
||||||
@@ -16,8 +15,6 @@ import 'package:pweb/widgets/sidebar/destinations.dart';
|
|||||||
|
|
||||||
|
|
||||||
class PageSelectorProvider extends ChangeNotifier {
|
class PageSelectorProvider extends ChangeNotifier {
|
||||||
static final _logger = Logger('provider.page_selector');
|
|
||||||
|
|
||||||
PayoutDestination _selected = PayoutDestination.dashboard;
|
PayoutDestination _selected = PayoutDestination.dashboard;
|
||||||
PaymentType? _type;
|
PaymentType? _type;
|
||||||
bool _cameFromRecipientList = false;
|
bool _cameFromRecipientList = false;
|
||||||
@@ -65,7 +62,7 @@ class PageSelectorProvider extends ChangeNotifier {
|
|||||||
|
|
||||||
void goToAddRecipient() {
|
void goToAddRecipient() {
|
||||||
AmplitudeService.recipientAddStarted();
|
AmplitudeService.recipientAddStarted();
|
||||||
recipientProvider!.setCurrentObject(null);
|
recipientProvider.setCurrentObject(null);
|
||||||
_selected = PayoutDestination.addrecipient;
|
_selected = PayoutDestination.addrecipient;
|
||||||
_cameFromRecipientList = false;
|
_cameFromRecipientList = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@@ -114,7 +111,7 @@ class PageSelectorProvider extends ChangeNotifier {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return methodsProvider!.methods.firstWhereOrNull(
|
return methodsProvider.methods.firstWhereOrNull(
|
||||||
(method) => method.type == PaymentType.wallet &&
|
(method) => method.type == PaymentType.wallet &&
|
||||||
(method.description?.contains(wallet.walletUserID) ?? false),
|
(method.description?.contains(wallet.walletUserID) ?? false),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'package:pshared/models/payment/methods/type.dart';
|
||||||
import 'package:pshared/models/payment/type.dart';
|
import 'package:pshared/models/payment/type.dart';
|
||||||
|
|
||||||
|
import 'package:pweb/utils/payment/masking.dart';
|
||||||
|
|
||||||
import 'package:pweb/generated/i18n/app_localizations.dart';
|
import 'package:pweb/generated/i18n/app_localizations.dart';
|
||||||
|
|
||||||
|
|
||||||
@@ -15,3 +18,15 @@ String getPaymentTypeLabel(BuildContext context, PaymentType type) {
|
|||||||
PaymentType.cryptoAddress => l10n.paymentTypeCryptoAddress,
|
PaymentType.cryptoAddress => l10n.paymentTypeCryptoAddress,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? _displayString(PaymentMethod m) => switch (m.type) {
|
||||||
|
PaymentType.card => maskCardNumber(m.cardData?.pan),
|
||||||
|
PaymentType.bankAccount => m.bankAccountData?.accountNumber,
|
||||||
|
PaymentType.iban => m.ibanData?.iban,
|
||||||
|
PaymentType.wallet => m.walletData?.walletId,
|
||||||
|
PaymentType.cryptoAddress => m.cryptoAddressData?.address,
|
||||||
|
};
|
||||||
|
|
||||||
|
String getPaymentTypeDescription(BuildContext context, PaymentMethod m) {
|
||||||
|
return _displayString(m) ?? AppLocalizations.of(context)!.notSet;
|
||||||
|
}
|
||||||
25
frontend/pweb/lib/utils/payment/masking.dart
Normal file
25
frontend/pweb/lib/utils/payment/masking.dart
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
|
||||||
|
/// Masks a card number, leaving only the last 4 digits visible.
|
||||||
|
/// Returns 'N/A' when the input is null or empty.
|
||||||
|
String? maskCardNumber(String? pan) {
|
||||||
|
if (pan == null || pan.isEmpty) return null;
|
||||||
|
|
||||||
|
// Strip non-digits to avoid leaking formatting patterns.
|
||||||
|
final digits = pan.replaceAll(RegExp(r'\D'), '');
|
||||||
|
if (digits.length <= 4) return digits;
|
||||||
|
|
||||||
|
final last4 = digits.substring(digits.length - 4);
|
||||||
|
final maskedPrefix = '*' * (digits.length - 4);
|
||||||
|
final masked = '$maskedPrefix$last4';
|
||||||
|
|
||||||
|
// Group into blocks of 4 for readability.
|
||||||
|
final groups = <String>[];
|
||||||
|
for (var i = 0; i < masked.length; i += 4) {
|
||||||
|
final end = min(i + 4, masked.length);
|
||||||
|
groups.add(masked.substring(i, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups.join(' ');
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user