From a069754f6f5d5e4344a9cda6f564fe556dc7fac3 Mon Sep 17 00:00:00 2001 From: Mahdi Date: Tue, 18 Jun 2024 14:35:01 +0330 Subject: [PATCH] Pin Block, Card Entry Mode and POS Condition Code fields added. --- CHANGELOG.md | 4 ++ lib/src/message.dart | 128 +++++++++++++++++++++++++++++++++++-------- pubspec.yaml | 2 +- test/pos_test.dart | 35 ++---------- 4 files changed, 115 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 579e380..fec3b47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.7 + +* Pin Block, Card Entry Mode and POS Condition Code fields added. + ## 0.3.6 * Data Element (DE) for field 48. diff --git a/lib/src/message.dart b/lib/src/message.dart index 869aa1a..d4b557b 100644 --- a/lib/src/message.dart +++ b/lib/src/message.dart @@ -37,11 +37,11 @@ class Message { final x = Message('0300'); //x.set(1, '0300'); - x.set(3, [0x41, 0x00, 0x00]); + x.processCode = 0x410000; x.dateTime = now; - x.set(25, [0x14]); - x.set(49, [0x33, 0x36, 0x34]); // '364' in ASCII. + x.posConditionCode = '14'; + x.currency = 364; return x; } @@ -54,7 +54,7 @@ class Message { final am = amount.toString().padLeft(12, '0'); final amb = hex.decode(am); - x.set(3, [0, 0, 0]); + x.processCode = 0x000000; x.set(4, amb); x.dateTime = now; @@ -72,11 +72,11 @@ class Message { final x = Message('0300'); final now = dateTime ?? DateTime.now().toLocal(); - x.set(3, [0x00, 0x00, 0x04]); + x.processCode = 0x000004; x.dateTime = now; x.set(25, [0x14]); - x.set(41, terminalId.codeUnits); + x.terminalId = terminalId; return x; } @@ -86,11 +86,11 @@ class Message { final x = Message('0300'); final now = dateTime ?? DateTime.now().toLocal(); - x.set(3, [0x00, 0x00, 0x05]); + x.processCode = 0x000005; x.dateTime = now; x.set(25, [0x14]); - x.set(41, terminalId.codeUnits); + x.terminalId = terminalId; return x; } @@ -100,12 +100,11 @@ class Message { final x = Message('0300'); final now = dateTime ?? DateTime.now().toLocal(); - x.set(3, [0x00, 0x00, 0x07]); - + x.processCode = 0x000007; x.dateTime = now; x.set(39, [0x17]); - x.set(41, terminalId.codeUnits); + x.terminalId = terminalId; return x; } @@ -115,7 +114,7 @@ class Message { final x = Message('0300'); final now = dateTime ?? DateTime.now().toLocal(); - x.set(3, [0x00, 0x00, 0x01]); + x.processCode = 0x000001; x.dateTime = now; x.set(25, [0x14]); @@ -194,16 +193,19 @@ class Message { } String? _f02Pan; - String? _f03ProcessCode; + int? _f03ProcessCode; int? _f11Stan; DateTime? _f1213DateTime; + String? _f22CardEntryMode; String? _f24Nii; + String? _f25POSConditionCode; String? _f35Track2; String? _f41TerminalId; String? _f42MerchantId; DataElement? _f48DataElement; int? _f49Currency; + List? _f52PinBlock; String? _mac; /// PAN, the Card Number. @@ -232,13 +234,13 @@ class Message { /// Field 3. /// /// Must be 6 characters. - String? get processCode => _f03ProcessCode; - set processCode(String? value) { + int? get processCode => _f03ProcessCode; + set processCode(int? value) { final v = value; assert( - v == null || v.length == 6, - 'Process Code should be null or 6 characters long.', + v == null || v < 0xffffff, + 'Process Code should be null or <= 0xFFFFFF.', ); if (v == null) { @@ -292,6 +294,21 @@ class Message { } } + /// Card Entry Mode. + /// Field 22. + String? get cardEntryMode => _f22CardEntryMode; + set cardEntryMode(String? value) { + final v = value; + + if (v == null) { + _bmp[22] = false; + _f22CardEntryMode = null; + } else { + _bmp[22] = true; + _f22CardEntryMode = value; + } + } + /// NII. /// Field 24. /// @@ -314,6 +331,21 @@ class Message { } } + /// POS Condition Code. + /// Field 25. + String? get posConditionCode => _f25POSConditionCode; + set posConditionCode(String? value) { + final v = value; + + if (v == null) { + _bmp[25] = false; + _f25POSConditionCode = null; + } else { + _bmp[25] = true; + _f25POSConditionCode = value; + } + } + /// Track 2. /// Field 35. /// @@ -396,6 +428,26 @@ class Message { } } + /// Pin Block. + /// Field 52. + List? get pinBlock => _f52PinBlock; + set pinBlock(List? value) { + final v = value; + + assert( + v == null || v.length == 8, + 'pinBlock must be null or 8 bytes.', + ); + + if (v == null) { + _bmp[52] = false; + _f52PinBlock = null; + } else { + _bmp[52] = true; + _f52PinBlock = value; + } + } + /// MAC. /// Field 64 or 128. /// @@ -442,10 +494,14 @@ class Message { final p = processCode; if (p != null) { - final f2 = hex.decode(p); - bits.add(f2); + final bt = ByteData(4); + bt.setUint32(0, p, Endian.big); - strBits.add(p); + final f3 = bt.buffer.asUint8List().skip(1).toList(); + final h3 = hex.encode(f3); + + bits.add(f3); + strBits.add(h3); } continue; @@ -558,6 +614,17 @@ class Message { strBits.add(hh); } + continue; + } else if (i == 52) { + final p = pinBlock; + + if (p != null) { + final h = hex.encode(p); + + bits.add(p); + strBits.add(h); + } + continue; } else if (i == 64) { final p = mac; @@ -619,9 +686,9 @@ class Message { /// Calculates the MAC for current [Message]. Uint8List calcmac(Uint8List Function(List message) algorithm) { final c = clone(); - c.set(64, List.filled(8, 0)); + c.mac = '00000000000000'; final bmp = c._bitmap(); - c.unset(64); + c.mac = null; final List v = []; @@ -647,13 +714,16 @@ class Message { final mProcessCode = processCode; final mStan = stan; final mDateTime = dateTime; + final mCardEntryMode = cardEntryMode; final mNii = nii; + final mPosConditionCode = posConditionCode; final mTrack2 = track2; final mTerminalId = terminalId; final mMerchantId = merchantId; final mCurrency = currency; - final mMac = mac; final mDataElement = dataElement; + final mPinBlock = pinBlock; + final mMac = mac; if (mPan != null) { map['PAN'] = mPan; @@ -671,10 +741,18 @@ class Message { map['DateTime'] = mDateTime.toIso8601String(); } + if (mCardEntryMode != null) { + map['CardEntryMode'] = mCardEntryMode; + } + if (mNii != null) { map['NII'] = mNii; } + if (mPosConditionCode != null) { + map['PosConditionCode'] = mPosConditionCode; + } + if (mTrack2 != null) { map['Track2'] = mTrack2; } @@ -695,6 +773,10 @@ class Message { map['Currency'] = mCurrency; } + if (mPinBlock != null) { + map['PinBlock'] = '0x${hex.encode(mPinBlock).toUpperCase()}'; + } + for (var i = 1; i < 64; i++) { final f = _data[i]; diff --git a/pubspec.yaml b/pubspec.yaml index 6f027c3..0746a44 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: pos description: Dart Implementation of the ISO-8583 banking protocol for Point of Sale (POS) Devices. -version: 0.3.6 +version: 0.3.7 repository: https://github.com/xclud/dart_pos homepage: https://pwa.ir diff --git a/test/pos_test.dart b/test/pos_test.dart index 9c723b4..a372f7e 100644 --- a/test/pos_test.dart +++ b/test/pos_test.dart @@ -63,7 +63,7 @@ void main() { final now = DateTime(2024, 6, 10, 14, 24, 03); - message.processCode = '920000'; + message.processCode = 0x920000; message.stan = 123456; message.dateTime = now; message.nii = '0300'; @@ -90,7 +90,7 @@ void main() { final message = pos.Message('0100'); message.pan = '6274121195119854'; - message.processCode = '310000'; + message.processCode = 0x310000; message.track2 = '6274121195119854d281010052639594340480'; message.stan = 123456; message.dateTime = now; @@ -105,12 +105,11 @@ void main() { connectionType: 0x32, ); - message.set(22, [0x00, 0x21]); - message.set(25, [0x00]); - message.set(48, createField48ForLogOn(sid, aid)); + message.cardEntryMode = '0021'; + message.posConditionCode = '00'; // Pin Block - message.set(52, [0xB5, 0xB5, 0x2E, 0xB4, 0x10, 0x13, 0x9F, 0xD7]); + message.pinBlock = [0xB5, 0xB5, 0x2E, 0xB4, 0x10, 0x13, 0x9F, 0xD7]; message.mac = '0000000000000000'; final messageData = message.encode(algorithm: _calculateMac); @@ -122,30 +121,6 @@ void main() { }); } -List createField48ForLogOn(String serialNumber, String version, - [int language = 0x30]) { - final posSerial = [0x01, ...serialNumber.codeUnits]; - final langugeCode = [0x03, language]; - final appVersion = [0x02, ...version.codeUnits]; - const connectionType = [0x15, 0x32]; - - var field48 = _decimalAsHexBytes(posSerial.length, 2) + - posSerial + - _decimalAsHexBytes(appVersion.length, 2) + - appVersion + - _decimalAsHexBytes(langugeCode.length, 2) + - langugeCode + - _decimalAsHexBytes(connectionType.length, 2) + - connectionType; - - return field48; -} - -List _decimalAsHexBytes(int v, int l) { - final y = v.toString().padLeft(l, '0'); - return hex.decode(y); -} - Uint8List _calculateMac(List data) { if (data.length % 8 != 0) { final copyOfData = data.toList();