blockchainofthings/catenis-api-client

PHP 的 Catenis API 客户端库

v6.0.1 2022-10-05 14:28 UTC

This package is auto-updated.

Last update: 2024-09-05 19:14:49 UTC


README

此库用于简化从 PHP 应用程序访问 Catenis API 服务。

当前版本(6.0.1)针对 Catenis API 的 0.12 版本。

安装

推荐使用 Composer 安装 Catenis API PHP 客户端。

要将 Catenis API 客户端作为依赖项添加到您的项目中,请按照以下步骤操作

  1. 将以下存储库条目添加到您的 composer.json 文件中
{
  "repositories": [
    {
        "type": "vcs",
        "url": "https://github.com/claudiosdc/react-guzzle-psr7.git"
    },
    {
        "type": "vcs",
        "url": "https://github.com/claudiosdc/react-guzzle-http-client.git"
    },
    {
        "type": "vcs",
        "url": "https://github.com/claudiosdc/reactphp-buzz.git"
    }
  ]
}
  1. 然后,通过以下方式添加所需的依赖项

运行以下命令

composer require blockchainofthings/catenis-api-client:~3.0 wyrihaximus/react-guzzle-psr7:dev-decode-content wyrihaximus/react-guzzle-http-client:dev-decode-content clue/buzz-react:dev-decode-content

或直接编辑 composer.json 文件

{
    "require": {
        "blockchainofthings/catenis-api-client": "~6.0",
        "wyrihaximus/react-guzzle-psr7": "dev-decode-content",
        "wyrihaximus/react-guzzle-http-client": "dev-decode-content",
        "clue/buzz-react": "dev-decode-content"
    }
}

注意:通常,只需要列出 blockchainofthings/catenis-api-client 包。但是,由于此版本的 Catenis API 库依赖于其他三个包的特殊修补版本,因此它们也需要在这里列出。

用法

只需包含 Composer 的 vendor/autoload.php 文件,Catenis API 客户端组件即可在您的代码中使用。

require __DIR__ . 'vendor/autoload.php';

实例化客户端

$ctnApiClient = new \Catenis\ApiClient(
    $deviceId,
    $apiAccessSecret,
    [
        'environment' => 'sandbox'
    ]
);

可选地,客户端可以不传递 $deviceId$apiAccessSecret 参数或将它们设置为 null 来实例化,如下所示。在这种情况下,应仅使用生成的客户端对象调用 公共 API 方法。

$ctnApiClient = new \Catenis\ApiClient(null, null,
    [
        'environment' => 'sandbox'
    ]
);

构造函数选项

在实例化客户端时可以使用以下选项

  • host [字符串] - (可选,默认: 'catenis.io') 目标 Catenis API 服务器的主机名(可选端口号)。
  • environment [字符串] - (可选,默认: 'prod') 目标 Catenis API 服务器环境。有效值:'prod''sandbox'
  • secure [布尔值] - (可选,默认: true) 指示是否应使用安全连接(HTTPS)。
  • version [字符串] - (可选,默认: '0.12') 要针对的 Catenis API 版本。
  • useCompression [布尔值] - (可选,默认: true) 指示是否应压缩请求/响应正文。
  • compressThreshold [整数] - (可选,默认: 1024) 请求正文的最小大小(以字节为单位),以便压缩。
  • timeout [浮点数|整数] - (可选,默认: 0,无超时) 等待响应的超时时间(以秒为单位)。
  • eventLoop [React\EventLoop\LoopInterface] - (可选) 用于异步 API 方法调用机制的事件循环。
  • pumpTaskQueue [布尔值] - (可选,默认: true) 指示是否强制定期运行承诺任务队列。
  • pumpInterval [整数] - (可选,默认: 10) 指定定期运行任务队列的时间间隔(以毫秒为单位)。

异步方法调用

每个 API 方法都有一个异步对应方法,该方法具有 Async 后缀,例如 logMessageAsync

异步方法返回一个承诺,并且当与事件循环一起使用时,可以在异步方式中处理其结果。

要使用事件循环,请在实例化 ApiClient 对象时传递事件循环实例作为选项。

$loop = \React\EventLoop\Factory::create();

$ctnApiClient = new \Catenis\ApiClient(
    $deviceId,
    $apiAccessSecret,
    [
        'environment' => 'sandbox'
        'eventLoop' => $loop
    ]
);

异步 API 方法调用处理的示例。

$ctnApiClient->logMessageAsync('My message')->then(
    function (stdClass $data) {
        // Process returned data
    },
    function (\Catenis\Exception\CatenisException $ex) {
        // Process exception
    }
);

注意:为了异步解析承诺,应定期运行承诺任务队列(即 \GuzzleHttp\Promise\queue()->run())。默认情况下,Catenis API PHP客户端将每10毫秒运行一次承诺任务队列。要设置运行承诺任务队列的不同间隔,在实例化Catenis API PHP客户端时传递可选参数 pumpInterval,该参数为与所需毫秒数相对应的整数值(例如,'pumpInterval' => 5)。或者,为了避免运行承诺任务队列,在实例化客户端时传递可选参数 pumpTaskQueue 并将其设置为 false(即 'pumpTaskQueue' => false)。在这种情况下,最终用户应负责运行承诺任务队列。

要强制返回的承诺完成并获取API方法的返回数据,请使用其 wait() 方法。

try {
    $data = $ctnApiClient->logMessageAsync('My message')->wait();

    // Process returned data
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

返回的数据

在成功调用Catenis API时,客户端库方法返回的数据仅包括Catenis API请求原响应中返回的JSON的 data 属性。

例如,您应该期望从成功调用 logMessage 方法的以下数据被返回

object(stdClass)#54 (1) {
  ["messageId"]=>
  string(20) "m57enyYQK7QmqSxgP94j"
}

将消息记录到区块链

一次性传递整个消息的内容

try {
    $data = $ctnApiClient->logMessage('My message', [
        'encoding' => 'utf8',
        'encrypt' => true,
        'offChain' => true,
        'storage' => 'auto'
    ]);

    // Process returned data
    echo 'ID of logged message: ' . $data->messageId . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

分块传递消息的内容

$message = [
    'First part of message',
    'Second part of message',
    'Third and last part of message'
];

try {
    $continuationToken = null;

    foreach ($message as $chunk) {
        $data = $ctnApiClient->logMessage([
            'data' => $chunk,
            'isFinal' => false,
            'continuationToken' => $continuationToken
        ], [
            'encoding' => 'utf8'
        ]);

        $continuationToken = $data->continuationToken;
    }

    // Signal that message has ended and get result
    $data = $ctnApiClient->logMessage([
        'isFinal' => true,
        'continuationToken' => $continuationToken
    ], [
        'encrypt' => true,
        'offChain' => true,
        'storage' => 'auto'
    ]);

    echo 'ID of logged message: ' . $data->messageId . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

异步记录消息

try {
    $data = $ctnApiClient->logMessage('My message', [
        'encoding' => 'utf8',
        'encrypt' => true,
        'offChain' => true,
        'storage' => 'auto',
        'async' => true
    ]);

    // Start pooling for asynchronous processing progress
    $provisionalMessageId = $data->provisionalMessageId;
    $done = false;
    $result = null;
    wait(1);

    do {
        $data = $ctnApiClient->retrieveMessageProgress($provisionalMessageId);

        // Process returned data
        echo 'Number of bytes processed so far: ' . $data->progress->bytesProcessed . PHP_EOL;
            
        if ($data->progress->done) {
            if ($data->progress->success) {
                // Get result
                $result = $data->result;
            } else {
                // Process error
                echo 'Asynchronous processing error: [' . $data->progress->error->code . '] - '
                    . $data->progress->error->message . PHP_EOL;
            }

            $done = true;
        } else {
            // Asynchronous processing not done yet. Wait before continuing pooling
            wait(3);
        }
    } while (!$done);

    if (!is_null($result)) {
        echo 'ID of logged message: ' . $result->messageId . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

向另一个设备发送消息

一次性传递整个消息的内容

try {
    $data = $ctnApiClient->sendMessage('My message', [
        'id' => $targetDeviceId,
        'isProdUniqueId' => false
    ], [
        'encoding' => 'utf8',
        'encrypt' => true,
        'offChain' => true,
        'storage' => 'auto',
        'readConfirmation' => true
    ]);

    // Process returned data
    echo 'ID of sent message: ' . $data->messageId . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

分块传递消息的内容

$message = [
    'First part of message',
    'Second part of message',
    'Third and last part of message'
];

try {
    $continuationToken = null;

    foreach ($message as $chunk) {
        $data = $ctnApiClient->sendMessage([
            'data' => $chunk,
            'isFinal' => false,
            'continuationToken' => $continuationToken
        ], [
            'id' => $targetDeviceId,
            'isProdUniqueId' => false
        ], [
            'encoding' => 'utf8'
        ]);

        $continuationToken = $data->continuationToken;
    }

    // Signal that message has ended and get result
    $data = $ctnApiClient->sendMessage([
        'isFinal' => true,
        'continuationToken' => $continuationToken
    ], [
        'id' => $targetDeviceId,
        'isProdUniqueId' => false
    ], [
        'encrypt' => true,
        'offChain' => true,
        'storage' => 'auto',
        'readConfirmation' => true
    ]);

    echo 'ID of sent message: ' . $data->messageId . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

异步发送消息

try {
    $data = $ctnApiClient->sendMessage('My message', [
        'id' => $targetDeviceId,
        'isProdUniqueId' => false
    ], [
        'encoding' => 'utf8',
        'encrypt' => true,
        'offChain' => true,
        'storage' => 'auto',
        'readConfirmation' => true,
        'async' => true
    ]);

    // Start pooling for asynchronous processing progress
    $provisionalMessageId = $data->provisionalMessageId;
    $done = false;
    $result = null;
    wait(1);

    do {
        $data = $ctnApiClient->retrieveMessageProgress($provisionalMessageId);

        // Process returned data
        echo 'Number of bytes processed so far: ' . $data->progress->bytesProcessed . PHP_EOL;
            
        if ($data->progress->done) {
            if ($data->progress->success) {
                // Get result
                $result = $data->result;
            } else {
                // Process error
                echo 'Asynchronous processing error: [' . $data->progress->error->code . '] - '
                    . $data->progress->error->message . PHP_EOL;
            }

            $done = true;
        } else {
            // Asynchronous processing not done yet. Wait before continuing pooling
            wait(3);
        }
    } while (!$done);

    if (!is_null($result)) {
        echo 'ID of sent message: ' . $result->messageId . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

读取消息

一次性检索整个已读消息的内容

try {
    $data = $ctnApiClient->readMessage($messageId, 'utf8');

    // Process returned data
    if ($data->msgInfo->action === 'send') {
        echo 'Message sent from: ' . print_r($data->msgInfo->from, true);
    }

    echo 'Read message: ' . $data->msgData . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

分块检索已读消息的内容

try {
    $continuationToken = null;
    $chunkCount = 1;

    do {
        $data = $ctnApiClient->readMessage($messageId, [
            'encoding' => 'utf8',
            'continuationToken' => $continuationToken,
            'dataChunkSize' => 1024
        ]);

        // Process returned data
        if (isset($data->msgInfo) && $data->msgInfo->action == 'send') {
            echo 'Message sent from: ' . $data->msgInfo->from);
        }
        
        echo 'Read message (chunk ' . $chunkCount . '): ' . $data->msgData);
        
        if (isset($data->continuationToken)) {
            // Get continuation token to continue reading message
            $continuationToken = $data->continuationToken;
            $chunkCount += 1;
        } else {
            $continuationToken = null;
        }
    } while (!is_null($continuationToken));
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

异步读取消息

try {
    // Request to read message asynchronously
    $data = $ctnApiClient->readMessage($messageId, [
        'async' => true
    ]);

    // Start pooling for asynchronous processing progress
    $cachedMessageId = $data->cachedMessageId;
    $done = false;
    $result = null;
    wait(1);

    do {
        $data = $ctnApiClient->retrieveMessageProgress($cachedMessageId);

        // Process returned data
        echo 'Number of bytes processed so far: ' . $data->progress->bytesProcessed . PHP_EOL;
            
        if ($data->progress->done) {
            if ($data->progress->success) {
                // Get result
                $result = $data->result;
            } else {
                // Process error
                echo 'Asynchronous processing error: [' . $data->progress->error->code . '] - '
                    . $data->progress->error->message . PHP_EOL;
            }

            $done = true;
        } else {
            // Asynchronous processing not done yet. Wait before continuing pooling
            wait(3);
        }
    } while (!$done);

    if (!is_null($result)) {
        // Retrieve read message
        $data = $ctnApiClient->readMessage($messageId, [
            'encoding' => 'utf8',
            'continuationToken' => $result->continuationToken
        ]);

        if ($data->msgInfo->action === 'send') {
            echo 'Message sent from: ' . print_r($data->msgInfo->from, true);
        }

        echo 'Read message: ' . $data->msgData . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

检索消息容器的信息

try {
    $data = $ctnApiClient->retrieveMessageContainer($messageId);

    // Process returned data
    if (isset($data->offChain)) {
        echo 'IPFS CID of Catenis off-chain message envelope: ' . $data->offChain->cid . PHP_EOL;
    }
    
    if (isset($data->blockchain)) {
        echo 'ID of blockchain transaction containing the message: ' . $data->blockchain->txid . PHP_EOL;
    }

    if (isset($data->externalStorage)) {
        echo 'IPFS reference to message: ' . $data->externalStorage->ipfs . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

检索消息源的信息

try {
    $data = $ctnApiClient->retrieveMessageOrigin($messageId, 'Any text to be signed');

    // Process returned data
    if (isset($data->tx)) {
        echo 'Catenis message transaction info: ' . print_r($data->tx, true);
    }
    
    if (isset($data->offChainMsgEnvelope)) {
        echo 'Off-chain message envelope info: ' . print_r($data->offChainMsgEnvelope, true);
    }

    if (isset($data->proof)) {
        echo 'Origin proof info: ' . print_r($data->proof, true);
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

检索异步消息处理进度

try {
    $data = $ctnApiClient->retrieveMessageProgress($provisionalMessageId);

    // Process returned data
    echo 'Number of bytes processed so far: ' . $data->progress->bytesProcessed . PHP_EOL;

    if ($data->progress->done) {
        if ($data->progress->success) {
            // Get result
            echo 'Asynchronous processing result: ' . $data->result . PHP_EOL;
        }
        else {
            // Process error
            echo 'Asynchronous processing error: [' . $data->progress->error->code . '] - '
                . $data->progress->error->message . PHP_EOL;
        }
    } else {
        // Asynchronous processing not done yet. Continue pooling
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

注意:请参阅上面的 异步记录消息异步发送消息异步读取消息 部分,以获取更完整的示例。

列出消息

try {
    $data = $ctnApiClient->listMessages([
        'action' => 'send',
        'direction' => 'inbound',
        'readState' => 'unread',
        'startDate' => new \DateTime('20170101T000000Z')
    ], 200, 0);

    // Process returned data
    if ($data->msgCount > 0) {
        echo 'Returned messages: ' . print_r($data->messages, true);
        
        if ($data->hasMore) {
            echo 'Not all messages have been returned' . PHP_EOL;
        }
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

注意listMessages 方法接受的参数与List Messages Catenis API方法接受的参数不完全相同。大多数参数(除了最后两个limitskip)都映射到listMessages 方法的第一个参数($selector)的键,有几个特殊性:参数fromDeviceIdsfromDeviceProdUniqueIds与参数toDeviceIdstoDeviceProdUniqueIds分别替换为键fromDevicestoDevices。这些键接受值为设备ID的关联数组索引数组,这与sendMessage 方法的第一个参数($targetDevice)接受的关联数组类型相同。日期键startDateendDate不仅接受包含ISO 8601格式日期/时间的字符串值,还接受DateTime对象。

发行一种新的资产数量

try {
    $data = $ctnApiClient->issueAsset([
        'name' => 'XYZ001',
        'description' => 'My first test asset',
        'canReissue' => true,
        'decimalPlaces' => 2
    ], 1500.00, null);

    // Process returned data
    echo 'ID of newly issued asset: ' . $data->assetId . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

发行现有资产额外的数量

try {
    $data = $ctnApiClient->reissueAsset($assetId, 650.25, [
        'id' => $otherDeviceId,
        'isProdUniqueId' => false
    ]);

    // Process returned data
    echo 'Total existent asset balance (after issuance): ' . $data->totalExistentBalance . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

将资产的数量转移到另一个设备

try {
    $data = $ctnApiClient->transferAsset($assetId, 50.75, [
        'id' => $otherDeviceId,
        'isProdUniqueId' => false
    ]);

    // Process returned data
    echo 'Remaining asset balance: ' . $data->remainingBalance . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

创建一个新的非同质化资产并发行其(初始)非同质化代币

在单次调用中传递非同质化代币内容

try {
    $data = $ctnApiClient->issueNonFungibleAsset([
        'assetInfo' => [
            'name' => 'Catenis NFA 1',
            'description' => 'Non-fungible asset #1 for testing',
            'canReissue' => true
        ]
    ], [
        [
            'metadata' => [
                'name' => 'NFA1 NFT 1',
                'description' => 'First token of Catenis non-fungible asset #1'
            ],
            'contents' => [
                'data' => 'Contents of first token of Catenis non-fungible asset #1',
                'encoding' => 'utf8'
            ]
        ],
        [
            'metadata' => [
                'name' => 'NFA1 NFT 2',
                'description' => 'Second token of Catenis non-fungible asset #1'
            ],
            'contents' => [
                'data' => 'Contents of second token of Catenis non-fungible asset #1',
                'encoding' => 'utf8'
            ]
        ]
    ]);

    // Process returned data
    echo 'ID of newly created non-fungible asset: ' . $data->assetId . PHP_EOL;
    echo 'IDs of newly issued non-fungible tokens: ' . implode(', ', $data->nfTokenIds) . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

在多次调用中传递非同质化代币内容

$issuanceInfo = [
    'assetInfo' => [
        'name' => 'Catenis NFA 1',
        'description' => 'Non-fungible asset #1 for testing',
        'canReissue' => true
    ]
];
$nftMetadata = [
    [
        'name' => 'NFA1 NFT 1',
        'description' => 'First token of Catenis non-fungible asset #1'
    ],
    [
        'name' => 'NFA1 NFT 2',
        'description' => 'Second token of Catenis non-fungible asset #1'
    ]
];
$nftContents = [
    [
        [
            'data' => 'Contents of first token of Catenis non-fungible asset #1',
            'encoding' => 'utf8'
        ]
    ],
    [
        [
            'data' => 'Here is the contents of the second token of Catenis non-fungible asset #1 (part #1)',
            'encoding' => 'utf8'
        ],
        [
            'data' => '; and here is the last part of the contents of the second token of Catenis non-fungible asset #1.',
            'encoding' => 'utf8'
        ]
    ]
];

try {
    $continuationToken = null;
    $data = null;
    $nfTokens = null;
    $callIdx = -1;

    do {
        $nfTokens = null;
        $callIdx++;

        if ($continuationToken === null) {
            foreach ($nftMetadata as $tokenIdx => $metadata) {
                $nfToken = [
                    'metadata' => $metadata
                ];

                if (isset($nftContents[$tokenIdx])) {
                    $nfToken['contents'] = $nftContents[$tokenIdx][$callIdx];
                }

                $nfTokens[] = $nfToken;
            }
        }
        else {  // Continuation call
            foreach ($nftContents as $tokenIdx => $contents) {
                $nfTokens[] = isset($contents) && $callIdx < count($callIdx)
                    ? ['contents' => $contents[$callIdx]]
                    : null;
            }

            if (is_array($nfTokens)) {
                $allNull = true;

                foreach ($nfTokens as $tokenIdx => $nfToken) {
                    if ($nfToken !== null) {
                        $allNull = false;
                        break;
                    }
                }

                if ($allNull) {
                    $nfTokens = null;
                }
            }
        }

        $data = $ctnApiClient->issueNonFungibleAsset(
            $continuationToken !== null ? $continuationToken : $issuanceInfo,
            $nfTokens,
            !isset($nfTokens)
        );

        $continuationToken = isset($data->continuationToken)
            ? $data->continuationToken
            : null;
    } while ($continuationToken !== null);

    // Process returned data
    echo 'ID of newly created non-fungible asset: ' . $data->assetId . PHP_EOL;
    echo 'IDs of newly issued non-fungible tokens: ' . implode(', ', $data->nfTokenIds) . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

异步进行发行

try {
    $data = $ctnApiClient->issueNonFungibleAsset([
        'assetInfo' => [
            'name' => 'Catenis NFA 1',
            'description' => 'Non-fungible asset #1 for testing',
            'canReissue' => true
        ],
        'async' => true
    ], [
        [
            'metadata' => [
                'name' => 'NFA1 NFT 1',
                'description' => 'First token of Catenis non-fungible asset #1'
            ],
            'contents' => [
                'data' => 'Contents of first token of Catenis non-fungible asset #1',
                'encoding' => 'utf8'
            ]
        ],
        [
            'metadata' => [
                'name' => 'NFA1 NFT 2',
                'description' => 'Second token of Catenis non-fungible asset #1'
            ],
            'contents' => [
                'data' => 'Contents of second token of Catenis non-fungible asset #1',
                'encoding' => 'utf8'
            ]
        ]
    ]);

    // Start pooling for asynchronous processing progress
    $assetIssuanceId = $data->assetIssuanceId;
    $done = false;
    $result = null;
    wait(1);

    do {
        $data = $ctnApiClient->retrieveNonFungibleAssetIssuanceProgress($assetIssuanceId);

        // Process returned data
        echo 'Percent processed: ', $data->progress->percentProcessed . PHP_EOL;
            
        if ($data->progress->done) {
            if ($data->progress->success) {
                // Get result
                $result = $data->result;
            } else {
                // Process error
                echo 'Asynchronous processing error: [' . $data->progress->error->code . '] - '
                    . $data->progress->error->message . PHP_EOL;
            }

            $done = true;
        } else {
            // Asynchronous processing not done yet. Wait before continuing pooling
            wait(3);
        }
    } while (!$done);

    if ($result !== null) {
        echo 'ID of newly created non-fungible asset: ' . $result->assetId . PHP_EOL;
        echo 'IDs of newly issued non-fungible tokens: ' . implode(', ', $result->nfTokenIds) . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

为先前创建的非同质化资产发行更多非同质化代币

在单次调用中传递非同质化代币内容

try {
    $data = $ctnApiClient->reissueNonFungibleAsset($assetId, null, [
        [
            'metadata' => [
                'name' => 'NFA1 NFT 3',
                'description' => 'Third token of Catenis non-fungible asset #1'
            ],
            'contents' => [
                'data' => 'Contents of third token of Catenis non-fungible asset #1',
                'encoding' => 'utf8'
            ]
        ],
        [
            'metadata' => [
                'name' => 'NFA1 NFT 4',
                'description' => 'Forth token of Catenis non-fungible asset #1'
            ],
            'contents' => [
                'data' => 'Contents of forth token of Catenis non-fungible asset #1',
                'encoding' => 'utf8'
            ]
        ]
    ]);

    // Process returned data
    echo 'IDs of newly issued non-fungible tokens: ' . $data->nfTokenIds . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

在多次调用中传递非同质化代币内容

$nftMetadata = [
    [
        'name' => 'NFA1 NFT 3',
        'description' => 'Third token of Catenis non-fungible asset #1'
    ],
    [
        'name' => 'NFA1 NFT 4',
        'description' => 'Forth token of Catenis non-fungible asset #1'
    ]
];
$nftContents = [
    [
        [
            'data' => 'Contents of third token of Catenis non-fungible asset #1',
            'encoding' => 'utf8'
        ]
    ],
    [
        [
            'data' => 'Here is the contents of the forth token of Catenis non-fungible asset #1 (part #1)',
            'encoding' => 'utf8'
        ],
        [
            'data' => '; and here is the last part of the contents of the forth token of Catenis non-fungible asset #1.',
            'encoding' => 'utf8'
        ]
    ]
];

try {
    $continuationToken = null;
    $data = null;
    $nfTokens = null;
    $callIdx = -1;

    do {
        $nfTokens = null;
        $callIdx++;

        if ($continuationToken === null) {
            foreach ($nftMetadata as $tokenIdx => $metadata) {
                $nfToken = [
                    'metadata' => $metadata
                ];

                if (isset($nftContents[$tokenIdx])) {
                    $nfToken['contents'] = $nftContents[$tokenIdx][$callIdx];
                }

                $nfTokens[] = $nfToken;
            }
        }
        else {  // Continuation call
            foreach ($nftContents as $tokenIdx => $contents) {
                $nfTokens[] = isset($contents) && $callIdx < count($callIdx)
                    ? ['contents' => $contents[$callIdx]]
                    : null;
            }

            if (is_array($nfTokens)) {
                $allNull = true;

                foreach ($nfTokens as $tokenIdx => $nfToken) {
                    if ($nfToken !== null) {
                        $allNull = false;
                        break;
                    }
                }

                if ($allNull) {
                    $nfTokens = null;
                }
            }
        }

        $data = $ctnApiClient->reissueNonFungibleAsset(
            $assetId,
            $continuationToken,
            $nfTokens,
            !isset($nfTokens)
        );

        $continuationToken = isset($data->continuationToken)
            ? $data->continuationToken
            : null;
    } while ($continuationToken !== null);

    // Process returned data
    echo 'IDs of newly issued non-fungible tokens: ' . implode(', ', $data->nfTokenIds) . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

异步进行发行

try {
    $data = $ctnApiClient->reissueNonFungibleAsset($assetId, [
        'async' => true
    ], [
        [
            'metadata' => [
                'name' => 'NFA1 NFT 3',
                'description' => 'Third token of Catenis non-fungible asset #1'
            ],
            'contents' => [
                'data' => 'Contents of third token of Catenis non-fungible asset #1',
                'encoding' => 'utf8'
            ]
        ],
        [
            'metadata' => [
                'name' => 'NFA1 NFT 4',
                'description' => 'Forth token of Catenis non-fungible asset #1'
            ],
            'contents' => [
                'data' => 'Contents of forth token of Catenis non-fungible asset #1',
                'encoding' => 'utf8'
            ]
        ]
    ]);

    // Start pooling for asynchronous processing progress
    $assetIssuanceId = $data->assetIssuanceId;
    $done = false;
    $result = null;
    wait(1);

    do {
        $data = $ctnApiClient->retrieveNonFungibleAssetIssuanceProgress($assetIssuanceId);

        // Process returned data
        echo 'Percent processed: ', $data->progress->percentProcessed . PHP_EOL;
            
        if ($data->progress->done) {
            if ($data->progress->success) {
                // Get result
                $result = $data->result;
            } else {
                // Process error
                echo 'Asynchronous processing error: [' . $data->progress->error->code . '] - '
                    . $data->progress->error->message . PHP_EOL;
            }

            $done = true;
        } else {
            // Asynchronous processing not done yet. Wait before continuing pooling
            wait(3);
        }
    } while (!$done);

    if ($result !== null) {
        echo 'IDs of newly issued non-fungible tokens: ' . implode(', ', $result->nfTokenIds) . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

检索与非同质化代币关联的数据

同步检索

try {
    $continuationToken = null;
    $data = null;
    $nfTokenData = null;

    do {
        $data = $ctnApiClient->retrieveNonFungibleToken(
            $tokenId,
            isset($continuationToken) ? ['continuationToken' => $continuationToken] : null
        );

        if (!isset($nfTokenData)) {
            // Get token data
            $nfTokenData = (object)[
                'assetId' => $data->nonFungibleToken->assetId,
                'metadata' => $data->nonFungibleToken->metadata,
                'contents' => [$data->nonFungibleToken->contents->data]
            ];
        } else {
            // Add next contents part to token data
            $nfTokenData->contents[] = $data->nonFungibleToken->contents->data;
        }

        $continuationToken = isset($data->continuationToken)
            ? $data->continuationToken
            : null;
    } while ($continuationToken !== null);

    // Process returned data
    echo 'Non-fungible token data: ' . print_r($nfTokenData, true);
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

异步检索

try {
    $data = $ctnApiClient->retrieveNonFungibleToken($tokenId, [
        'async' => true
    ]);

    // Start pooling for asynchronous processing progress
    $tokenRetrievalId = $data->tokenRetrievalId;
    $done = false;
    $continuationToken = null;
    wait(1);

    do {
        $data = $ctnApiClient->retrieveNonFungibleTokenRetrievalProgress($tokenId, $tokenRetrievalId);

        // Process returned data
        echo 'Bytes already retrieved: ', $data->progress->bytesRetrieved . PHP_EOL;
            
        if ($data->progress->done) {
            if ($data->progress->success) {
                // Prepare to finish retrieving the non-fungible token data
                $continuationToken = $data->continuationToken;
            } else {
                // Process error
                echo 'Asynchronous processing error: [' . $data->progress->error->code . '] - '
                    . $data->progress->error->message . PHP_EOL;
            }

            $done = true;
        } else {
            // Asynchronous processing not done yet. Wait before continuing pooling
            wait(3);
        }
    } while (!$done);

    if ($continuationToken !== null) {
        // Finish retrieving the non-fungible token data
        $nfTokenData = null;

        do {
            $data = $ctnApiClient->retrieveNonFungibleToken(
                $tokenId,
                $continuationToken
            );

            if (!isset($nfTokenData)) {
                // Get token data
                $nfTokenData = (object)[
                    'assetId' => $data->nonFungibleToken->assetId,
                    'metadata' => $data->nonFungibleToken->metadata,
                    'contents' => [$data->nonFungibleToken->contents->data]
                ];
            } else {
                // Add next contents part to token data
                $nfTokenData->contents[] = $data->nonFungibleToken->contents->data;
            }

            $continuationToken = isset($data->continuationToken)
                ? $data->continuationToken
                : null;
        } while ($continuationToken !== null);

        // Process returned data
        echo 'Non-fungible token data: ' . print_r($nfTokenData, true);
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

将非同质化代币转移到另一个设备

同步转移

try {
    $data = $ctnApiClient->transferNonFungibleToken($tokenId, [
        'id' => $otherDeviceId,
        'isProdUniqueId' => false
    ]);

    // Process returned data
    echo 'Non-fungible token successfully transferred' . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

异步转移

try {
    $data = $ctnApiClient->transferNonFungibleToken($tokenId, [
        'id' => $otherDeviceId,
        'isProdUniqueId' => false
    ], true);

    // Start pooling for asynchronous processing progress
    $tokenTransferId = $data->tokenTransferId;
    $done = false;
    wait(1);

    do {
        $data = $ctnApiClient->retrieveNonFungibleTokenTransferProgress($tokenId, $tokenTransferId);

        // Process returned data
        echo 'Current data manipulation: ', print_r($data->progress->dataManipulation, true);
            
        if ($data->progress->done) {
            if ($data->progress->success) {
                // Display result
                echo 'Non-fungible token successfully transferred' . PHP_EOL;
            } else {
                // Process error
                echo 'Asynchronous processing error: [' . $data->progress->error->code . '] - '
                    . $data->progress->error->message . PHP_EOL;
            }

            $done = true;
        } else {
            // Asynchronous processing not done yet. Wait before continuing pooling
            wait(3);
        }
    } while (!$done);
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

检索关于特定资产的信息

try {
    $data = $ctnApiClient->retrieveAssetInfo($assetId);

    // Process returned data
    echo 'Asset info:' . print_r($data, true);
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

获取设备持有的特定资产的当前余额

try {
    $data = $ctnApiClient->getAssetBalance($assetId);

    // Process returned data
    echo 'Current asset balance: ' . $data->balance->total . PHP_EOL;
    echo 'Amount not yet confirmed: ' . $data->balance->unconfirmed . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

列出设备拥有的资产

try {
    $data = $ctnApiClient->listOwnedAssets(200, 0);
    
    // Process returned data
    foreach ($data->ownedAssets as $idx => $ownedAsset) {
        echo 'Owned asset #' . ($idx + 1) . ':' . PHP_EOL;
        echo '  - asset ID: ' . $ownedAsset->assetId . PHP_EOL;
        echo '  - current asset balance: ' . $ownedAsset->balance->total . PHP_EOL;
        echo '  - amount not yet confirmed: ' . $ownedAsset->balance->unconfirmed . PHP_EOL;
    }

    if ($data->hasMore) {
        echo 'Not all owned assets have been returned' . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

列出设备发行的资产

try {
    $data = $ctnApiClient->listIssuedAssets(200, 0);
    
    // Process returned data
    foreach ($data->issuedAssets as $idx => $issuedAsset) {
        echo 'Issued asset #' . ($idx + 1) . ':' . PHP_EOL;
        echo '  - asset ID: ' . $issuedAsset->assetId . PHP_EOL;
        echo '  - total existent balance: ' . $issuedAsset->totalExistentBalance . PHP_EOL;
    }

    if ($data->hasMore) {
        echo 'Not all issued assets have been returned' . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

检索特定资产的发行历史

try {
    $data = $ctnApiClient->retrieveAssetIssuanceHistory($assetId, new \DateTime('20170101T000000Z'), null, 200, 0);
    
    // Process returned data
    foreach ($data->issuanceEvents as $idx => $issuanceEvent) {
        echo 'Issuance event #', ($idx + 1) . ':' . PHP_EOL;

        if (!isset($issuanceEvent->nfTokenIds)) {
            echo '  - issued amount: ' . $issuanceEvent->amount . PHP_EOL;
        }
        else {
            echo '  - IDs of issued non-fungible tokens:' . print_r($issuanceEvent->nfTokenIds, true);
        }

        if (!isset($issuanceEvent->holdingDevices)) {
            echo '  - device to which issued amount has been assigned: ' . print_r($issuanceEvent->holdingDevice, true);
        }
        else {
            echo '  - devices to which issued non-fungible tokens have been assigned:', print_r($issuanceEvent->holdingDevices, true);
        }

        echo '  - date of issuance: ' . $issuanceEvent->date . PHP_EOL;
    }

    if ($data->hasMore) {
        echo 'Not all asset issuance events have been returned' . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

注意:方法 retrieveAssetIssuanceHistory 的参数与 Catenis API 中获取资产发行历史的方法的参数略有不同。特别是日期参数 $startDate$endDate,不仅接受包含 ISO 8601 格式日期/时间的字符串,还接受 DateTime 对象。

列出当前持有特定资产任何数量的设备

try {
    $data = $ctnApiClient->listAssetHolders($assetId, 200, 0);
    
    // Process returned data
    foreach ($data->assetHolders as $idx => $assetHolder) {
        if (isset($assetHolder->holder)) {
            echo 'Asset holder #' . ($idx + 1) . ':' . PHP_EOL;
            echo '  - device holding an amount of the asset: ' . print_r($assetHolder->holder, true);
            echo '  - amount of asset currently held by device: ' . $assetHolder->balance->total . PHP_EOL;
            echo '  - amount not yet confirmed: ' . $assetHolder->balance->unconfirmed . PHP_EOL;
        } else {
            echo 'Migrated asset:' . PHP_EOL;
            echo '  - total migrated amount: ' . $assetHolder->balance->total . PHP_EOL;
            echo '  - amount not yet confirmed: ' . $assetHolder->balance->unconfirmed . PHP_EOL;
        }
    }

    if ($data->hasMore) {
        echo 'Not all asset holders have been returned' . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

将资产导出到国外区块链

估算在国外区块链的本位币中的导出成本

try {
    $foreignBlockchain = 'ethereum';

    $data = $ctnApiClient->exportAsset($assetId, $foreignBlockchain, [
        'name' => 'Test Catenis token #01',
        'symbol' => 'CTK01'
    ], [
        'estimateOnly' => true
    ]);

    // Process returned data
    echo 'Estimated foreign blockchain transaction execution price: ' . $data->estimatedPrice . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

执行导出操作

try {
    $foreignBlockchain = 'ethereum';

    $data = $ctnApiClient->exportAsset($assetId, $foreignBlockchain, [
        'name' => 'Test Catenis token #01',
        'symbol' => 'CTK01'
    ]);

    // Process returned data
    echo 'Foreign blockchain transaction ID (hash): ' . $data->foreignTransaction->id . PHP_EOL;

    // Start polling for asset export outcome
    $done = false;
    $tokenId = null;
    wait(1);

    do {
        $data = $ctnApiClient->assetExportOutcome($assetId, $foreignBlockchain);

        // Process returned data
        if ($data->status === 'success') {
            // Asset successfully exported
            $tokenId = $data->token->id;
            $done = true;
        } elseif ($data->status === 'pending') {
            // Final asset export state not yet reached. Wait before continuing pooling
            wait(3);
        } else {
            // Asset export has failed. Process error
            echo 'Error executing foreign blockchain transaction: ' . $data->foreignTransaction->error . PHP_EOL;
            $done = true;
        }
    } while (!$done);

    if (!is_null($tokenId)) {
        echo 'Foreign token ID (address): ' . $tokenId . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

将资产数量迁移到国外区块链

估算在国外区块链的本位币中的迁移成本

try {
    $foreignBlockchain = 'ethereum';

    $data = $ctnApiClient->migrateAsset($assetId, $foreignBlockchain, [
        'direction' => 'outward',
        'amount' => 50,
        'destAddress' => '0xe247c9BfDb17e7D8Ae60a744843ffAd19C784943'
    ], [
        'estimateOnly' => true
    ]);

    // Process returned data
    echo 'Estimated foreign blockchain transaction execution price: ' . $data->estimatedPrice . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

执行迁移操作

try {
    $foreignBlockchain = 'ethereum';

    $data = $ctnApiClient->migrateAsset($assetId, $foreignBlockchain, [
        'direction' => 'outward',
        'amount' => 50,
        'destAddress' => '0xe247c9BfDb17e7D8Ae60a744843ffAd19C784943'
    ]);

    // Process returned data
    $migrationId = $data->migrationId;
    echo 'Asset migration ID: ' . $migrationId . PHP_EOL;

    // Start polling for asset migration outcome
    $done = false;
    wait(1);

    do {
        $data = $ctnApiClient->assetMigrationOutcome($migrationId);

        // Process returned data
        if ($data->status === 'success') {
            // Asset amount successfully migrated
            echo 'Asset amount successfully migrated' . PHP_EOL;
            $done = true;
        } elseif ($data->status === 'pending') {
            // Final asset migration state not yet reached. Wait before continuing pooling
            wait(3);
        } else {
            // Asset migration has failed. Process error
            if (isset($data->catenisService->error)) {
                echo 'Error executing Catenis service: ' . $data->catenisService->error . PHP_EOL;
            }

            if (isset($data->foreignTransaction->error)) {
                echo 'Error executing foreign blockchain transaction: ' . $data->foreignTransaction->error . PHP_EOL;
            }

            $done = true;
        }
    } while (!$done);
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

重新处理(失败的)迁移

try {
    $foreignBlockchain = 'ethereum';

    $data = $ctnApiClient->migrateAsset($assetId, $foreignBlockchain, $migrationId);

    // Start polling for asset migration outcome
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

获取资产导出结果

try {
    $foreignBlockchain = 'ethereum';

    $data = $ctnApiClient->assetExportOutcome($assetId, $foreignBlockchain);

    // Process returned data
    if ($data->status === 'success') {
        // Asset successfully exported
        echo 'Foreign token ID (address): ' . $data->token->id . PHP_EOL;
    } elseif ($data->status === 'pending') {
        // Final asset export state not yet reached
    } else {
        // Asset export has failed. Process error
        echo 'Error executing foreign blockchain transaction: ' . $data->foreignTransaction->error . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

获取资产迁移结果

try {
    $data = $ctnApiClient->assetMigrationOutcome($migrationId);

    // Process returned data
    if ($data->status === 'success') {
        // Asset amount successfully migrated
        echo 'Asset amount successfully migrated' . PHP_EOL;
    } elseif ($data->status === 'pending') {
        // Final asset migration state not yet reached
    } else {
        // Asset migration has failed. Process error
        if (isset($data->catenisService->error)) {
            echo 'Error executing Catenis service: ' . $data->catenisService->error . PHP_EOL;
        }

        if (isset($data->foreignTransaction->error)) {
            echo 'Error executing foreign blockchain transaction: ' . $data->foreignTransaction->error . PHP_EOL;
        }
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

列出已导出的资产

try {
    $data = $ctnApiClient->listExportedAssets([
        'foreignBlockchain' => 'ethereum',
        'status' => 'success',
        'startDate' => new \DateTime('20210801T000000Z')
    ], 200, 0);

    // Process returned data
    if (count($data->exportedAssets) > 0) {
        echo 'Returned asset exports: ' . print_r($data->exportedAssets, true);
        
        if ($data->hasMore) {
            echo 'Not all asset exports have been returned' . PHP_EOL;
        }
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

注意:方法 listExportedAssets 所接受的参数与 List Exported Assets Catenis API 方法的参数不完全匹配。大部分参数(除最后两个 limitskip 外)被映射到 listExportedAssets 方法的第一个参数($selector)的键,有一些特殊之处:日期键 startDateendDate,接受值为包含 ISO 8601 格式日期/时间的字符串,也接受 DateTime 对象。

列出资产迁移

try {
    $data = $ctnApiClient->listAssetMigrations([
        'foreignBlockchain' => 'ethereum',
        'direction' => 'outward',
        'status' => 'success',
        'startDate' => new \DateTime('20210801T000000Z')
    ], 200, 0);

    // Process returned data
    if (count($data->assetMigrations) > 0) {
        echo 'Returned asset migrations: ' . print_r($data->assetMigrations, true);
        
        if ($data->hasMore) {
            echo 'Not all asset migrations have been returned' . PHP_EOL;
        }
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

注意:方法 listAssetMigrations 所接受的参数与 List Asset Migrations Catenis API 方法的参数不完全匹配。大部分参数(除最后两个 limitskip 外)被映射到 listAssetMigrations 方法的第一个参数($selector)的键,有一些特殊之处:日期键 startDateendDate,接受值为包含 ISO 8601 格式日期/时间的字符串,也接受 DateTime 对象。

列出系统定义的权限事件

try {
    $data = $ctnApiClient->listPermissionEvents();

    // Process returned data
    foreach ($data as $eventName => $description) {
        echo 'Event name: ' . $eventName . '; event description: ' . $description . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

检索指定权限事件的当前权限

try {
    $data = $ctnApiClient->retrievePermissionRights('receive-msg');
    
    // Process returned data
    echo 'Default (system) permission right: ' . $data->system . PHP_EOL;
    
    if (isset($data->catenisNode)) {
        if (isset($data->catenisNode->allow)) {
            echo 'Index of Catenis nodes with \'allow\' permission right: ' . implode(', ', $data->catenisNode->allow)
                . PHP_EOL;
        }
        
        if (isset($data->catenisNode->deny)) {
            echo 'Index of Catenis nodes with \'deny\' permission right: ' . implode(', ', $data->catenisNode->deny)
                . PHP_EOL;
        }
    }
    
    if (isset($data->client)) {
        if (isset($data->client->allow)) {
            echo 'ID of clients with \'allow\' permission right: ' . implode(', ', $data->client->allow) . PHP_EOL;
        }
        
        if (isset($data->client->deny)) {
            echo 'ID of clients with \'deny\' permission right: ' . implode(', ', $data->client->deny) . PHP_EOL;
        }
    }
    
    if (isset($data->device)) {
        if (isset($data->device->allow)) {
            echo 'Devices with \'allow\' permission right: ' . print_r($data->device->allow, true);
        }
        
        if (isset($data->device->deny)) {
            echo 'Devices with \'deny\' permission right: ' . print_r($data->device->deny, true);
        }
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

为指定权限事件设置不同级别的权限

try {
    $data = $ctnApiClient->setPermissionRights(
        'receive-msg',
        [
            'system' => 'deny',
            'catenisNode' => [
                'allow' => 'self'
            ],
            'client' => [
                'allow' => [
                    'self',
                    $clientId
                ]
            ],
            'device' => [
                'deny' => [[
                    'id' => $deviceId1
                ], [
                    'id' => 'ABCD001',
                    'isProdUniqueId' => true
                ]]
            ]
        ]
    );

    // Process returned data
    echo 'Permission rights successfully set' . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

检查针对指定权限事件应用于特定设备的有效权限

try {
    $data = $ctnApiClient->checkEffectivePermissionRight('receive-msg', $deviceProdUniqueId, true);

    // Process returned data
    $deviceId = array_keys(get_object_vars($data))[0];
    echo 'Effective right for device ' . $deviceId . ': ' . $data->$deviceId . PHP_EOL;
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

检索特定设备的标识信息

try {
    $data = $ctnApiClient->retrieveDeviceIdentificationInfo($deviceId, false);
    
    // Process returned data
    echo 'Device\'s Catenis node ID info:' . print_r($data->catenisNode, true);
    echo 'Device\'s client ID info:' . print_r($data->client, true);
    echo 'Device\'s own ID info:' . print_r($data->device, true);
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

列出系统定义的通知事件

try {
    $data = $ctnApiClient->listNotificationEvents();

    // Process returned data
    foreach ($data as $eventName => $description) {
        echo 'Event name: ' . $eventName . '; event description: ' . $description . PHP_EOL;
    }
} catch (\Catenis\Exception\CatenisException $ex) {
    // Process exception
}

通知

Catenis API PHP 客户端通过嵌入 WebSocket 客户端,使得接收来自 Catenis 系统的通知变得简单。所有终端用户需要做的就是为所需的 Catenis 通知事件打开一个 WebSocket 通知通道,并监控该通道的活动。

通知需要使用事件循环。您应该在实例化 ApiClient 对象时将事件循环实例作为选项传递,就像使用异步 API 方法一样。

$loop = \React\EventLoop\Factory::create();

$ctnApiClient = new \Catenis\ApiClient(
    $deviceId,
    $apiAccessSecret, [
        'environment' => 'sandbox'
        'eventLoop' => $loop
    ]
);

注意:如果在实例化 ApiClient 对象时没有传递事件循环实例,将创建一个内部事件循环。然而,在这种情况下,通知只有在应用程序关闭(事件循环最终运行)后才会被处理。

接收通知

实例化 WebSocket 通知通道对象。

$wsNtfyChannel = $ctnApiClient->createWsNotifyChannel($eventName);

添加监听器。

$wsNtfyChannel->on('error', function ($error) {
    // Process error in the underlying WebSocket connection
});

$wsNtfyChannel->on('close', function ($code, $reason) {
    // Process indication that underlying WebSocket connection has been closed
});

$wsNtfyChannel->on('open', function () {
    // Process indication that notification channel is successfully open
    //  and ready to send notifications 
});

$wsNtfyChannel->on('notify', function ($data) {
    // Process received notification
    echo 'Received notification:' . PHP_EOL;
    print_r($data);
});

注意:通知事件 notifydata 参数包含相应的通知事件的反序列化 JSON 通知消息(一个 stdClass 实例)。

打开通知通道。

$wsNtfyChannel->open()->then(
    function () {
        // WebSocket client successfully connected. Wait for open event to make
        //  sure that notification channel is ready to send notifications
    },
    function (\Catenis\Exception\WsNotificationException $ex) {
        // Process exception
    }
);

注意:WebSocket 通知通道对象的 open() 方法以异步方式工作,因此它返回一个像异步 API 方法一样的承诺。

关闭通知通道。

$wsNtfyChannel->close();

错误处理

通过异常对象报告错误条件,在同步方法中抛出,在异步方法中作为参数传递。

API 方法异常

调用 API 方法时可能会发生以下异常

  • CatenisClientException - 表示在尝试调用 Catenis API 端点时发生错误。
  • CatenisApiException - 表示 Catenis API 端点返回了错误。

注意:这两个异常源于一个单一的异常,即CatenisException

CatenisApiException对象提供了一些自定义方法,可以用来获取有关错误状态的一些特定数据,如下所示

  • getHttpStatusCode() - 返回从Catenis API端点接收到的HTTP响应的数字状态码。

  • getHttpStatusMessage() - 返回从Catenis API端点接收到的HTTP响应状态码的文本。

  • getCatenisErrorMessage() - 返回从Catenis API端点返回的Catenis错误消息。

用法示例

try {
    $data = $ctnApiClient->readMessage('INVALID_MSG_ID', null);
    
    // Process returned data
} catch (\Catenis\Exception\CatenisException $ex) {
    if ($ex instanceof \Catenis\Exception\CatenisApiException) {
        // Catenis API error
        echo 'HTTP status code: ' . $ex->getHttpStatusCode() . PHP_EOL;
        echo 'HTTP status message: ' . $ex->getHttpStatusMessage() . PHP_EOL;
        echo 'Catenis error message: ' . $ex->getCatenisErrorMessage() . PHP_EOL;
        echo 'Compiled error message: ' . $ex->getMessage() . PHP_EOL;
    } else {
        // Client error
        echo $ex . PHP_EOL;
    }
}

预期结果

HTTP status code: 400
HTTP status message: Bad Request
Catenis error message: Invalid message ID
Compiled error message: Error returned from Catenis API endpoint: [400] Invalid message ID

WebSocket通知异常

在打开WebSocket通知通道时可能会发生以下异常

  • OpenWsConnException - 表示在建立底层WebSocket连接时发生错误。
  • WsNotifyChannelAlreadyOpenException - 表示WebSocket通知通道(对于该设备和通知事件)已经打开。

注意:这两个异常源于一个单一的异常,即WsNotificationException,而WsNotificationException本身也源于CatenisException

用法示例

$wsNtfyChannel->open()->then(
    function () {
        // WebSocket client successfully connected. Wait for open event to make
        //  sure that notification channel is ready to send notifications
    },
    function (\Catenis\Exception\WsNotificationException $ex) {
        if ($ex instanceof \Catenis\Exception\OpenWsConnException) {
            // Error opening WebSocket connection
            echo $ex . PHP_EOL;
        } else {
            // WebSocket nofitication channel already open
        }
    }
);

Catenis API文档

有关Catenis API的更多信息,请参阅Catenis API文档

许可证

此库基于MIT许可证发布。请随意分支和修改!

版权所有 © 2018-2022,区块链事物公司