ECR via RS232 (Transit)
1. Communication Interaction Protocol Data Format
When communicating via serial ports, communication must be adhere to protocol data format defined below. Failure to do so will result in the device being unable to recognize the data
| Start symbol (1Byte) | CMD (2Bytes) | Data Size (2Bytes) | Type (1Byte) | Header CRC (4Bytes) | Data (n Bytes) | Data CRC (4Bytes) |
|---|---|---|---|---|---|---|
| 0x55 | 0xXXXX | 0xXXXX | 0x02 | 0xXXXXXXXX | 0xXXXXXXXX |
- Start symbol:
Fixed value: 0x55. It occupies one byte. The end bit of an entire instruction is determined by the start byte, along with the data structure and length. - CMD:
Occupies two bytes. In ECR interface communication, CMD: 0101 - Data Size:
Occupies two bytes. It represents the length of the data field. - Type:
Fixed value: 0x02. It occupies one byte and specifies the type of the current protocol. - Header CRC:
Occupies four bytes. The Header CRC is calculated based on the start symbol, CMD, Data Size, and Type. - Data:
The data field, which occupies n bytes. The content of the data field is defined in the protocol document. - Data CRC:
Occupies four bytes. The Data CRC is calculated from the data field.
2 Data and Flow
2.1 Generate Serial Port Data
The CMD command for functional interface communication is consistently set to 0x0006 and remains static.
// 1. Obtain the JSON data object
const deviceInfoRequestBodyJson = {
"version": "2.0",
"action": "DeviceInfo",
"data": "TviXv23Ee/P/LsVJ3LuhUNlqYtFcQLF3wKeMiPn2p50Z9aaquqJ/LCo5hHEMhvLsOGjzB/nsOvqe2Ebm78X4chufwVH5uhH1pvokt3mkFqZUEnaUKrYvfUYF8kc72VTAdXjG9np2MkRU2yESBVfg2pJQ4RhmgtT8+W72bnPyN/Z6gpV63Ynk4GRqjJ2mgrLP"
};
// Convert JSON data to String
const deviceInfoRequestBodyString = '{"version":"2.0","action":"DeviceInfo","data":"TviXv23Ee/P/LsVJ3LuhUNlqYtFcQLF3wKeMiPn2p50Z9aaquqJ/LCo5hHEMhvLsOGjzB/nsOvqe2Ebm78X4chufwVH5uhH1pvokt3mkFqZUEnaUKrYvfUYF8kc72VTAdXjG9np2MkRU2yESBVfg2pJQ4RhmgtT8+W72bnPyN/Z6gpV63Ynk4GRqjJ2mgrLP"}';
// 2. Convert String data to Hex
const deviceInfoRequestBodyHex = "7B 22 76 65 72 73 69 6F 6E 22 3A 22 32 2E 30 22 2C 22 61 63 74 69 6F 6E 22 3A 22 44 65 76 69 63 65 49 6E 66 6F 22 2C 22 64 61 74 61 22 3A 22 54 76 69 58 76 32 33 45 65 2F 50 2F 4C 73 56 4A 33 4C 75 68 55 4E 6C 71 59 74 46 63 51 4C 46 33 77 4B 65 4D 69 50 6E 32 70 35 30 5A 39 61 61 71 75 71 4A 2F 4C 43 6F 35 68 48 45 4D 68 76 4C 73 4F 47 6A 7A 42 2F 6E 73 4F 76 71 65 32 45 62 6D 37 38 58 34 63 68 75 66 77 56 48 35 75 68 48 31 70 76 6F 6B 74 33 6D 6B 46 71 5A 55 45 6E 61 55 4B 72 59 76 66 55 59 46 38 6B 63 37 32 56 54 41 64 58 6A 47 39 6E 70 32 4D 6B 52 55 32 79 45 53 42 56 66 67 32 70 4A 51 34 52 68 6D 67 74 54 38 2B 57 37 32 62 6E 50 79 4E 2F 5A 36 67 70 56 36 33 59 6E 6B 34 47 52 71 6A 4A 32 6D 67 72 4C 50 22 7D";
// 3. Header Section
const headerHex = "55 01 01 00 F1 02"
// 4. CRC of the Header
const headerCRC = "8B F4 C7 E2";
// 5. CRC of the data
const dataCRC = "B7 80 AC B6";
// 6. The entire data finally transmitted through the serial port
const data = "55 01 01 00 F1 02 8B F4 C7 E2 7B 22 76 65 72 73 69 6F 6E 22 3A 22 32 2E 30 22 2C 22 61 63 74 69 6F 6E 22 3A 22 44 65 76 69 63 65 49 6E 66 6F 22 2C 22 64 61 74 61 22 3A 22 54 76 69 58 76 32 33 45 65 2F 50 2F 4C 73 56 4A 33 4C 75 68 55 4E 6C 71 59 74 46 63 51 4C 46 33 77 4B 65 4D 69 50 6E 32 70 35 30 5A 39 61 61 71 75 71 4A 2F 4C 43 6F 35 68 48 45 4D 68 76 4C 73 4F 47 6A 7A 42 2F 6E 73 4F 76 71 65 32 45 62 6D 37 38 58 34 63 68 75 66 77 56 48 35 75 68 48 31 70 76 6F 6B 74 33 6D 6B 46 71 5A 55 45 6E 61 55 4B 72 59 76 66 55 59 46 38 6B 63 37 32 56 54 41 64 58 6A 47 39 6E 70 32 4D 6B 52 55 32 79 45 53 42 56 66 67 32 70 4A 51 34 52 68 6D 67 74 54 38 2B 57 37 32 62 6E 50 79 4E 2F 5A 36 67 70 56 36 33 59 6E 6B 34 47 52 71 6A 4A 32 6D 67 72 4C 50 22 7D B7 80 AC B6";
2.2 Scan and Sale flow
- When the third-party terminal is ready, send the Scan interface to the wonder terminal.
- Wait for passengers to swipe their cards;
- After a passenger swipes their card, the wonder terminal will respond with a Scan interface to a third-party terminal.
- The third-party terminal confirms the information and initiates a Sale again for order collection.
a. Scan and sale for transit
-
第三方终端发送Scan指令时,开启读卡功能
-
Wonder Terminal根据参数等待一定时长,同步响应第三方终端
2.1 等等时间最小200ms, 最大3000ms, 默认500ms
2.2 5分钟没有收到Scan指令,则关闭读卡功能
2.3 读取的卡信息,1000ms内有效,超时无效,不能返回给第三方终端
-
Wonder Terminal同步读卡信息到Wonder Gateway,三个小时后没有同步,需要预警
-
Wonder Gateway通过Webhook,把数据同步给第三方
-
When a third-party terminal sends a Scan command, the card reading function is enabled.
-
Wonder Terminal waits for a specified duration based on parameters and responds synchronously to the third-party terminal.
2.1 Wait time: minimum 200ms, maximum 3000ms, default 500ms.
2.2 If no Scan command is received within 5 minutes, the card reading function is turned off.
2.3 The card information read is valid within 1000ms; if it times out, it becomes invalid and cannot be returned to the third-party terminal.
-
Wonder Terminal synchronizes the card information to Wonder Gateway. If synchronization is not completed within three hours, an alert is required.
-
Wonder Gateway synchronizes the data to the third party via webhook.
Success Flow

Error Flow

b. Sale directly for transit
Success Flow

Error Flow

3. Interface Protocol
3.1 Pair
- CMD: 0101
- Please refer to ECR Interface Protocol - 3.1 Pair
3.2 Ack
- CMD: 0101
- Please refer to ECR Interface Protocol - 3.2 Ack
3.3 Device Info
- CMD: 0101
- Please refer to ECR Interface Protocol - 3.3 Device Info
3.4 Scan
- Action: Scan
- Pin Code: pinCode2
- CMD: 0101
Request "data.body" structure
| Variable | Type | Required | Description |
|---|---|---|---|
| referenceID | String | Y | Reference ID: Every time a transaction request is initiated, it is crucial to ensure the uniqueness of this ID. Failure to do so will render any subsequent operations on the transaction invalid. |
| paymentMethod | enum | N | Payment methods: -credit_card, -fps, -octopus. -consumer_presented_qr_code, -all, If the payment method is transmitted, it will directly redirect to the target page |
| restrictedPaymentMethods | Array | N | Payment Methods |
| currency | String | Y | Example: HKD / USD / RMB |
| amount | String | Y | Sale amount |
| remark | String | N | Remark |
| waitTime | Number | N | Time value for the Reader waiting for an EMV contactless card in milliseconds, Minimum value: 200 ms, Maximum value: 3000 ms, Default 500ms |
Example:
/*
Pin Code: pinCode2 (OvSdpyD2FoUNR5rNyte41QqZzR1Y4DVN)
Encrypt the original data:
{
"header": {
"requestID": "5debf769-49d7-4c9b-b6f4-8a9d90e1a874",
"clientDeviceSN": "126498561093",
"timestamp": "2025-11-12T10:11:04+00:00"
},
"body": {
"referenceID": "f8b13b22-16ca-4a87-95a4-df4bebf09ee1",
"currency": "HKD",
"amount": "10.20",
"timeout": 0
}
}
*/
{
"version": "2.0",
"action": "Scan",
"data": "0x6EURH9/Mbg1BT9cYG6pIT67E/kjqar8HN1IxALX8O2W+fNwhYk7U3odmad1vj3hT88eG5AhiQ1hs1f09EpkF27qnut0eJppedkHa0EMjAWJTsmnh0qbTBrK76fh50kQrTHS4rPOYIK9xz/r30SEEGIw09jYNVBONeEPuLYejCPst1NjhDgjPX84u1c8HejYaJbIdRkC2lFIQ4FZSR2ThYyp3+jGEG6gsyLySaGFboaoFuHl3zcTLGAK8+fAv3E9WBvEcB6L4lq/H9hIJlh3SRDR7yp68/x8ySiXFO+Q5EslYr4UxWR2ARTx3C9zER0Iwe0GPIr46TC4QXuRUF6LVS8GwOv6//gW4gtCnn4wK+d60SduOV/82y7mngRjtpR"
}
Response "data.body" structure
| Variable | Type | Required | Description |
|---|---|---|---|
| status | String | Y | Response status: Success / Failed |
| errorCode | String | N | |
| errorMessage | String | N | |
| currency | String | Y | Example: HKD / USD / RMB |
| amount | String | Y | Sale amount |
| consumerIdentifyHash | String | Y | |
| consumerCountryCode | String | N | Consumer country code |
| acquirerType | String | Y | Acquirer type |
| enPaymentData | String | Y | |
| balance | String | N | Balance amount |
| balanceCurrency | String | N | Balance currency |
| unuploadInfo | Object | N | If there are three hours of data uploaded to the server, this object needs to be returned as a warning |
| unuploadInfo.count | Number | N | The count of unuploaded transactions |
| unuploadInfo.datetime | String | N | The earliest time when the sale was not uploaded to the server, it is recommended to issue a warning after 4 hours |
3.5 CancelScan
- Action: CancelScan
- Pin Code: pinCode2
Request "data.body" structure
This request has no body
Example:
/*
Pin Code: pinCode2 (OvSdpyD2FoUNR5rNyte41QqZzR1Y4DVN)
Encrypt the original data:
{
"header": {
"requestID": "c5bf3cbe-a146-4f8c-bb8e-209c1e7b8437",
"clientDeviceSN": "126498561093",
"timestamp": "2025-11-12T10:11:04+00:00"
}
}
*/
{
"version": 2.0,
"action": "CancelScan",
"data": "AG1mb0IbA6EzpyvE6g6ySqemX3XiXLVm8wlGbPkOfsRNuxSCy1fz3/VlDqRd3PxDvy8FHCv6pvB0nhxysM+V85NNbQTC15+GgrnLeupS9IMSYYPYAmhyXtqae1DbXcqCj9F614QJIB0bWzRpqWmOgap/uzTIwFHf/qGMKCCCADouMiDxnirp1EunsRBeECyBLdlBoqPaWoNhhMISyZu5+9eul5E+eZMmKwNUDL4KJw0="
}
Response "data.body" structure
| Variable | Type | Required | Description |
|---|---|---|---|
| status | String | Y | Response status: Success |
| errorCode | String | Y | |
| errorMessage | String | Y |
Example:
/*
Pin Code: pinCode2 (OvSdpyD2FoUNR5rNyte41QqZzR1Y4DVN)
Encrypt the original data:
{
"header": {
"responseID": "c5bf3cbe-a146-4f8c-bb8e-209c1e7b8437",
"serverDeviceSN": "NEXGO-N96-1170270945",
"timestamp": "2025-11-12T10:12:04+00:00"
},
"body": {
"status": "Success",
"errorCode": "",
"errorMessage": ""
}
}
*/
{
"version": 2.0,
"action": "CancelScan",
"data": "MPf0p+jMU+smn2s/S1so9c4x3YLmJeJ3TzXbtiniGAVFhjYJrCCOdC0wc32c738qd1D5F8WlaWXqz+uNtQzgkLgmx3wz/rDGBNRh11tNzY4AfZEra3JfWEWEsNDFpDco9FMjF4mO/AhD/KFYe5+1suMCtJdLYE5ka9XnJr3xoYd8YrHdkY3Bpq8/tveW8FHIwMOj67hyOvtjdtnemv/uD52QTcSZMVCF6vQFbK9VJb74tE2kbGWlWmYxtWwPRwrv5gTh6YSdMESoFxITjE29SkVXv+oNa85B/NqOMSFEc6AsgIKV0xCpebIPOM6Owz6G"
}
3.6 Sale
- Action: Sale
- Pin Code: pinCode2
- CMD: 0101
Request "data.body" structure
| Variable | Type | Required | Description |
|---|---|---|---|
| referenceID | String | Y | Reference ID, It should be consistent with the referenceID in the Scan interface |
| currency | String | Y | Example: HKD / USD / RMB |
| amount | String | Y | Sale amount |
| remark | String | N | Remark |
| timeout | Number | N | The timeout in seconds. The default is 0, indicating that it will never time out |
Example:
/*
Pin Code: pinCode2 (OvSdpyD2FoUNR5rNyte41QqZzR1Y4DVN)
Encrypt the original data:
{
"header": {
"requestID": "5debf769-49d7-4c9b-b6f4-8a9d90e1a874",
"clientDeviceSN": "126498561093",
"timestamp": "2025-11-12T10:11:04+00:00"
},
"body": {
"referenceID": "f8b13b22-16ca-4a87-95a4-df4bebf09ee1",
"currency": "HKD",
"amount": "10.20",
"timeout": 0
}
}
*/
{
"version": "2.0",
"action": "Sale",
"data": "N8wRhMEy+l2h0oj6XV6liUP31/2Yi10NNqtOicoV2sr10hIjHQK/sAfbkA3uiF3OEvzTUhOuu67BiAnl5SHJ5T9mujrHTyi25jGxo6sjkz0lDEvP99WQAY9QCueXdg6YaWSdnzHfosyN5vkY8lTMCwJ5NnRVv46qflxpGe9fC9n9hUAiR/NbkXWDeikO8XdA2zxbOtYLGG9SaNhDMNhvqxYzoiKniGlG85kPyb3n0AzjIppefng2OsPebzluSDi43JbZl/TF++x4pgvHzEmnGydCd+qNWaKggcP3ymOAJut50qnC2E1u2mmke4IvmH3e513iF++C0fmViz3SX8K2mIDaXyiM7COEy35nHX5ODAgB5GbGPamJjKT2zW2Z2yOl"
}
Response "data.body" structure
| Variable | Type | Required | Description |
|---|---|---|---|
| status | String | Y | Response status: Success / Failed |
| errorCode | String | N | |
| errorMessage | String | N | |
| currency | String | Y | Example: HKD / USD / RMB |
| amount | String | Y | Sale amount |
| balance | String | N | Balance amount |
| balanceCurrency | String | N | Balance currency |
3.7 Sale Directly
- Action: SaleDirectly
- Pin Code: pinCode2
- CMD: 0101
Request "data.body" structure
| Variable | Type | Required | Description |
|---|---|---|---|
| referenceID | String | Y | Reference ID: Every time a transaction request is initiated, it is crucial to ensure the uniqueness of this ID. Failure to do so will render any subsequent operations on the transaction invalid. |
| qrCode | String | Y | The QR code string |
| currency | String | Y | Example: HKD / USD / RMB |
| amount | String | Y | Sale amount |
| remark | String | N | Remark |
| timeout | Number | N | The timeout in seconds. The default is 0, indicating that it will never time out |
Example:
/*
Pin Code: pinCode2 (OvSdpyD2FoUNR5rNyte41QqZzR1Y4DVN)
Encrypt the original data:
{
"header": {
"requestID": "5debf769-49d7-4c9b-b6f4-8a9d90e1a874",
"clientDeviceSN": "126498561093",
"timestamp": "2025-11-12T10:11:04+00:00"
},
"body": {
"referenceID": "f8b13b22-16ca-4a87-95a4-df4bebf09ee1",
"qrCode": "22349671249672146346",
"currency": "HKD",
"amount": "10.20",
"timeout": 120
}
}
*/
{
"version": "2.0",
"action": "SaleDirectly",
"data": "t2Pds7/sabaIl4xFgjH1vZijylhvw8INcawmyuyxkMZzwId/fnmD18T7AAYzV+zA6aJa2H3Rauwa6APh4+/8CjjgcCoGwNp4xzVmuQXwQbxZahMy1n5VRfU5mOhP8z9N5KXZA/SCnkF+Q/mRy6/VARD4GQhWt6PB0dX6/fZzUkQAjK4ygeXnsLw/gPE26dBmmyJ6T0vAyuQpsl3jYGCeeTRfj36hCtWY9i0SufzaNvm4HooOpxOtFrYo3+dLntxBz0ww4QdD3wjvNrOLn+2FmEcMwkYwjT9IpOYYNLBx2MWMGY1iipkw0hHh1Qo4NTZjjN+87fRaOOoyrMuz0O9pu9sRtPuJ9FN9asIaUoKQkOhbl01liPPQiwmDWFWuv1XoD2ngnCuWNE783b4wlsy4Vi1K9kPyzrXIXPOt6kkd/Bs="
}
Response "data.body" structure
| Variable | Type | Required | Description |
|---|---|---|---|
| status | String | Y | Response status: Success / Failed |
| errorCode | String | N | |
| errorMessage | String | N | |
| currency | String | Y | Example: HKD / USD / RMB |
| amount | String | Y | Sale amount |
| consumerIdentifyHash | String | Y | |
| acquirerType | String | Y | Acquirer type |