Skip to main content

ECR Payment Json

Send ECR Request to mPOS

Process:

a.Ensure that the terminal interface is the POS integration interface of QR code (open ECR through API, re-enter the app or refresh the status to enter this page)

1704943370817

b.Pair the terminal with "Pair" (the value of MessageCategory is "Pair"). After it is successfully paired, the page for Waiting Ecr Instructions is displayed

Refer to the encryption demo at the bottom

Communication Methods Supported

OnlineAsync:

  • POS and mPOS can not be on the same LAN and are not limited by distance

  • No need to wait after the communication is initiated, and the query results can be polled later (or callback via url).

`https:

OfflineAsync:

  • POS and mPOS are on the same LAN

  • Communication is faster and more stable

  • No need to wait after the communication is initiated, and the query results can be polled later (or callback via url).

172.16.91.29:8443/wonder-terminal/async

OfflineSync: Synchronous Communication under the same LAN

  • POS and mPOS are on the same LAN

  • Communication is faster and more stable

  • Easy to use

172.16.91.29:8443/wonder-terminal/sync


API Illustration

Currently Supported Functions

  • Pair
  • Payment
  • PreAuth
  • TransactionStatus
  • Reversal
  • Void
  • Abort
  • Push
  • SwitchBusiness

Request Format

 {
"SaleToPOIRequest": {
"MessageHeader": {
"ProtocolVersion": "1.0",
"MessageClass": "Service",
"MessageCategory": "Pair",
"MessageType": "Request",
"ServiceID": "4e412543v1v14e3",
"POIID": "PAX-A930-1170270945",
"BusinessID": "ff467f02-5b69-45f3-81aa-bffcca55fe8f"
},

"SecurityTrailer": {
"KeyVersion": "1",
"KeyIdentifier": "",
"Hmac": "",
"Nonce": "",
"WonderCryptoVersion": 0
},

"WonderBlob":"",




"PushRequest": {
"Action": "ECR_CANCEL"
},




"SwitchBusinessRequest":{
"BusinessID":"a5467f02-2914-5d1a-21bb-bffcca55fe94"
},
"TransactionStatusRequest": {
"MessageReference": {
"ServiceID": "59b90600-13cf-11ee-bab2-99e6a6ef5392",
"MessageCategory": "Payment"
}
},
"ReversalRequest": {
"SaleData": {
"SaleToAcquirerData": "currency=HKD"
},
"OriginalPOITransaction": {
"POITransactionID": {
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5049",
"OriginBRN": "3267887011708553217",
"ReferenceNumber":"8daf4dc0-6ad6-44b1-8256-a0f1549c0fa6"
}
},
"ReversedAmount": 2.0
},
"VoidRequest": {
"OriginalPOITransaction": {
"POITransactionID": {
"TransactionID": "guid",
"OriginBRN": "3267887011708553217",
"ReferenceNumber":"8daf4dc0-6ad6-44b1-8256-a0f1549c0fa6"
}
}
},
"PaymentRequest": {
"SaleData": {
"SaleTransactionID": {
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5392",
"TimeStamp": "2019-03-07T10:11:04+00:00",
"CallBackUrl":"https:",
"ReferenceNumber":"8daf4dc0-6ad6-44b1-8256-a0f1549c0fa6",
"AppKey":"ca82cb30-44af-4c1d-a211-98c0403d1f9f",
"AppSlug":"1wU7Au"
}
},
"PaymentTransaction": {
"AmountsReq": {
"Currency": "HKD",
"RequestedAmount": "11.2"
}
}
},
"AbortRequest": {
"AbortReason": "MerchantAbort",
"MessageReference": {
"MessageCategory": "Payment",

"ServiceID": "59b90600-13cf-11ee-bab2-99e6a6ef5392"
}
}
}
}

MessageHeader:

keyvalueRemark
ProtocolVersion1.0Agreement Version(Fixed to be “1.0")
MessageClassServiceAgreement(Fixed to be “Service")
MessageCategoryPairPair
Payment
TransactionStatus
Reversal
Void
Abort
Push
-Six functions supported
MessageTypeRequestFixed to be  "Request"
ServiceIDSuggest:uuidIt is used to distinguish each request and prevent repeated processing
PostMan "guid"
POIIDDevice SNEg:PAX-A930-1191851002
BusinessIDBusiness IDeg:ff467f02-5b69-45f3-81aa-bffcca55fe21
You can obtain it on the binding QR code page

WonderBlob:

WonderBlob is an encrypted string, and the source data corresponds to the data in **Request. Developers need to decrypt for their own use

PushRequest

PushRequest is a special case and it doesn't need to be encryted. If it is needed for use, need to reserve it into the request structure.


Response Result Returning Format

{
"MessageHeader": {
"BusinessID": "ff467f02-5b69-45f3-81aa-bffcca55fe8f",
"MessageCategory": "TransactionStatus",
"MessageClass": "Service",
"MessageType": "Response",
"POIID": "PAX-A930-1170270945",
"ProtocolVersion": "1.0",
"ServiceID": "31fd1gg4fg6211"
},
"SecurityTrailer": {
"KeyVersion": "",
"KeyIdentifier": "",
"Hmac": "",
"Nonce": "",
"WonderCryptoVersion": 0
},
"WonderBlob": null,




"AbortResponse": null,
"PairResponse": null,
"PaymentResponse": null,
"PushResponse": null,
"ReversalResponse": null,
"SecurityTrailer": null,
"SwitchBusinessResponse":null,
"TransactionStatusResponse": {
"RepeatedMessageResponse": {
"MessageHeader": {
"BusinessID": "ff467f02-5b69-45f3-81aa-bffcca55fe8f",
"MessageCategory": "TransactionStatus",
"MessageClass": "Service",
"MessageType": "Response",
"POIID": "PAX-A930-1170270945",
"ProtocolVersion": "1.0",
"ServiceID": "31fd1gg4fg6211"
},
"RepeatedResponseMessageBody": {
"PaymentResponse": null,
"ReversalResponse": {
"POIData": null,
"PaymentReceipt": null,
"Response": {
"AdditionalResponse": "UUID Conflict",
"ErrorCondition": null,
"Result": "Failure"
},
"ReversedAmount": null
}
}
},
"Response": {
"AdditionalResponse": null,
"ErrorCondition": null,
"Result": "Success"
}
}
}
  • WonderBlob is an encrypted string, and the source data corresponds to the data in **Response. You can use the type of MessageCategory to find the corresponding ****Response to decrypt it.
  • PushResponse will also keep the structure but will not be encrypted
  • "MessageType": Response corresponds to Request
  • Every Response corresponds to Request
  • Response returns in two formats
    • Sync. When these two requests come, the service does not return immediately. Instead, it wait for the operation to finish and then return the result. TransactionStatus(querying payment status) is not supported in this mode.
    • Async .Under this model, all request return immediatly (Payment,Reversal)but it will not return the actual payment result, instead, it will receive a structure body "Request Received". The real payment result requires the client to query the result through TransactionStatus in the mode of rotation training.

Payment

{
"PaymentRequest": {
"SaleData": {
"SaleTransactionID": {
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5392",
"TimeStamp": "2019-03-07T10:11:04+00:00",
"CallBackUrl": "https:",
"ReferenceNumber": "8daf4dc0-6ad6-44b1-8256-a0f1549c0fa6",
"AppKey": "ca82cb30-44af-4c1d-a211-98c0403d1f9f",
"AppSlug": "1wU7Au",
"PaymentType": "Optional",
"CodeType": "qrcode",
"CodeData": "https:",
"PrinterMsg": "MCC ID: 84930838505\n"
}
},
"PaymentTransaction": {
"AmountsReq": {
"Currency": "HKD",
"RequestedAmount": "10.2"
}
}
}
}
{
"PaymentResponse": {
"POIData": null,
"PaymentReceipt": null,
"Response": {
"AdditionalResponse": null,
"ErrorCondition": null,
"Result": "Success"
}
}
}
{
"PaymentResponse": {
"POIData": {
"POITransactionID": {
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5392"
}
},
"Response": {
"Result": "Failure",
"ErrorCondition": "Aborted",
"AdditionalResponse": null
},
"PaymentReceipt": null
}
}
{
"PaymentResponse": {
"POIData": {
"POITransactionID": {
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5392"
}
},
"Response": {
"Result": "Success",
"ErrorCondition": null,
"AdditionalResponse": null
},
"PaymentReceipt": {
"amount": "10.20",
"tipsAmount": "0.00",
"currency": "HKD",
"payment_method": "fps",
"transaction_state": "success",
"date_time": "2023-06-30T09:08:52+00:00",
"merchant_id": "3333",
"terminal_id": null,
"rrn": "3263492852830699521",
"brn": "3263492852495159296",
"acquirer_type": "fps",
"receiptData": {
"reference_number": "W-158637380062",
"sale_transactions": [
{
"success": true,
"amount": "10.2",
"payment_method": 22,
"payment": "FPS",
"payment_data": {
"credit_card_type": "fps",
"new_gateway_txn_id": "3263492852495159296",
"rrn": "3263492852830699521",
"merchant_id": "3333",
"terminal_id": "",
"created_at": "2023-06-30T09:08:52+00:00",
"reference_id": "3263492852495159296",
"auth_code": "3263492852830699522"
}
}
],
"initial_total": "10.2",
"initial_tips": null,
"subtotal": "10.2"
}
}
}
}

About PaymentType:

  • can be null, if null, then the PaymentType will be "Optional"
  • "Card": use Card payment like: "Visa", "MasterCard", "JCB", "American Express", "Diners Club", "Discover", "Union Pay", "Apple Pay","Google Pay","Huawei Pay","Samsung Pay"
  • "Wallets": use Wallets payment like: "Alipay", "WechatPay", "Xpay","Other" et. need client show their QRCode by smartphone
  • "Octopus": use Octopus payment like: "Octopus"
  • "QRCodePresent": use QRCodePresent payment like: "FPS" et. need client scan the QRCode by smartphone
  • "Optional": No Specified Payment Type. The above modes need to be specified manually
  • before use the above modes, need to check the merchant's payment method support that

About “extra business data”

  • can be null
  • show a QRCode or BarCode at Receipt
  • barcode type is "code39"
  • like this img_1.png

Preauth

{
"PreauthRequest": {
"SaleData": {
"SaleTransactionID": {
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5392",
"TimeStamp": "2019-03-07T10:11:04+00:00",
"ReferenceNumber": "8daf4dc0-6ad6-44b1-8256-a0f1549c0fa6",
"CallBackUrl": "https:",
"AppKey": "ca82cb30-44af-4c1d-a211-98c0403d1f9f",
"AppSlug": "1wU7Au"
}
},
"PaymentTransaction": {
"AmountsReq": {
"Currency": "HKD",
"RequestedAmount": "10.2"
}
}
}
}
{
"PreauthResponse": {
"Response": {
"AdditionalResponse": null,
"ErrorCondition": null,
"Result": "Success"
}
}
}
{
"PreauthResponse": {
"POIData": {
"POITransactionID": {
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5392"
}
},
"Response": {
"Result": "Failure",
"ErrorCondition": "Aborted",
"AdditionalResponse": null
}
}
}

{
"PreauthResponse": {
"POIData": {
"POITransactionID": {
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5392"
}
},
"Response": {
"Result": "Success",
"ErrorCondition": null,
"AdditionalResponse": null
},
"PaymentReceipt": {
"amount": "10.20",
"tipsAmount": "0.00",
"currency": "HKD",
"payment_method": "fps",
"transaction_state": "success",
"date_time": "2023-06-30T09:08:52+00:00",
"merchant_id": "3333",
"terminal_id": null,
"rrn": "3263492852830699521",
"brn": "3263492852495159296",
"acquirer_type": "fps",
"receiptData": {
"reference_number": "W-158637380062",
"sale_transactions": [
{
"success": true,
"amount": "10.2",
"payment_method": 22,
"payment": "FPS",
"payment_data": {
"credit_card_type": "fps",
"new_gateway_txn_id": "3263492852495159296",
"rrn": "3263492852830699521",
"merchant_id": "3333",
"terminal_id": "",
"created_at": "2023-06-30T09:08:52+00:00",
"reference_id": "3263492852495159296",
"auth_code": "3263492852830699522"
}
}
],
"initial_total": "10.2",
"initial_tips": null,
"subtotal": "10.2"
}
}
}
}

Abort

      {
"AbortRequest": {
"AbortReason": "MerchantAbort",
"MessageReference": {
"MessageCategory": "Payment",
"ServiceID": "59b90600-13cf-11ee-bab2-99e6a6ef5392"
}
}
}

  • AbortReason: can be empty
  • MessageCategory: Support Payment/Preauth
  • ServiceID: TransactionID uuid of PaymentRequest/PreauthRequest

Reversal


{
"ReversalRequest": {
"SaleData": {
"SaleToAcquirerData": "currency=HKD"
},
"OriginalPOITransaction": {
"OriginalPOITransaction": {
"POITransactionID": {
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5049",
"OriginBRN": "3267887011708553217",
"ReferenceNumber": "8daf4dc0-6ad6-44b1-8256-a0f1549c0fa6"
}
}
},
"ReversedAmount": 2.0
}
}
{
"ReversalResponse": {
"POIData": null,
"PaymentReceipt": null,
"Response": {
"AdditionalResponse": null,
"ErrorCondition": null,
"Result": "Success"
},
"ReversedAmount": null
}
}

Void( The same as Reversal)

{
"VoidRequest": {
"OriginalPOITransaction": {
"POITransactionID": {
"TransactionID": "guid",
"OriginBRN": "3267887011708553217",
"ReferenceNumber": "8daf4dc0-6ad6-44b1-8256-a0f1549c0fa6"
}
}
}
}

TransactionStatus(Payment/Void/Reversal/Preauth)

{
"TransactionStatusRequest": {
"MessageReference": {
"ServiceID": "59b90600-13cf-11ee-bab2-99e6a6ef5392",
"MessageCategory": "Payment"
}
}
}
{
"TransactionStatusResponse": {
"RepeatedMessageResponse": null,
"Response": {
"AdditionalResponse": "TransactionStatusResponse is inProgress",
"ErrorCondition": "InProgress",
"Result": "Failure"
}
}
}
{
"TransactionStatusResponse": {
"RepeatedMessageResponse": {
"MessageHeader": {
"BusinessID": "ff467f02-5b69-45f3-81aa-bffcca55fe8f",
"MessageCategory": "TransactionStatus",
"MessageClass": "Service",
"MessageType": "Response",
"POIID": "PAX-A930-1170270945",
"ProtocolVersion": "1.0",
"ServiceID": "31fdrg23gg45人d34f4fg6211"
},
"RepeatedResponseMessageBody": {
"PaymentResponse": {
"POIData": {
"POITransactionID": {
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5392"
}
},
"PaymentReceipt": null,
"Response": {
"AdditionalResponse": null,
"ErrorCondition": "Aborted",
"Result": "Failure"
}
},
"ReversalResponse": null
}
},
"Response": {
"AdditionalResponse": null,
"ErrorCondition": null,
"Result": "Success"
}
}
}
{
"TransactionStatusResponse": {
"RepeatedMessageResponse": {
"MessageHeader": {
"BusinessID": "ff467f02-5b69-45f3-81aa-bffcca55fe8f",
"MessageCategory": "TransactionStatus",
"MessageClass": "Service",
"MessageType": "Response",
"POIID": "PAX-A930-1170270945",
"ProtocolVersion": "1.0",
"ServiceID": "31fdrg23gg4f42a5人d34f4fg6211"
},
"RepeatedResponseMessageBody": {
"PaymentResponse": {
"POIData": {
"POITransactionID": {
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5392"
}
},
"PaymentReceipt": {
"acquirer_type": "fps",
"amount": "10.20",
"brn": "3263590179977302016",
"currency": "HKD",
"date_time": "2023-06-30T10:45:33+00:00",
"merchant_id": "3333",
"payment_method": "fps",
"receiptData": {
"initial_tips": null,
"initial_total": "10.2",
"reference_number": "W-142631801078",
"sale_transactions": [
{
"amount": "10.2",
"payment": "FPS",
"payment_data": {
"auth_code": "3263590180312842242",
"created_at": "2023-06-30T10:45:33+00:00",
"credit_card_type": "fps",
"merchant_id": "3333",
"new_gateway_txn_id": "3263590179977302016",
"reference_id": "3263590179977302016",
"rrn": "3263590180312842241",
"terminal_id": ""
},
"payment_method": 22,
"success": true
}
],
"subtotal": "10.2"
},
"rrn": "3263590180312842241",
"terminal_id": null,
"tipsAmount": "0.00",
"transaction_state": "success"
},
"Response": {
"AdditionalResponse": null,
"ErrorCondition": null,
"Result": "Success"
}
},
"ReversalResponse": null
}
},
"Response": {
"AdditionalResponse": null,
"ErrorCondition": null,
"Result": "Success"
}
}
}

The Result of Response are “Success” and “Failure”

ErrorCondition:

  • InProgress(The task is underworking)
  • Aborted(The task is cancelled)
  • NotFound(Can not find the transaction)
  • UnKnown(Unknown error)

AdditionalResponse: Normally it has no content. If any, this is for reminding the developer.

TransactionStatus(Reversal)

"TransactionStatusRequest": {
"MessageReference": {
"ServiceID": "59b90600-13cf-11ee-bab2-99e6a6ef5941",
"MessageCategory": "Reversal"
}
}


"TransactionStatusResponse": {
"RepeatedMessageResponse": {
"MessageHeader": {
"BusinessID": "ff467f02-5b69-45f3-81aa-bffcca55fe8f",
"MessageCategory": "TransactionStatus",
"MessageClass": "Service",
"MessageType": "Response",
"POIID": "PAX-A930-1170270945",
"ProtocolVersion": "1.0",
"ServiceID": "31fdrg234g4g4f42a5人d34f4fg6211"
},
"RepeatedResponseMessageBody": {
"PaymentResponse": null,
"ReversalResponse": {
"POIData": {
"POITransactionID": {
"OriginBRN": null,
"TimeStamp": "2023-06-30T18:47:12.686525",
"TransactionID": "59b90600-13cf-11ee-bab2-99e6a6ef5941"
}
},
"PaymentReceipt": {
"acquirer_auth_code": "3263590180312842242",
"acquirer_rrn": "3263590180312842241",
"acquirer_settlement_at": null,
"adjust_times": 0,
"amount": "10.2",
"closed_at": null,
"consumer_identify": "",
"created_at": "2023-06-30T10:45:33.38Z",
"credit_card_fall_back": false,
"currency": "HKD",
"deleted_at": null,
"fee_amount": "0.1",
"fee_currency": "HKD",
"id": "3263590179977302016",
"initial_total": 0,
"mid": "3333",
"oms_transaction_id": 75780958,
"order": {
"additional_documents": null,
"auth_code": "bzoRKzdmKypmwPQ",
"business_name": "Bindo Labs Limited Test 2",
"confidence_store": null,
"correspondence_state": "paid",
"created_at": "2023-06-30T10:45:33Z",
"created_by": "Wonder POS",
"currency": "HKD",
"custom_attributes": null,
"customer_country_code": null,
"customer_dial_code": null,
"customer_email": null,
"customer_linked_source_type": null,
"customer_name": null,
"customer_phone": null,
"customer_reference": null,
"delivery_date": null,
"discount_total": 0,
"due_date": "2023-07-01 23:59:59",
"fail_transactions": [],
"files": null,
"from": 11,
"id": 15619648,
"initial_normal_tax": 0,
"initial_tax": 0,
"initial_tips": 0,
"initial_total": 10.2,
"inventory_status": null,
"line_items": [
{
"discount_total": 0,
"id": 18096861,
"image_url": "",
"label": "Charge",
"price": 10.2,
"purchasable_type": "Charge",
"purchase_id": null,
"quantity": 1,
"total": 10.2
}
],
"note": null,
"number": "202306301845331287548306",
"oms_delivery_note": {
"address1": null,
"address2": null,
"city": null,
"country": null,
"email": null,
"name": null,
"note": null,
"phone": null
},
"paid_total": "10.2",
"payment_link_url": "https:
"reference_number": "W-142631801078",
"refund_transactions": [],
"sale_transactions": [
{
"3ds": {
"enrolled": "",
"redirect_url": ""
},
"acquirer_response_body": "",
"allow_refund": false,
"allow_void": true,
"amount": 10.2,
"created_at": "2023-06-30T10:45:34Z",
"currency": "HKD",
"extra": {
"credit_card_type": null,
"first_6_digits": null,
"holder_name": null,
"last_4_digits": null,
"number": null
},
"from": 16,
"id": 75780958,
"is_pending": false,
"note": null,
"payment": "HKFPS",
"payment_data": {
"acquirer_name": "fake",
"acquirer_type": "fps",
"auth_code": "3263590180312842242",
"batch_no": "",
"brn": "75780958",
"device_id": "PAX-A930-1170270945",
"merchant_id": "3333",
"new_gateway_txn_id": "3263590179977302016",
"payment_inst": "",
"payment_method": "fps",
"rrn": "3263590180312842241",
"trace_no": "",
"transaction_state": "success"
},
"payment_method": 22,
"reference_id": "3263590179977302016",
"refunded_amount": 0,
"success": true,
"tips_amount": null,
"voided_at": null
}
],
"sale_type": "invoice_sale",
"signatures": [],
"state": "completed",
"subtotal": 10.2,
"surcharge_fee": 0,
"type": "Invoice",
"unpaid_total": "0",
"updated_at": "2023-06-30T10:45:34Z",
"void_line_items": [],
"void_transactions": []
},
"order_info": {
"customer_reference": null,
"reference_number": "W-142631801078"
},
"order_num": "202306301845331287548306",
"org_txn_id": "0",
"p_business_id": "ff467f02-5b69-45f3-81aa-bffcca55fe8f",
"payment_entry_type": "merchant_presented_qr_code",
"payment_method": "fps",
"payment_type": "sale",
"remark": "",
"settlement_amount": "10.1",
"settlement_currency": "HKD",
"signature": "",
"store_id": "538047",
"tag_length_values": null,
"tid": "",
"tid_batch_num": 0,
"tid_trace_num": 0,
"tipsAmount": "0",
"tipsTransaction": null,
"total_amount": "10.2",
"transacted_on": "0001-01-01T00:00:00Z",
"updated_at": "2023-06-30T10:45:33.905Z",
"void_at": null
},
"Response": {
"AdditionalResponse": "Success",
"ErrorCondition": null,
"Result": "Success"
},
"ReversedAmount": 2
}
}
},
"Response": {
"AdditionalResponse": null,
"ErrorCondition": null,
"Result": "Success"
}
}

SwitchBusiness

       {
"SwitchBusinessRequest": {
"BusinessID": "a5467f02-2914-5d1a-21bb-bffcca55fe94"
},
"SwitchBusinessResponse": {
"Response": {
"Result": "Success",
"AdditionalResponse": null,
"ErrorCondition": null
}
}
}


Attentions

  • If you need to use SwitchBusiness, call SwitchBusiness every time you call another API
  • all the slug/key/authorization/businessId both use store data with device bind(Except PaymentRequest)
  • We recommend that AppKey/AppSlug in PaymentRequest use the value corresponding to the (A/B/C/D) store

Encrypted Code(dart):

  • import thrid packages
    • encrypt: 5.0.1
    • pbkdf2ns: 0.0.2
 class EncryptionUtils {
static const salt = 'WMSaltV1';
static const int rounds = 4000;
static const int keyLength = 80;
static KeyMaterial keyMaterial = KeyMaterial('123456');

static init(String passphrase) {
keyMaterial = KeyMaterial(passphrase);
}

static SaleToPoiResponse encryptWonderBlob(Map<String, dynamic>? map) {
if (map == null) return SaleToPoiResponse();
var str = json.encode(map);


var iv_2 = List<int>.generate(16, (i) => Random.secure().nextInt(256));


var iv = List<int>.generate(16, (i) => keyMaterial.iv[i] ^ iv_2[i]);


final encrypter = Encrypter(
AES(Key(Uint8List.fromList(keyMaterial.cipherKey)), mode: AESMode.cbc));
final encrypted =
encrypter.encrypt(str, iv: IV(Uint8List.fromList(iv))).base64;

String keyhex = keyMaterial.hmacKey
.map((i) => i.toRadixString(16).padLeft(2, '0'))
.join("");

var hash = Hmac(sha256, utf8.encode(keyhex)).convert(str.codeUnits).bytes;

return SaleToPoiResponse.fromJson({
"SecurityTrailer": {
"KeyVersion": "",
"KeyIdentifier": "",
"Hmac": hash.toBase64(),
"Nonce": iv_2.toBase64(),
"WonderCryptoVersion": 1

},
"WonderBlob": encrypted

});
}

static decryptWonderBlob(String wonderBlob, SecurityTrailer securityTrailer) {
try {
var hmac_key = base64.decode(securityTrailer.hmac!);
var iv_2 = base64.decode(securityTrailer.nonce!);

var iv = List<int>.generate(16, (i) => keyMaterial.iv[i] ^ iv_2[i]);

final key = Key(Uint8List.fromList(keyMaterial.cipherKey));
final encrypter = Encrypter(AES(key, mode: AESMode.cbc));
var result = encrypter.decrypt(Encrypted.fromBase64(wonderBlob),
iv: IV(Uint8List.fromList(iv)));

String keyhex = keyMaterial.hmacKey
.map((i) => i.toRadixString(16).padLeft(2, '0'))
.join("");
var hash =
Hmac(sha256, utf8.encode(keyhex)).convert(result.codeUnits).bytes;

if (hmac_key.toBase64() != hash.toBase64()) {
throw DecryptException('');
}
return result;
} catch (e) {
throw DecryptException('Decryption failure');
}
}
}
class KeyMaterial {
late List<int> hmacKey;
late List<int> cipherKey;
late List<int> iv;

KeyMaterial(String passphrase) {
PBKDF2NS gen = PBKDF2NS(hash: sha1);
List<int> key = gen.generateKey(passphrase, EncryptionUtils.salt,
EncryptionUtils.rounds, EncryptionUtils.keyLength);
hmacKey = key.sublist(0, 32);
cipherKey = key.sublist(32, 64);
iv = key.sublist(64, 80);
}
}

Encrypted Code(go):

  • import thrid packages
    • https:
package encrypt

import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"fmt"

"github.com/forgoer/openssl"
"golang.org/x/crypto/pbkdf2"
)

type SecretResp struct {
Data struct {
EnableEcr bool `json:"enable_ecr"`
EcrKey string `json:"ecr_key"`
} `json:"data"`
Code int `json:"code"`
Message string `json:"message"`
}

type KeyMaterial struct {
HmacKey []byte
CipherKey []byte
IV []byte
}

type SecurityTrailer struct {
KeyVersion string `json:"KeyVersion"`
KeyIdentifier string `json:"KeyIdentifier"`
Hmac string `json:"Hmac"`
Nonce string `json:"Nonce"`
WonderCryptoVersion int `json:"WonderCryptoVersion"`
}

type SaleToPoiResponse struct {
SecurityTrailer SecurityTrailer `json:"SecurityTrailer"`
WonderBlob string `json:"WonderBlob"`
WonderCryptoVersion int `json:"WonderCryptoVersion"`
}

type Encryption struct {
salt string
rounds int
keyLength int
}

func NewEncryption() *Encryption {
return &Encryption{
salt: "WMSaltV1",
rounds: 4000,
keyLength: 80,
}
}

func (e *Encryption) GenerateKeyMaterial(passphrase string) KeyMaterial {
var keyMaterial KeyMaterial
key := pbkdf2.Key([]byte(passphrase), []byte(e.salt), e.rounds, e.keyLength, sha1.New)
keyMaterial.HmacKey = key[:32]
keyMaterial.CipherKey = key[32:64]
keyMaterial.IV = key[64:80]
return keyMaterial
}

func (e *Encryption) EncryptWonderBlob(src interface{}, keyMaterial KeyMaterial) (*SaleToPoiResponse, error) {
if src == nil {
return nil, errors.New("")
}
plaintext, err := json.Marshal(src)
if err != nil {
return nil, err
}






iv2 := []byte{255, 88, 159, 70, 231, 201, 10, 60, 95, 140, 221, 100, 82, 152, 239, 115}

iv := make([]byte, 16)
for i := range iv {
iv[i] = keyMaterial.IV[i] ^ iv2[i]
}

ciphertext, err := openssl.AesCBCEncrypt(plaintext, keyMaterial.CipherKey, iv, openssl.PKCS7_PADDING)
if err != nil {
return nil, err
}
fmt.Println(base64.StdEncoding.EncodeToString(ciphertext))

hash, err := calculateHash(plaintext, keyMaterial.HmacKey)
if err != nil {
return nil, err
}
fmt.Println(hash)

return &SaleToPoiResponse{
SecurityTrailer: SecurityTrailer{
Hmac: base64.StdEncoding.EncodeToString(hash),
Nonce: base64.StdEncoding.EncodeToString(iv2),
WonderCryptoVersion: 1,
},
WonderBlob: base64.StdEncoding.EncodeToString(ciphertext),
}, nil
}

func (e *Encryption) DecryptWonderBlob(ciphertext []byte, securityTrailer SecurityTrailer, keyMaterial KeyMaterial) ([]byte, error) {
iv2, err := base64.StdEncoding.DecodeString(securityTrailer.Nonce)
if err != nil {
return nil, err
}
iv := make([]byte, 16)
for i := range iv {
iv[i] = keyMaterial.IV[i] ^ iv2[i]
}

plaintext, err := openssl.AesCBCDecrypt(ciphertext, keyMaterial.CipherKey, iv, openssl.PKCS7_PADDING)
if err != nil {
return nil, err
}
fmt.Println(string(plaintext))

hash, err := calculateHash(plaintext, keyMaterial.HmacKey)
if err != nil {
return nil, err
}
if securityTrailer.Hmac != base64.StdEncoding.EncodeToString(hash) {
return nil, errors.New("HMAC verification failed")
}
return plaintext, nil
}

func calculateHash(message []byte, hmacKey []byte) ([]byte, error) {
var keyHex string
for _, b := range hmacKey {
keyHex += fmt.Sprintf("%02x", b)
}
mac := hmac.New(sha256.New, []byte(keyHex))
_, err := mac.Write(message)
if err != nil {
return nil, err
}
return mac.Sum(nil), nil
}

Encrypted data flow




[165, 138, 241, 50, 162, 203, 20, 42, 15, 10, 129, 234, 91, 89, 216, 213, 54, 156, 17, 145, 252, 246, 97, 56, 79, 254, 159, 155, 195, 102, 221, 214]

[124, 16, 196, 209, 149, 179, 163, 231, 22, 222, 229, 9, 69, 253, 158, 112, 161, 10, 5, 57, 4, 59, 181, 224, 116, 159, 128, 19, 26, 176, 103, 124]

[97, 99, 31, 10, 239, 170, 238, 34, 14, 249, 144, 174, 230, 229, 97, 15]

[255, 88, 159, 70, 231, 201, 10, 60, 95, 140, 221, 100, 82, 152, 239, 115]

[158, 59, 128, 76, 8, 99, 228, 30, 81, 117, 77, 202, 180, 125, 142, 124]

xd+O7byVDPt6YCm9FagPmQ==

a58af132a2cb142a0f0a81ea5b59d8d5369c1191fcf661384ffe9f9bc366ddd6

[68, 206, 161, 97, 35, 65, 25, 67, 195, 186, 190, 10, 218, 98, 97, 76, 138, 1, 72, 81, 164, 30, 124, 193, 250, 129, 2, 41, 48, 4, 111, 231]

return SaleToPoiResponse.fromJson({
"SecurityTrailer": {
"KeyVersion": "",
"KeyIdentifier": "",
"Hmac": hash.toBase64(),
"Nonce": iv_2.toBase64(),
"WonderCryptoVersion": 1

},
"WonderBlob": encrypted
});