Skip to content

Commit

Permalink
Pin Block, Card Entry Mode and POS Condition Code fields added.
Browse files Browse the repository at this point in the history
  • Loading branch information
xclud committed Jun 18, 2024
1 parent 3b0d9ca commit a069754
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 54 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
128 changes: 105 additions & 23 deletions lib/src/message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;

Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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]);
Expand Down Expand Up @@ -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<int>? _f52PinBlock;
String? _mac;

/// PAN, the Card Number.
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
///
Expand All @@ -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.
///
Expand Down Expand Up @@ -396,6 +428,26 @@ class Message {
}
}

/// Pin Block.
/// Field 52.
List<int>? get pinBlock => _f52PinBlock;
set pinBlock(List<int>? 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.
///
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -619,9 +686,9 @@ class Message {
/// Calculates the MAC for current [Message].
Uint8List calcmac(Uint8List Function(List<int> message) algorithm) {
final c = clone();
c.set(64, List<int>.filled(8, 0));
c.mac = '00000000000000';
final bmp = c._bitmap();
c.unset(64);
c.mac = null;

final List<Uint8List> v = [];

Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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];

Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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

Expand Down
35 changes: 5 additions & 30 deletions test/pos_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -122,30 +121,6 @@ void main() {
});
}

List<int> 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<int> _decimalAsHexBytes(int v, int l) {
final y = v.toString().padLeft(l, '0');
return hex.decode(y);
}

Uint8List _calculateMac(List<int> data) {
if (data.length % 8 != 0) {
final copyOfData = data.toList();
Expand Down

0 comments on commit a069754

Please sign in to comment.