parse static method
Parses a VietQR payload string and extracts all the information.
This method analyzes a VietQR payload string and returns a VietQRParsedData object containing all the extracted information including bank details, account number, amount, message, and other metadata.
Parameters:
payload
: The VietQR payload string to parse
Returns a VietQRParsedData object with all parsed information.
Throws FormatException if the payload is malformed or invalid.
Example:
try {
final parsedData = VietQR.parse('0002010102113802A000000727...');
print('Bank: ${parsedData.bankName}');
print('Account: ${parsedData.accountNumber}');
print('Amount: ${parsedData.formattedAmount}');
} catch (e) {
print('Invalid VietQR payload: $e');
}
Implementation
static VietQRParsedData parse(String payload) {
if (payload.isEmpty) {
throw FormatException('Payload cannot be empty');
}
// Validate CRC first
_validateCRC(payload);
final fields = <String, String>{};
int index = 0;
// Parse all TLV fields
while (index < payload.length - 4) {
final fieldId = payload.substring(index, index + 2);
final lengthStr = payload.substring(index + 2, index + 4);
if (!RegExp(r'^\d{2}$').hasMatch(lengthStr)) {
throw FormatException('Invalid length format at position $index');
}
final length = int.parse(lengthStr);
if (index + 4 + length > payload.length) {
throw FormatException('Field $fieldId extends beyond payload length');
}
final value = payload.substring(index + 4, index + 4 + length);
fields[fieldId] = value;
index += 4 + length;
}
// Extract and validate required fields
final payloadFormat = fields['00'] ?? '';
final pointOfInitiation = fields['01'] ?? '';
final merchantAccount = fields['38'] ?? '';
final currency = fields['53'] ?? '';
final amount = fields['54'];
final countryCode = fields['58'] ?? '';
final additionalData = fields['62'];
final crc = fields['63'] ?? '';
// Validate required fields
if (payloadFormat != '01') {
throw FormatException('Invalid payload format: $payloadFormat');
}
if (pointOfInitiation != '11' && pointOfInitiation != '12') {
throw FormatException('Invalid point of initiation: $pointOfInitiation');
}
if (currency != '704') {
throw FormatException('Invalid currency code: $currency');
}
if (countryCode != 'VN') {
throw FormatException('Invalid country code: $countryCode');
}
// Parse merchant account information
final merchantInfo = _parseMerchantAccountInfo(merchantAccount);
// Parse additional data (message)
String? message;
if (additionalData != null && additionalData.isNotEmpty) {
message = _parseAdditionalData(additionalData);
}
// Parse amount
double? parsedAmount;
if (amount != null && amount.isNotEmpty) {
try {
parsedAmount = double.parse(amount);
} catch (e) {
throw FormatException('Invalid amount format: $amount');
}
}
// Determine if dynamic QR
final isDynamic = pointOfInitiation == '12';
// Find matching bank
Bank? bank;
try {
bank = Bank.values.firstWhere(
(b) => b.bin == merchantInfo['bankBin'],
orElse: () => throw StateError('Bank not found'),
);
} catch (e) {
// Bank not found in enum, will be null
}
return VietQRParsedData(
bank: bank,
bankBin: merchantInfo['bankBin']!,
accountNumber: merchantInfo['accountNumber']!,
amount: parsedAmount,
message: message,
isDynamic: isDynamic,
payloadFormat: payloadFormat,
pointOfInitiation: pointOfInitiation,
currency: currency,
countryCode: countryCode,
crc: crc,
);
}