validateRaastQr function

RaastQrValidation validateRaastQr(
  1. String payload, {
  2. DateTime? now,
  3. int expiresSoonMs = 3600000,
})

Checks whether a Raast QR code can be paid, e.g. before showing a payment screen or to warn about a tampered sticker.

Errors: not a Pakistani payment QR, checksum mismatch (damaged or edited), no IBAN or an IBAN that fails its checksum, expired, or a fixed-amount QR without the expiry that banking apps require.

Warnings: an amount in a static QR (apps ignore it, so the payer must type it), or a QR that expires within expiresSoonMs milliseconds (an hour by default) of now (defaults to DateTime.now).

Implementation

RaastQrValidation validateRaastQr(
  String payload, {
  DateTime? now,
  int expiresSoonMs = 3600000,
}) {
  final qr = parseRaastQr(payload);
  if (qr == null) {
    return const RaastQrValidation(
      valid: false,
      errors: [RaastQrError.notARaastQr],
      warnings: [],
      qr: null,
    );
  }

  final errors = <RaastQrError>[];
  final warnings = <RaastQrWarning>[];
  final time = now ?? DateTime.now();

  if (!qr.crcValid) errors.add(RaastQrError.badChecksum);
  if (qr.iban == null) {
    errors.add(RaastQrError.noIban);
  } else if (!qr.ibanValid) {
    errors.add(RaastQrError.invalidIban);
  }

  if (qr.kind == RaastQrKind.p2p) {
    if (qr.isDynamic && qr.expiresAt == null) {
      errors.add(RaastQrError.missingExpiry);
    }
    if (!qr.isDynamic && qr.amount != null) {
      warnings.add(RaastQrWarning.amountIgnored);
    }
    if (qr.expiresAt != null) {
      final remaining = _localDateTime(qr.expiresAt!).millisecondsSinceEpoch -
          time.millisecondsSinceEpoch;
      if (remaining <= 0) {
        errors.add(RaastQrError.expired);
      } else if (remaining <= expiresSoonMs) {
        warnings.add(RaastQrWarning.expiresSoon);
      }
    }
  }

  return RaastQrValidation(
    valid: errors.isEmpty,
    errors: List.unmodifiable(errors),
    warnings: List.unmodifiable(warnings),
    qr: qr,
  );
}