Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 3 additions & 12 deletions mobile-app/lib/services/pos_service.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
import 'package:quantus_sdk/quantus_sdk.dart';

class PosPaymentRequest {
final String paymentUrl;
final String refId;
final String amount;

const PosPaymentRequest({required this.paymentUrl, required this.refId, required this.amount});
const PosPaymentRequest({required this.paymentUrl, required this.refId});
}

class PosService {
final NumberFormattingService _formattingService;

PosService({NumberFormattingService? formattingService})
: _formattingService = formattingService ?? NumberFormattingService();

String generateRefId() {
final now = DateTime.now().millisecondsSinceEpoch;
return now.toRadixString(36).toUpperCase();
Expand All @@ -26,8 +18,7 @@ class PosService {

PosPaymentRequest createPaymentRequest({required String accountId, required BigInt amountPlanck}) {
final refId = generateRefId();
final wireAmount = _formattingService.formatWireAmount(amountPlanck);
final url = buildPaymentUrl(accountId: accountId, amount: wireAmount, refId: refId);
return PosPaymentRequest(paymentUrl: url, refId: refId, amount: wireAmount);
final url = buildPaymentUrl(accountId: accountId, amount: amountPlanck.toString(), refId: refId);
return PosPaymentRequest(paymentUrl: url, refId: refId);
}
}
8 changes: 4 additions & 4 deletions mobile-app/lib/v2/screens/pos/pos_qr_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ class _PosQrScreenState extends ConsumerState<PosQrScreen> {
final colors = context.colors;
final text = context.themeText;
final accountAsync = ref.watch(activeAccountProvider);
final formattingService = ref.watch(numberFormattingServiceProvider);
final display = ref.watch(txAmountDisplayProvider)(
widget.amountPlanck,
withSignPrefix: false,
Expand All @@ -176,9 +175,10 @@ class _PosQrScreenState extends ConsumerState<PosQrScreen> {
),
data: (active) {
if (active == null) return const Center(child: Text('No active account'));
_request ??= PosService(
formattingService: formattingService,
).createPaymentRequest(accountId: active.account.accountId, amountPlanck: widget.amountPlanck);
_request ??= PosService().createPaymentRequest(
accountId: active.account.accountId,
amountPlanck: widget.amountPlanck,
);
if (_isPaid) return _buildPaidContent(colors, text, display.primaryAmount);
return _buildQrContent(_request!, colors, text, display);
},
Expand Down
3 changes: 1 addition & 2 deletions mobile-app/lib/v2/screens/send/input_amount_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,8 @@ class _InputAmountScreenState extends ConsumerState<InputAmountScreen> {
assert(widget.recipientAddress.trim().isNotEmpty, 'InputAmountScreen requires a recipient');
_amountFocus.addListener(_onAmountFocusChanged);
if (widget.initialAmount != null && widget.initialAmount!.isNotEmpty) {
final formattingService = ref.read(numberFormattingServiceProvider);
final planck = widget.isPayMode
? formattingService.parseWireAmount(widget.initialAmount!) ?? BigInt.zero
? BigInt.tryParse(widget.initialAmount!) ?? BigInt.zero
: _amountInputLogic.parseQuanAmount(widget.initialAmount!);
if (planck > BigInt.zero) {
_amount = planck;
Expand Down
35 changes: 0 additions & 35 deletions mobile-app/test/unit/number_formatting_service_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,40 +90,5 @@ void main() {
});
});

group('formatWireAmount / parseWireAmount', () {
test('formatWireAmount always uses dot decimal without grouping', () {
final commaService = NumberFormattingService(localeConfig: LocaleNumberConfig.commaDecimal);
final balance = BigInt.parse('1500000000000');
expect(commaService.formatWireAmount(balance), '1.5');
});

test('round-trips canonical wire amounts', () {
final balance = BigInt.parse('1500000000000');
final wire = service.formatWireAmount(balance);
expect(wire, '1.5');
expect(service.parseWireAmount(wire), balance);
});

test('parses legacy comma-decimal amounts', () {
expect(service.parseWireAmount('1,5'), BigInt.parse('1500000000000'));
});

test('parses legacy dot-decimal amounts', () {
expect(service.parseWireAmount('1.5'), BigInt.parse('1500000000000'));
});

test('parses integer amounts', () {
expect(service.parseWireAmount('1000'), scaleFactor * BigInt.from(1000));
});

test('parses mixed separators using rightmost decimal mark', () {
expect(service.parseWireAmount('1.000,50'), BigInt.parse('1000500000000000'));
expect(service.parseWireAmount('1,000.50'), BigInt.parse('1000500000000000'));
});

test('returns zero for empty wire amount', () {
expect(service.parseWireAmount(''), BigInt.zero);
});
});
});
}
10 changes: 3 additions & 7 deletions mobile-app/test/unit/pos_service_test.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:quantus_sdk/quantus_sdk.dart';
import 'package:resonance_network_wallet/services/pos_service.dart';

void main() {
group('PosService', () {
test('createPaymentRequest embeds canonical dot-decimal wire amount', () {
final formattingService = NumberFormattingService(localeConfig: LocaleNumberConfig.commaDecimal);
final service = PosService(formattingService: formattingService);
test('createPaymentRequest embeds raw planck integer amount', () {
final service = PosService();
final amountPlanck = BigInt.parse('1500000000000');

final request = service.createPaymentRequest(accountId: 'account123', amountPlanck: amountPlanck);

expect(request.amount, '1.5');
expect(request.paymentUrl, contains('amount=1.5'));
expect(request.paymentUrl, isNot(contains('amount=1%2C5')));
expect(request.paymentUrl, contains('amount=1500000000000'));
expect(request.paymentUrl, contains('to=account123'));
expect(request.refId, isNotEmpty);
});
Expand Down
37 changes: 0 additions & 37 deletions quantus_sdk/lib/src/services/number_formatting_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,43 +58,6 @@ class NumberFormattingService {
return resultString;
}

/// Formats a balance for payment URL wire transport: dot decimal, no grouping.
///
/// Wire amounts are locale-neutral and must be parsed with [parseWireAmount].
String formatWireAmount(BigInt balance) {
return NumberFormattingService(
localeConfig: LocaleNumberConfig.dotDecimal,
).formatBalance(balance, maxDecimals: decimals, addThousandsSeparators: false);
}

/// Parses a payment URL amount without assuming the payer's locale.
///
/// Supports canonical dot-decimal wire amounts and legacy locale-formatted
/// amounts from older POS QR codes.
BigInt? parseWireAmount(String formattedAmount) {
if (formattedAmount.isEmpty) {
return BigInt.zero;
}

final config = _wireLocaleConfigFor(formattedAmount);
return NumberFormattingService(localeConfig: config).parseAmount(formattedAmount);
}

static LocaleNumberConfig _wireLocaleConfigFor(String input) {
final hasComma = input.contains(',');
final hasDot = input.contains('.');

if (hasComma && hasDot) {
final lastComma = input.lastIndexOf(',');
final lastDot = input.lastIndexOf('.');
return lastComma > lastDot ? LocaleNumberConfig.commaDecimal : LocaleNumberConfig.dotDecimal;
}
if (hasComma) {
return LocaleNumberConfig.commaDecimal;
}
return LocaleNumberConfig.dotDecimal;
}

/// Parses a user-entered formatted string amount into a raw BigInt amount
/// scaled by the chain's decimals.
///
Expand Down
Loading