鼠笼管理系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

323 lines
7.7 KiB

<?php
// namespace ISO8583;
// use ISO8583\Error\UnpackError;
// use ISO8583\Error\PackError;
require_once(dirname(__FILE__) . '/Error/UnpackError.php');
require_once(dirname(__FILE__) . '/Error/PackError.php');
require_once(dirname(__FILE__) . '/Mapper/AbstractMapper.php');
require_once(dirname(__FILE__) . '/Mapper/AlphaNumeric.php');
require_once(dirname(__FILE__) . '/Mapper/Binary.php');
/**
* 8583报文
*/
class Message
{
protected $protocol; //8583协议定义
protected $options; //配置信息 lengthPrefix appCategory
protected $length; //消息长度
protected $tpdu; //TPDU ID(60H)+目的地址(N2)+源地址(N2)
protected $header; //报文头 {应用类别(N2)}+软件版本号(N2)+终端状态(N1)+处理要求(N1)+保留使用(N6)
protected $encrypted; //加密信息 报文长度(A3)+算法标识(A1)+{商户号(A15)}+{终端号(A8)}+交易标识(A10)+响应码(A2)+保留(A2)
//应用数据 交易数据(ISO8583Msg)
protected $mti; //消息类型(Message Type Identifier) —— 0200金融类请求消息
protected $bitmap; //位图
protected $fields = []; //数据域
protected $mappers = [ //数据类型
'a' => AlphaNumeric::class,
'n' => AlphaNumeric::class,
's' => AlphaNumeric::class,
'an' => AlphaNumeric::class,
'as' => AlphaNumeric::class,
'ns' => AlphaNumeric::class,
'ans' => AlphaNumeric::class,
'b' => Binary::class,
'z' => AlphaNumeric::class
];
public function __construct(Protocol $protocol , $options = [])
{
$defaults = [
'lengthPrefix' => null
];
$this->options = $options + $defaults;
$this->protocol = $protocol;
}
protected function shrink(&$message, $length)
{
$message = substr($message, $length);
}
public function pack()
{
// 设置 TPDU
// $tpdu = $this->tpdu;
$tpdu = bin2hex($this->tpdu);
// 设置 报文头
// $header = $this->header;
$header = bin2hex($this->header);
// 设置 加密信息
// $encrypted = $this->encrypted;
$encrypted = bin2hex($this->encrypted);
// 设置 MTI
// $mti = $this->mti;
$mti = bin2hex($this->mti);
// Dropping bad fields
foreach($this->fields as $key=>$val) {
if (in_array($key, [1, 65])) {
unset($this->fields[$key]);
}
}
// 填充 位图
$bitmap = "";
$bitmapLength = 64 * (floor(max(array_keys($this->fields)) / 64) + 1);
$tmpBitmap = "";
for($i=1; $i <= $bitmapLength; $i++) {
if (
$i == 1 && $bitmapLength > 64 ||
$i == 65 && $bitmapLength > 128 ||
isset($this->fields[$i])
) {
$tmpBitmap .= '1';
} else {
$tmpBitmap .= '0';
}
if ($i % 64 == 0) {
for($i=0; $i<64; $i+=4){
$bitmap .= sprintf('%01x', base_convert(substr($tmpBitmap, $i, 4), 2, 10));
}
}
}
$this->bitmap = $bitmap;
// 域排序
ksort($this->fields);
// 打包8583报文
$message = "";
foreach($this->fields as $id => $data) {
$fieldData = $this->protocol->getFieldData($id);
$fieldMapper = $fieldData['type'];
if (!isset($this->mappers[$fieldMapper])) {
throw new \Exception('Unknown field mapper for "' . $fieldMapper . '" type');
}
$mapper = new $this->mappers[$fieldMapper]($fieldData['length']);
if (
($mapper->getLength() > strlen($data) && $mapper->getVariableLength() === 0 ) ||
$mapper->getLength() < strlen($data)
) {
$error = 'FIELD [' . $id . '] should have length: ' . $mapper->getLength() . ' and your message "' . $data . "' is " . strlen($data);
throw new PackError($error);
}
$message .= $mapper->pack($data);
}
//加密信息中包含 报文长度
if (strlen($encrypted) != 0) {
$encrypted = bin2hex(sprintf('%03d', strlen($message) / 2)) . $encrypted;
}
// 打包所有字段 依次为 TPDU、报文头、加密信息、报文体(MTI、位图、域值)
$message = $mti . $bitmap . $message;
//首位长度字段
if ($this->options['lengthPrefix'] > 0) {
$message = bin2hex(sprintf('%0' . $this->options['lengthPrefix'] . 'd', strlen($message) / 2)) . $message;
}
$message = $tpdu . $header . $encrypted . $message;
//返回打包后的信息
return $message;
}
public function unpack($message)
{
// Getting message length if we have one
if ($this->options['lengthPrefix'] > 0) {
$length = (int)hex2bin(substr($message, 0, (int)$this->options['lengthPrefix'] * 2));
$this->shrink($message, (int)$this->options['lengthPrefix'] * 2);
if (strlen($message) != $length * 2) {
throw new UnpackError('Message length is ' . strlen($message) / 2 . ' and should be ' . $length);
}
}
// Parsing TPDU
$this->setTPDU(hex2bin(substr($message, 0, 20)));
$this->shrink($message, 20);
// Parsing Header
$this->setHeader(hex2bin(substr($message, 0, 24)));
$this->shrink($message, 24);
// Parsing length
$this->length = hex2bin(substr($message, 0, 6));
// Parsing Encrypted
$this->setEncrypted(hex2bin(substr($message, 6, 76)));
$this->shrink($message, 82);
// Parsing MTI
$this->setMTI(hex2bin(substr($message, 0, 8)));
$this->shrink($message, 8);
// Parsing bitmap
$bitmap = "";
for(;;) {
$tmp = implode(null,
array_map(
function($bit) {
return str_pad(base_convert($bit, 16, 2), 8, 0, STR_PAD_LEFT);
},
str_split(substr($message, 0, 16), 2)
)
);
$this->shrink($message, 16);
$bitmap .= $tmp;
if (substr($tmp, 0, 1) !== "1" || strlen($bitmap) > 128) {
break;
}
}
$this->bitmap = $bitmap;
if (strlen($message) != $this->length * 2) {
throw new UnpackError('Message length is ' . strlen($message) / 2 . ' and should be ' . $this->length);
}
// Parsing fields
for($i=0; $i < strlen($bitmap); $i++) {
if ($bitmap[$i] === "1") {
$fieldNumber = $i + 1;
if ($fieldNumber === 1 || $fieldNumber === 65) {
continue;
}
$fieldData = $this->protocol->getFieldData($fieldNumber);
$fieldMapper = $fieldData['type'];
if (!isset($this->mappers[$fieldMapper])) {
throw new \Exception('找不到指定的类型定义:' . $fieldMapper);
}
$mapper = new $this->mappers[$fieldMapper]($fieldData['length']);
$unpacked = $mapper->unpack($message);
$this->setField($fieldNumber, $unpacked);
}
}
}
public function getTPDU()
{
return $this->tpdu;
}
public function setTPDU($tpdu)
{
// if (!preg_match('/^60[0-9]{8}$/', $tpdu)) {
// throw new UnpackError('TPDU字段应该是以60开头的10位数字:' . $tpdu);
// }
$this->tpdu = $tpdu;
}
public function getHeader()
{
return $this->header;
}
public function setHeader($header)
{
// if (!preg_match('/^[0-9]{12}$/', $header)) {
// throw new UnpackError('Header字段应该是12位数字:' . $header);
// }
$this->header = $header;
}
//直接获取加密信息中<strong></strong>包含报文长度
public function getEncrypted()
{
return $this->encrypted;
}
public function setEncrypted($encrypted)
{
// if (strlen($encrypted) != 38) {
// throw new UnpackError('Encrypted字段应该是38位字符:' . $encrypted);
// }
$this->encrypted = $encrypted;
}
public function getMTI()
{
return $this->mti;
}
public function setMTI($mti)
{
// if (!preg_match('/^[0-9]{4}$/', $mti)) {
// throw new UnpackError('MTI字段应该是4位数字!');
// }
$this->mti = $mti;
}
public function set(array $fields)
{
$this->fields = $fields;
}
public function getFieldsIds()
{
$keys = array_keys($this->fields);
sort($keys);
return $keys;
}
public function getFields()
{
ksort($this->fields);
return $this->fields;
}
public function setField($field, $value)
{
$this->fields[(int)$field] = $value;
}
public function getField($field)
{
return isset($this->fields[$field]) ? $this->fields[$field] : null;
}
public function getBitmap()
{
return $this->bitmap;
}
}