256 lines
6.5 KiB
Dart
256 lines
6.5 KiB
Dart
enum DestinationType { crypto, card, russianBank }
|
|
|
|
enum EligibilityState {
|
|
eligible,
|
|
temporarilyUnavailable,
|
|
requiresAction,
|
|
unsupported,
|
|
}
|
|
|
|
enum DestinationIssue {
|
|
invalid,
|
|
unavailable,
|
|
uncertain,
|
|
conflict,
|
|
removed,
|
|
expired,
|
|
bankChanged,
|
|
authorization,
|
|
unsupported,
|
|
duplicate,
|
|
replacementRequired,
|
|
}
|
|
|
|
enum SaveOutcome { created, duplicate }
|
|
|
|
enum BindingState { ready, expired, revoked, unavailable }
|
|
|
|
class DestinationException implements Exception {
|
|
const DestinationException(this.issue, {this.field});
|
|
final DestinationIssue issue;
|
|
final String? field;
|
|
}
|
|
|
|
sealed class DestinationDetails {
|
|
const DestinationDetails();
|
|
DestinationType get type;
|
|
String get display;
|
|
String get review;
|
|
}
|
|
|
|
class CryptoDestination extends DestinationDetails {
|
|
const CryptoDestination({
|
|
required this.assetId,
|
|
required this.networkId,
|
|
required this.address,
|
|
this.memo = '',
|
|
required this.assetLabel,
|
|
required this.networkLabel,
|
|
});
|
|
final String assetId, networkId, address, memo, assetLabel, networkLabel;
|
|
@override
|
|
DestinationType get type => DestinationType.crypto;
|
|
@override
|
|
String get display =>
|
|
'$assetLabel · $networkLabel · ${address.length > 14 ? '${address.substring(0, 6)}…${address.substring(address.length - 6)}' : address}';
|
|
@override
|
|
String get review =>
|
|
'$assetLabel · $networkLabel\n$address${memo.isEmpty ? '' : '\n$memo'}';
|
|
}
|
|
|
|
/// Secure capture references are short lived, not PAN/CVV or destination IDs.
|
|
class CardDestination extends DestinationDetails {
|
|
const CardDestination({
|
|
this.capture = '',
|
|
this.identity = '',
|
|
required this.brand,
|
|
required this.last4,
|
|
required this.expiryMonth,
|
|
required this.expiryYear,
|
|
});
|
|
final String capture, identity, brand, last4;
|
|
final int expiryMonth, expiryYear;
|
|
@override
|
|
DestinationType get type => DestinationType.card;
|
|
@override
|
|
String get display => '$brand · •••• $last4';
|
|
@override
|
|
String get review => display;
|
|
}
|
|
|
|
class RussianBankDestination extends DestinationDetails {
|
|
const RussianBankDestination({
|
|
required this.firstName,
|
|
required this.lastName,
|
|
this.patronymic = '',
|
|
required this.bank,
|
|
required this.account,
|
|
});
|
|
final String firstName, lastName, patronymic, account;
|
|
final Bank bank;
|
|
String get holder =>
|
|
[firstName, lastName, patronymic].where((s) => s.isNotEmpty).join(' ');
|
|
@override
|
|
DestinationType get type => DestinationType.russianBank;
|
|
@override
|
|
String get display =>
|
|
'${bank.name} · •••• ${account.length < 4 ? account : account.substring(account.length - 4)}';
|
|
@override
|
|
String get review =>
|
|
'$holder\n${bank.name}\n${bank.bik}\n$account\n${bank.correspondent}';
|
|
}
|
|
|
|
class Bank {
|
|
const Bank({
|
|
required this.bik,
|
|
required this.name,
|
|
required this.city,
|
|
required this.correspondent,
|
|
required this.revision,
|
|
this.supported = true,
|
|
});
|
|
final String bik, name, city, correspondent, revision;
|
|
final bool supported;
|
|
}
|
|
|
|
class CryptoAsset {
|
|
const CryptoAsset(
|
|
this.id,
|
|
this.label,
|
|
this.networkId,
|
|
this.networkLabel, {
|
|
this.memoRequired = false,
|
|
this.memoSupported = false,
|
|
});
|
|
final String id, label, networkId, networkLabel;
|
|
final bool memoRequired, memoSupported;
|
|
}
|
|
|
|
class ProviderBinding {
|
|
const ProviderBinding({
|
|
required this.reference,
|
|
required this.state,
|
|
required this.reusable,
|
|
this.recoverable = false,
|
|
this.expiresAt,
|
|
});
|
|
final String reference;
|
|
final BindingState state;
|
|
final bool reusable, recoverable;
|
|
final DateTime? expiresAt;
|
|
}
|
|
|
|
class OwnershipAssessment {
|
|
const OwnershipAssessment({
|
|
this.declaredOwn = true,
|
|
this.verified = false,
|
|
this.actionRequired = false,
|
|
});
|
|
final bool declaredOwn, verified, actionRequired;
|
|
}
|
|
|
|
class SavedDestination {
|
|
SavedDestination({
|
|
required this.id,
|
|
required this.details,
|
|
this.nickname = '',
|
|
this.metadataRevision = 1,
|
|
this.executionRevision = 1,
|
|
this.removed = false,
|
|
this.lastUsed,
|
|
this.ownership = const OwnershipAssessment(),
|
|
List<ProviderBinding> bindings = const [],
|
|
}) : bindings = List.unmodifiable(bindings);
|
|
final String id, nickname;
|
|
final DestinationDetails details;
|
|
final int metadataRevision, executionRevision;
|
|
final bool removed;
|
|
final DateTime? lastUsed;
|
|
final OwnershipAssessment ownership;
|
|
final List<ProviderBinding> bindings;
|
|
String get label => nickname.isEmpty ? details.display : nickname;
|
|
SavedReference get input => SavedReference(id, executionRevision);
|
|
}
|
|
|
|
sealed class DestinationInput {
|
|
const DestinationInput();
|
|
}
|
|
|
|
class SavedReference extends DestinationInput {
|
|
const SavedReference(this.id, this.executionRevision);
|
|
final String id;
|
|
final int executionRevision;
|
|
}
|
|
|
|
class OneTimeDestination extends DestinationInput {
|
|
const OneTimeDestination(this.details);
|
|
final DestinationDetails details;
|
|
}
|
|
|
|
class PayoutContext {
|
|
const PayoutContext(this.source, {this.amount});
|
|
final String source;
|
|
final int? amount;
|
|
}
|
|
|
|
class PayoutEligibility {
|
|
const PayoutEligibility(this.state, {this.reason, this.canSave = true});
|
|
final EligibilityState state;
|
|
final DestinationIssue? reason;
|
|
final bool canSave;
|
|
bool get eligible => state == EligibilityState.eligible;
|
|
}
|
|
|
|
class DestinationValidation {
|
|
const DestinationValidation(this.details);
|
|
final DestinationDetails details;
|
|
}
|
|
|
|
class DestinationCapabilities {
|
|
DestinationCapabilities({
|
|
List<DestinationType> types = const [],
|
|
List<CryptoAsset> assets = const [],
|
|
this.reusableCards = false,
|
|
this.cardRecovery = false,
|
|
this.simulated = false,
|
|
}) : types = List.unmodifiable(types),
|
|
assets = List.unmodifiable(assets);
|
|
final List<DestinationType> types;
|
|
final List<CryptoAsset> assets;
|
|
final bool reusableCards, cardRecovery, simulated;
|
|
}
|
|
|
|
/// Outcome identity never changes when a record is later renamed or removed.
|
|
class CreationResult {
|
|
const CreationResult(this.outcome, this.destinationId);
|
|
final SaveOutcome outcome;
|
|
final String destinationId;
|
|
}
|
|
|
|
class DestinationLease {
|
|
const DestinationLease({
|
|
required this.input,
|
|
required this.snapshot,
|
|
required this.context,
|
|
required this.requirementsRevision,
|
|
required this.expiresAt,
|
|
});
|
|
final DestinationInput input;
|
|
final DestinationDetails snapshot;
|
|
final PayoutContext context;
|
|
final int requirementsRevision;
|
|
final DateTime expiresAt;
|
|
}
|
|
|
|
class DestinationAudit {
|
|
const DestinationAudit(
|
|
this.actor,
|
|
this.reference,
|
|
this.action,
|
|
this.outcome,
|
|
this.at,
|
|
);
|
|
final String actor, reference, action, outcome;
|
|
final DateTime at;
|
|
}
|